on master help sign in
Check source-proven recursive tree construction

Infer supported tagged constructors with a bounded source-derived view, preserving nominal type arguments and shared inference state. Keep matching, general structural fits and gate-freedom unresolved.

commitd9d51e6904c47906028d775f7fc17fe87826c5e4 authoriko <mail@iko.soy> dateSat 31 Oct 2026, 14:28 (7 hours ago) parent190a9d5
24 files changed+438−11
README.md+1−1 docs/compiler.md+1−1 docs/development.md+2 docs/status.md+5−3 jibb-dev/lib/jibb.hoon+73 tests/lib/jibb-generic-construction.hoon+141 tests/lib/jibb-generic-recursion.hoon+1−1 tests/type-generic-construction/about.txt+9 tests/type-generic-construction/alias-client.txt+12 tests/type-generic-construction/alias.txt+3 tests/type-generic-construction/client.txt+24 tests/type-generic-construction/expected.txt+65 tests/type-generic-construction/no-event-client.txt+8 tests/type-generic-construction/owner-negative.txt+10 tests/type-generic-construction/owner-positive.txt+9 tests/type-generic-construction/sibling-values.txt+4 tests/type-generic-construction/tree-no-event.txt+4 tests/type-generic-construction/tree.txt+6 tests/type-generic-recursion/about.txt+2−2 tests/type-generic-recursion/bad-constructor.txt+1−1 tests/type-generic-recursion/expected.txt+1−1 tools/testdata/ship-selftest+6 tools/type-generic-construction-test+49 tools/type-generic-recursion-test+1−1
README.md+1−1
@@ -42,7 +42,7 @@ tools/hoon '(golden /a/b/jibb (crip "++ main view"))'
4242tools/hoon --raw --cord src=tests/golden/foo/source.jibb 'tree:(golden /a/b/jibb src)'
4343```
4444
45−`tools/testdata/selftest` tests both tools against a stub library, and `tools/doc-blocks` against Markdown it writes. `tools/importer-test` compares the partial Hoon importer's fixture projections against their golden file. `tools/type-import-test`, `tools/type-import-overlap-test`, `tools/type-import-structural-test`, `tools/type-generic-recursion-test`, `tools/type-tlon-test`, `tools/type-mark-test`, `tools/type-poke-test` and `tools/type-api-paths-test` check importer/checker, prelude and partial API path analysis boundaries under eval; the ship suite runs the same fixtures.
45+`tools/testdata/selftest` tests both tools against a stub library, and `tools/doc-blocks` against Markdown it writes. `tools/importer-test` compares the partial Hoon importer's fixture projections against their golden file. `tools/type-import-test`, `tools/type-import-overlap-test`, `tools/type-import-structural-test`, `tools/type-generic-recursion-test`, `tools/type-generic-construction-test`, `tools/type-tlon-test`, `tools/type-mark-test`, `tools/type-poke-test` and `tools/type-api-paths-test` check importer/checker, prelude and partial API path analysis boundaries under eval; the ship suite runs the same fixtures.
4646
4747### The doc's examples
4848
docs/compiler.md+1−1
@@ -94,7 +94,7 @@ A known closed union can also provide context to each arm of a complete loobean
9494
9595The separate `jibb-dev/lib/jibb-import.hoon` is an unfinished reader for selected Hoon type graphs. It handles named recursion, nested map, set, queue, list, unit, jar and jug arguments, direct ordered maps, and direct `$^` unions whose source branches can be proved disjoint. It also recognizes a direct ordered-map call with two simple named mold operands, as in Tlon `notes.log`, after validating provenance and operand source; revisiting the same map hold fails closed. It also reads ordered map fields in records when the map arguments are syntactically fixed atom molds and other fields are fixed atom molds or standard collections of static atom molds. Collection names must not be shadowed. The reader can also import a mark door sample, removing its outer sample face and validating its build subject. For a mark whose sample is exactly a named arm of its single Ford-imported `/sur` core, it can recover a source-proved nonrecursive union, including source-location wrappers around fields; recursive or unverified fallback results remain unsupported. The reader checks explicit Hoon `$%`, `$?` and `$^` unions against the Grammar’s disjointness rule, as @iko confirmed on 2026-10-30. For an explicit `$%` with distinct literal head tags, it can also prove restricted local named payload dependencies, including bare `~` fields, re-evaluate and validate each source branch, and recover the union before Hoon collapses it. The real Tlon `notes.u-note` exercises its `[%deleted ~]` case. A bounded fallback also recovers one level of local named tagged unions inside such a union, including Tlon `notes.u-notebook` and `notes.u-notes`. Source-proved tagged unions can also be wrapped in Hoon `$~` defaults, including a downstream union using Tlon `epic.saga`; the selected source and projected branches still undergo the same provenance and disjointness checks. The tagged-source helper peels up to four consecutive default wrappers, while default wrappers encountered inside dependency proofs consume the existing semantic-node budget. A source-proved nested self-recursive union retains its named owner, including Tlon `notes.import-node`; deeper, mutually recursive or unproven cases stay unsupported. The nested source proof has a 256-visit semantic-node budget, and source-location wrapper peeling is limited to four per node; Hoon evaluation and source scans are not metered. It rejects recursive or computed dependencies, inherited same-name collisions, unsupported nested unions and proofs exceeding a shared 4,096-node budget. It re-evaluates statically recognizable source branches before Hoon can collapse them, checks materialized fork cases, and rejects shapes it cannot prove disjoint. Static records may also have verified standard-collection and `path` siblings. One direct explicit union argument in a standard list, set, map or unit is accepted when its call wrapper and collection provenance are verified and its static cases are pairwise disjoint. A synthetic regression checks that forged wrapper subjects, samples and tails are rejected. Other collection-union forms, nested ordered maps, general source-level disjointness and complete integration with the checker remain unfinished. `+golden-types-imports` can consume fixed-aura atoms, valid term singletons, Hoon's `%n` zero singleton as Jibb `~`, nested faced or unfaced cells, unions of supported, pairwise-disjoint cases, and opaque standard collections with supported arguments from caller-supplied reader results for declared `/-` imports; `+golden-types-with-marks` accepts the same shapes for declared marks. The caller must pass unchanged results from the source-aware reader: the bridge validates the supplied graph but cannot detect source cases Hoon collapsed before that graph was built. The opaque collection types retain their names and arguments for identity, unification and printing. The validated standard prelude `list` mold shares the imported list representation; other Jibb-written collection molds and most prelude collection operations remain unsupported. Other forms remain unsupported. Its disjointness proof is conservative and can reject a valid union it cannot prove disjoint. Collection recognition requires the originating `/sur` core. The reader validates direct zuse subjects and Ford subjects consisting of faced imported cores and a validated tail, checking imported cores transitively; it rejects shadowed names, inconsistent core subjects and other subject shapes. For a recursive reference, the reader matches the exact Hoon hold against named arms in validated cores, including transitive Ford imports; it rejects missing, unlabelled or ambiguous origins. Its fixtures include three real Tlon files; `tools/importer-test` compares the internal projection under eval, and the ship test compares the same golden output. `tools/type-import-test` and `tools/type-mark-test` compare bridge reports under eval and on the ship. `tools/type-tlon-test` and its ship counterpart exercise four real Tlon presence unions and a field read through the source-aware reader and partial checker. `tools/clay-fixtures` also passes Clay-built `/sur` molds and mark samples through the Clay-built importer and partial checker.
9696
97−An import-free Jibb library can export an opaque single-parameter recursive identity when its source has exactly two distinct term-tagged cases: a text leaf and a node containing `(list (name M))`, optionally with one `$-(@t M)` event field. A module-local index records the exact parsed source, defining path and prepared prelude interface; it is shared across that module's inference passes, and its canonical `list` must be source-backed. The normal resolver publishes an interface only after all ordinary mold and value checks succeed. Same-origin aliases retain the defining identity and independently instantiate type arguments; distinct origins remain unsupported. The opaque identity is conservatively treated as gate-bearing for numeric defaulting, and gate-freedom remains unknown. Recursive source with imports, local recursive molds, construction, matching and structural unfolding are not supported by this slice; the test tree does not prescribe the eventual `element` representation. Caller-supplied detached interfaces and caches retain the existing trusted-input contract.
97+An import-free Jibb library can export an opaque single-parameter recursive identity when its source has exactly two distinct term-tagged cases: a text leaf and a node containing `(list (name M))`, optionally with one `$-(@t M)` event field. A module-local index records the exact parsed source, defining path and prepared prelude interface; it is shared across that module's inference passes, and its canonical `list` must be source-backed. The normal resolver publishes an interface only after all ordinary mold and value checks succeed. Same-origin aliases retain the defining identity and independently instantiate type arguments; distinct origins remain unsupported. The opaque identity is conservatively treated as gate-bearing for numeric defaulting, and gate-freedom remains unknown. Explicit literal-headed construction uses a source-proven one-layer view: the event result receives the existing type argument and recursive children keep the defining identity. It consumes the existing shared 256-step recursive inference budget, and a per-view 4,096-visit guard bounds the new face and substitution scan; longer scans decline. These bounds do not cover parsing, existing inference, atom sizes or repeated attempts. One startup-inclusive, shared-load comparison of 100 valid and 100 invalid source definitions took 29.67 s wall / 25.14 s user CPU before this slice and 33.47 s / 26.99 s with it; the former declined all 200 while the latter inferred the valid definitions. These single runs are not a production benchmark. General structural fits, pattern matching, recursive source with imports and local recursive molds remain unsupported; the test tree does not prescribe the eventual `element` representation. Caller-supplied detached interfaces and caches retain the existing trusted-input contract.
9898
9999Self-recursive `/sur` molds can cross the bridge as opaque module-and-name identities when their supported source-aware graph passes validation, including one-argument lists containing same-origin recursive references. The real Tlon `notes.import-node` reaches this bridge; it does not unfold or construct the recursive type. Aliases, foreign references, recursive marks, unfolding, construction and field access remain unsupported; the partial printer declines recursive names that are not in scope.
100100
docs/development.md+2
@@ -73,6 +73,7 @@ git clone https://github.com/urbit/tools /root/src/tools
7373| `tools/type-hole-guards-test` | Runs the shared hole-defaulting and API interface guards under eval; the same checks run on the ship |
7474| `tools/type-list-alias-test` | Runs the shared prelude list-alias and provenance guards under eval and on the ship |
7575| `tools/type-generic-recursion-test` | Checks source-backed generic recursive identity and provenance under eval and on the ship |
76+| `tools/type-generic-construction-test` | Checks source-backed one-layer recursive construction, provenance and fuel under eval and on the ship |
7677| `tools/type-generic-alias-test` | Runs public/private generic union alias and provenance guards under eval and on the ship |
7778| `tools/type-positional-face-guards-test` | Runs positional-call and nested-face guards under eval and on the ship |
7879| `tools/type-qath-guards-test` | Runs source-provenance, recursive unfolding and face guards under eval and on the ship |
@@ -124,6 +125,7 @@ tools/type-todo-partial-test # unchanged todo partial report and reader fa
124125tools/type-todo-api-test # real todo API partial inference and reader guards pass
125126tools/type-direct-gate-test # direct gate source and provenance guards pass
126127tools/type-generic-recursion-test # source-backed recursive identities and provenance
128+tools/type-generic-construction-test # source-backed recursive construction and bounds
127129tools/type-closed-gate-guards-test # closed gate fit guards pass
128130tools/type-constructor-fit-guards-test # constructor fit guards pass
129131tools/type-nested-union-guards-test # nested union selector guards pass
docs/status.md+5−3
@@ -25,7 +25,7 @@ On 2026-10-31, @iko chose explicit, result-directed `(proj record)` to construct
2525
2626The internal prelude also defines `qath` as a transparent recursive knot sequence, re-exported by the public prelude. The partial checker recognizes its source identity, supports selected null/cell construction and matching, and types static application-path literals. Construction and matching for other recursive shapes, broader patterns and dynamic application-path segments remain unsupported. A 256-step limit applies per inference attempt, not to parsing or retries; checking routes and static files remains milestone 4.
2727
28−The checker also recognizes a limited source-proven generic recursive tree in an import-free defining module. Its opaque identity retains the defining path, mold name and type arguments, including across supported imports and aliases. Construction, pattern matching, unfolding and gate-free proof for this shape remain unsupported; this is not yet the prelude's `element` implementation or island checking. The source index uses fixed-shape checks during mold collection. A single startup-inclusive comparison of 1,000 ordinary gates produced identical reports and used 29.12 seconds of user CPU before the shared-resolver refactor and 28.55 seconds after it, with other work running; it is not a general performance bound.
28+The checker also recognizes a limited source-proven generic recursive tree in an import-free defining module. Its nominal identity retains the defining path, mold name and type arguments, including across supported imports and aliases. With a known expected type it can construct supported tagged cases by checking one source-proven layer at a time. Pattern matching, general structural fits and gate-free proof remain unsupported; this is not yet the prelude's `element` implementation or island checking. The source index uses fixed-shape checks during mold collection. Each construction attempt has 256 unfolding steps and at most 4,096 face and substitution visits per view; parsing, other inference and retries remain outside those limits. A single startup-inclusive comparison of 1,000 ordinary gates produced identical reports and used 29.12 seconds of user CPU before the shared-resolver refactor and 28.55 seconds after it, with other work running; it is not a general performance bound.
2929
3030The internal prelude defines a transparent generic `ask` with pure, agent and store cases, re-exported alongside the Jibb-written `pure` gate. The partial checker infers supported constructors and exhaustive matches. Agent and store receivers operate on raw nouns; their fields do not prove schema validity or watchability. The public prelude now defines the documented source-backed `sub` union; supported `%watch` payloads retain the public `(ask M)` type. Constructing a `sub` does not prove that its request is watchable. Request lowering, checked decoding, runtime validation and island watch checking remain unimplemented.
3131
@@ -76,7 +76,7 @@ What the plan asks for, and where it is:
7676Besides the plan's list:
7777
7878- 653 golden cases: 433 that parse and 220 that fail, each failure at a stated line and column. One failing case has an unclear expectation (corpus question 3). The newer type-output cases exercise only the partial inference fragment. Each case's `about.txt` quotes the doc's words it tests.
79−- The 653-case corpus passed under `urbit eval` in 107.5 s while other checks were running. The fake ship passed all 688 tests, including the source-overlap, structural-overlap and generic-recursion fixtures, importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, ask, default-wrapper importer, projection, arithmetic, generic-alias, positional-face, API-evidence, request, closed-gate, todo API, constructor, sub, nested-union, gate-free and direct-gate guards and the unchanged todo report. Desk sync took 94.5 s while other checks were running. The importer golden compares 221 projections; `tools/clay-fixtures` builds the example Hoon fixtures through Clay, checks a recursive Ford import's origin, and passes Clay-built types through the Clay-built importer and partial checker.
79+- The 653-case corpus passed under `urbit eval` in 105.9 s while other checks were running. The fake ship passed all 689 tests, including the source-overlap, structural-overlap, generic-recursion and generic-construction fixtures, importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, ask, default-wrapper importer, projection, arithmetic, generic-alias, positional-face, API-evidence, request, closed-gate, todo API, constructor, sub, nested-union, gate-free and direct-gate guards and the unchanged todo report. Desk sync took 98.2 s while other checks were running. The importer golden compares 221 projections; `tools/clay-fixtures` builds the example Hoon fixtures through Clay, checks a recursive Ford import's origin, and passes Clay-built types through the Clay-built importer and partial checker.
8080- `tools/testdata/selftest` (22 checks), `tools/testdata/ship-selftest` and `tools/testdata/clay-fixtures-selftest` test the tools themselves; the last checks temporary Clay fixture cleanup and signal handling without a ship.
8181- Two independent audits before 7c739b7, one of which compared the library with a reference parser written in Python from the doc over about 19,000 generated programs. Before ab797a9, a second Python parser, written from the decisions, agreed with the library on 8,000 generated programs and 900 hand-written probes.
8282- Speed: starting `urbit eval` and building the library takes about 8 s. After that, a 2,000-line module of 400 definitions parses in about a second. Loops over definitions, arms, lines and items grow no stack; nesting costs a few stack frames a level, and 100,000 nested brackets still parse (library section 3).
@@ -180,7 +180,8 @@ Besides the plan's list:
180180| 179d717 | 2026-10-31 | Diagnose source-proven Hoon union overlaps |
181181| 142f2e2 | 2026-10-31 | Prove structural overlaps across Hoon union cases |
182182| c96597d | 2026-10-31 | Track source-proven generic recursive identities |
183−| This change | 2026-10-31 | Add source-backed list flattening |
183+| 190a9d5 | 2026-10-31 | Add source-backed list flattening |
184+| This change | 2026-10-31 | Check source-proven recursive tree construction |
184185
185186## Checking it
186187
@@ -202,6 +203,7 @@ tools/type-hole-guards-test # hole defaulting and API interface guards
202203tools/type-list-alias-test # prelude list-alias guards
203204tools/type-generic-alias-test # generic union alias guards
204205tools/type-generic-recursion-test # source-proven generic recursion guards
206+tools/type-generic-construction-test # source-proven recursive construction guards
205207tools/type-positional-face-guards-test # positional sample face guards
206208tools/type-qath-guards-test # qath source and unfolding guards
207209tools/type-ask-guards-test # ask source and bootstrap guards
jibb-dev/lib/jibb.hoon+73
@@ -8840,6 +8840,20 @@
88408840 ?: ?=(?(%cell %face %fn %union %collection) -.val.i.sub) |
88418841 $(sub t.sub)
88428842::
8843+:: Check the syntactic root before scanning; scan substitutions once.
8844+++ grec-direct-context
8845+ |= [expected=itype sub=isub]
8846+ ^- ?
8847+ =/ left=@ud 4.096
8848+ |-
8849+ ?: ?=(%face -.expected)
8850+ ?: =(0 left) |
8851+ $(expected body.expected, left (dec left))
8852+ ?. ?=(%grec -.expected) |
8853+ ?~ sub &
8854+ ?: =(0 left) |
8855+ ?: ?=(?(%cell %face %fn %union %collection %grec) -.val.i.sub) |
8856+ $(sub t.sub, left (dec left))
88438857++ contextual-direct-union
88448858 |= target=itype
88458859 ^- ?
@@ -8879,6 +8893,23 @@
88798893 ?: ?=(%face -.target) $(target (itype-walk body.target sub.state))
88808894 ?: (explicit-proj-call e env state)
88818895 (infer-proj-context e expected env state context)
8896+ :: Experimental construction only for a source-proven direct target.
8897+ :: Decline any walked structural binding to retain raw-variable provenance.
8898+ ?: &(?=(%grec -.target) ?=(%tuple -.e) (grec-direct-context expected sub.state))
8899+ =/ unfolded=(unit [view=itype left=@ud]) (grec-unfold-view target jrec-fuel.state)
8900+ ?~ unfolded [%| ["recursive mold unfolding needs unsupported inference" ~]]
8901+ ?~ items.e [%| ["recursive mold constructor needs unsupported inference" ~]]
8902+ =/ first=item i.items.e
8903+ ?. &(?=(~ face.first) ?=(%lit -.val.first) =(%term form.lit.val.first))
8904+ [%| ["recursive mold constructor needs unsupported inference" ~]]
8905+ =/ chosen=(unit itype) (contextual-tagged-case view.u.unfolded val.lit.val.first sub.state)
8906+ ?~ chosen [%| ["recursive mold constructor has no matching case" ~]]
8907+ =/ value=iresult (infer-call-context-items items.e u.chosen env state(jrec-fuel left.u.unfolded) context)
8908+ ?: ?=(%| -.value) value
8909+ =/ fit=ifit (itype-fit typ.p.value u.chosen sub.state.p.value next.state.p.value)
8910+ ?: ?=(%unknown -.fit) [%| ["recursive mold case needs unsupported inference" ~]]
8911+ ?: ?=(%bad -.fit) [%| ["recursive mold case does not fit its source shape" ~]]
8912+ [%& target state.p.value(sub sub.fit, next next.fit)]
88828913 :: Only the source-proven one-layer cell is exposed. Each recursive
88838914 :: descent consumes one written tuple; named values use ordinary fit.
88848915 ?: &(?=(%jrec -.target) ?=(%lit -.e) =(%null form.lit.e))
@@ -12086,6 +12117,48 @@
1208612117 ?. ?=(%tvar -.i.args.self) |
1208712118 =(name.i.args.self param)
1208812119+$ grec-collected [molds=imolds proofs=(map term shape)]
12120+:: Experimental one-layer analysis view, never an exported mold identity.
12121+++ grec-unfold-view
12122+ |= [typ=itype fuel=@ud]
12123+ ^- (unit [view=itype left=@ud])
12124+ ?. ?=(%grec -.typ) ~
12125+ ?: =(fuel 0) ~
12126+ ?. ?=([* ~] args.typ) ~
12127+ =/ sh=shape proof.typ
12128+ ?. (grec-tree-proof name.typ sh) ~
12129+ ?> ?=(%generic -.sh)
12130+ ?> ?=([@ ~] vars.sh)
12131+ ?> ?=(%union -.body.sh)
12132+ ?> ?=([* [* ~]] cases.body.sh)
12133+ =/ ca=shape i.cases.body.sh
12134+ =/ cb=shape i.t.cases.body.sh
12135+ ?> ?=(%cell -.ca)
12136+ ?> ?=(%cell -.cb)
12137+ ?> ?=([* [* ~]] fields.ca)
12138+ ?> ?=([* *] fields.cb)
12139+ =/ tag-a=field i.fields.ca
12140+ =/ tag-b=field i.fields.cb
12141+ ?> ?=(%bare -.tag-a)
12142+ ?> ?=(%bare -.tag-b)
12143+ ?> ?=(%const -.shape.tag-a)
12144+ ?> ?=(%const -.shape.tag-b)
12145+ =/ text-field=field i.t.fields.ca
12146+ ?> ?=(%face -.text-field)
12147+ =/ branch=(list field) t.fields.cb
12148+ =/ kids=(unit field) (grec-tree-children branch i.vars.sh)
12149+ ?~ kids ~
12150+ ?> ?=(%face -.u.kids)
12151+ =/ children=itype [%face name.u.kids [%collection %list ~[typ]]]
12152+ ?> ?=(^ branch)
12153+ =/ payload=itype
12154+ ?~ t.branch children
12155+ =/ event=field i.branch
12156+ ?> ?=(%face -.event)
12157+ [%cell [%face name.event [%fn [%atom %t] i.args.typ]] children]
12158+ =/ leaf=itype [%cell [%const lit.shape.tag-a] [%face name.text-field [%atom %t]]]
12159+ =/ node=itype [%cell [%const lit.shape.tag-b] payload]
12160+ `[[%union ~[leaf node] ~ ~ ~] (dec fuel)]
12161+::
1208912162++ grec-collect-molds
1209012163 |= ds=(list def)
1209112164 ^- grec-collected
tests/lib/jibb-generic-construction.hoonnew file+141
@@ -0,0 +1,141 @@
1+:: Source-proven generic tree construction; not a backend/runtime test.
2+/+ jibb
3+/* real-prelude %jibb /lib/jibb/prelude/jibb
4+/* real-internal %jibb /lib/jibb/internal/jibb
5+/* tree %txt /tests/type-generic-construction/tree/txt
6+/* alias %txt /tests/type-generic-construction/alias/txt
7+/* client %txt /tests/type-generic-construction/client/txt
8+/* alias-client %txt /tests/type-generic-construction/alias-client/txt
9+/* tree-no-event %txt /tests/type-generic-construction/tree-no-event/txt
10+/* no-event-client %txt /tests/type-generic-construction/no-event-client/txt
11+/* owner-positive %txt /tests/type-generic-construction/owner-positive/txt
12+/* owner-negative %txt /tests/type-generic-construction/owner-negative/txt
13+/* sibling-values %txt /tests/type-generic-construction/sibling-values/txt
14+/* expected %txt /tests/type-generic-construction/expected/txt
15+|%
16+++ text
17+ |= lines=wain
18+ ^- @t
19+ (rap 3 (turn lines |=(line=@t (cat 3 line '\0a'))))
20+++ line
21+ |= [label=@t ok=?]
22+ ^- @t
23+ (crip "{(trip label)}: {?:(ok "ok" "BAD")}")
24+++ construction-output
25+ |= [pre=@t internal=@t tree=@t alias=@t client=@t alias-client=@t tree-no-event=@t no-event-client=@t owner-positive=@t owner-negative=@t sibling-values=@t]
26+ ^- @t
27+ =/ prep=(unit iprelude:jibb) (prepare-prelude-with-internal:jibb pre internal)
28+ ?~ prep 'BAD prelude preparation'
29+ :: Complete owner is checked by the real source interface, not this unit state.
30+ =/ owner=jfile-result:jibb
31+ (infer-jibb-file:jibb /ui/tree/jibb owner-positive ~ ~ ~ ~[/ui/tree/jibb] values.u.prep molds.u.prep ~ `iface.u.prep)
32+ ?: ?=(%| -.owner) 'BAD valid owner'
33+ =/ bad-owner=jfile-result:jibb
34+ (infer-jibb-file:jibb /ui/tree/jibb owner-negative ~ ~ ~ ~[/ui/tree/jibb] values.u.prep molds.u.prep ~ `iface.u.prep)
35+ =/ bad-parse=(each module:jibb error:jibb) (parse:jibb /ui/tree/jibb owner-negative)
36+ =/ mold=(unit ischeme:jibb) (find-jibb-mold:jibb %element iface.p.owner)
37+ ?~ mold 'BAD owner mold'
38+ ?. ?=(%grec -.typ.u.mold) 'BAD nominal owner'
39+ =/ generic=itype:jibb typ.u.mold(args ~[[%var 42]])
40+ =/ target=itype:jibb typ.u.mold(args ~[[%atom %t]])
41+ ?> ?=(%grec -.generic)
42+ ?> ?=(%grec -.target)
43+ =/ owner-leaf=(unit ischeme:jibb) (ienv-lookup:jibb %made-leaf values.iface.p.owner)
44+ =/ owner-node=(unit ischeme:jibb) (ienv-lookup:jibb %made-node values.iface.p.owner)
45+ =/ leaf-ok=? ?~(owner-leaf | &(?=(~ vars.u.owner-leaf) =(target typ.u.owner-leaf)))
46+ =/ node-ok=? ?~(owner-node | &(?=(~ vars.u.owner-node) =(target typ.u.owner-node)))
47+ =/ foreign=itype:jibb target(at /ui/foreign/jibb)
48+ =/ view=(unit [view=itype:jibb left=@ud]) (grec-unfold-view:jibb generic 2)
49+ ?~ view 'BAD generic view'
50+ =/ target-view=(unit [view=itype:jibb left=@ud]) (grec-unfold-view:jibb target 2)
51+ ?~ target-view 'BAD target view'
52+ =/ expected-leaf=itype:jibb [%cell [%const [%term %tas %text '%text']] [%face %value [%atom %t]]]
53+ =/ expected-node=itype:jibb [%cell [%const [%term %tas %node '%node']] [%cell [%face %event [%fn [%atom %t] [%var 42]]] [%face %children [%collection %list ~[generic]]]]]
54+ =/ expected-view=itype:jibb [%union ~[expected-leaf expected-node] ~ ~ ~]
55+ =/ wrong-view=(unit [view=itype:jibb left=@ud]) (grec-unfold-view:jibb foreign 2)
56+ =/ malformed=shape:jibb *shape:jibb
57+ =/ parsed=(each module:jibb error:jibb) (parse:jibb /tests/tree/values/jibb sibling-values)
58+ ?: ?=(%| -.parsed) 'BAD values parse'
59+ =/ ds=(list def:jibb) defs.p.parsed
60+ ?. ?=([* [* [* [* ~]]]] ds) 'BAD values count'
61+ =/ a=def:jibb i.ds
62+ =/ b=def:jibb i.t.ds
63+ =/ c=def:jibb i.t.t.ds
64+ =/ d=def:jibb i.t.t.t.ds
65+ ?. &(?=(%value -.a) ?=(%value -.b) ?=(%value -.c) ?=(%value -.d)) 'BAD value declarations'
66+ =/ env=ienv:jibb ~[[%text-event [[%fn [%atom %t] [%atom %t]] ~]]]
67+ =/ tag=ipending:jibb [100 [%term %tas %unrelated '%unrelated']]
68+ =/ projection=iwprojection:jibb *iwprojection:jibb
69+ =/ st=istate:jibb *istate:jibb
70+ =. st st(next 201, prelude `iface.u.prep, tags ~[tag], holes ~[[99 (span-of:jibb val.a)]], projections ~[projection])
71+ =/ r0=iresult:jibb (infer-call-context-arg:jibb val.a target env st(jrec-fuel 0) (span-of:jibb val.a))
72+ =/ r1=iresult:jibb (infer-call-context-arg:jibb val.a target env st(jrec-fuel 1) (span-of:jibb val.a))
73+ =/ rnode=iresult:jibb (infer-call-context-arg:jibb val.b target env st(jrec-fuel 1) (span-of:jibb val.b))
74+ =/ short=iresult:jibb (infer-call-context-arg:jibb val.c target env st(jrec-fuel 1) (span-of:jibb val.c))
75+ =/ nested=iresult:jibb (infer-call-context-arg:jibb val.c target env st(jrec-fuel 2) (span-of:jibb val.c))
76+ =/ sibling-short=iresult:jibb (infer-call-context-arg:jibb val.d target env st(jrec-fuel 2) (span-of:jibb val.d))
77+ =/ siblings=iresult:jibb (infer-call-context-arg:jibb val.d target env st(jrec-fuel 3) (span-of:jibb val.d))
78+ =/ retry=iresult:jibb (infer-call-context-arg:jibb val.a target env st(jrec-fuel 1) (span-of:jibb val.a))
79+ =/ failed-retry=iresult:jibb (infer-call-context-arg:jibb val.a target env st(jrec-fuel 0) (span-of:jibb val.a))
80+ =/ rvar=iresult:jibb (infer-call-context-arg:jibb val.b generic env st(jrec-fuel 1) (span-of:jibb val.b))
81+ =/ hidden=istate:jibb st(sub ~[[200 target]], jrec-fuel 2)
82+ =/ rhidden=iresult:jibb (infer-call-context-arg:jibb val.a [%var 200] env hidden (span-of:jibb val.a))
83+ =/ hidden-safe=?
84+ ?: ?=(%| -.rhidden) &
85+ =/ fit=ifit:jibb (itype-fit:jibb typ.p.rhidden [%var 200] sub.state.p.rhidden next.state.p.rhidden)
86+ ?=(%unknown -.fit)
87+ =/ exact=isub:jibb (turn (gulf 1 4.096) |=(n=@ud [(add 1.000 n) [%atom %t]]))
88+ =/ over=isub:jibb [[9.999 [%atom %t]] exact]
89+ =/ face-exact=isub:jibb (turn (gulf 1 4.095) |=(n=@ud [(add 1.000 n) [%atom %t]]))
90+ =/ checks=(list [label=@t ok=?])
91+ :~ ['complete owner' ?=(%& -.owner)]
92+ ['owner exports constructed nominal values' &(leaf-ok node-ok)]
93+ ['invalid owner declined' &(?=(%& -.bad-parse) ?=(%| -.bad-owner))]
94+ ['view spends one step' =(1 left.u.view)]
95+ ['view retains argument and child identity' =(expected-view view.u.view)]
96+ ['zero view fuel declined' ?=(~ (grec-unfold-view:jibb generic 0))]
97+ ['malformed proof declined' ?=(~ (grec-unfold-view:jibb generic(proof malformed) 2))]
98+ ['foreign recursive tail retained' ?:(?=(~ wrong-view) | !=(view.u.wrong-view view.u.target-view))]
99+ ['zero inference fuel declined' ?=(%| -.r0)]
100+ ['one-step leaf retains nominal identity' ?:(?=(%& -.r1) &(=(target typ.p.r1) =(0 jrec-fuel.state.p.r1)) |)]
101+ ['one-step node succeeds' ?:(?=(%& -.rnode) &(=(target typ.p.rnode) =(0 jrec-fuel.state.p.rnode)) |)]
102+ ['nested one-step declined' ?=(%| -.short)]
103+ ['nested two-step succeeds' ?:(?=(%& -.nested) &(=(target typ.p.nested) =(0 jrec-fuel.state.p.nested)) |)]
104+ ['siblings share fuel' &(?=(%| -.sibling-short) ?:(?=(%& -.siblings) =(0 jrec-fuel.state.p.siblings) |))]
105+ ['same-state retry agrees' =(r1 retry)]
106+ ['failed retry agrees' =(r0 failed-retry)]
107+ ['argument binding retains source identity' ?:(?=(%& -.rvar) =(target (itype-normalize:jibb typ.p.rvar sub.state.p.rvar)) |)]
108+ ['pending tag retained' ?:(?=(%& -.r1) (lien tags.state.p.r1 |=(entry=ipending:jibb =(entry tag))) |)]
109+ ['pending hole retained' ?:(?=(%& -.r1) =(holes.st holes.state.p.r1) |)]
110+ ['projection evidence retained' ?:(?=(%& -.r1) =(projections.st projections.state.p.r1) |)]
111+ ['hidden variable declined' hidden-safe]
112+ ['scan exact limit' (grec-direct-context:jibb target exact)]
113+ ['scan over limit declined' !(grec-direct-context:jibb target over)]
114+ ['faced scan exact limit' (grec-direct-context:jibb [%face %field target] face-exact)]
115+ ['faced scan over limit declined' !(grec-direct-context:jibb [%face %field target] exact)]
116+ ['structural substitution declined' !(grec-direct-context:jibb target ~[[9 [%cell [%atom %t] [%atom %t]]]])]
117+ ==
118+ =/ modules=jbundle:jibb ~[[/ui/tree/jibb tree] [/ui/tree-two/jibb tree] [/ui/tree-alias/jibb alias]]
119+ =/ small=jbundle:jibb ~[[/ui/tree/jibb tree-no-event] [/ui/tree-two/jibb tree-no-event]]
120+ =/ report=@t (golden-types-with-prepared-prelude:jibb /tests/tree/client/jibb client ~ ~ modules prep)
121+ =/ aliases=@t (golden-types-with-prepared-prelude:jibb /tests/tree/alias-client/jibb alias-client ~ ~ modules prep)
122+ =/ no-event=@t (golden-types-with-prepared-prelude:jibb /tests/tree/no-event-client/jibb no-event-client ~ ~ small prep)
123+ (cat 3 (of-wain:format (turn checks line)) (cat 3 '\0a' (cat 3 report (cat 3 '\0a' (cat 3 aliases (cat 3 '\0a' no-event))))))
124+++ test-generic-construction
125+ ^- tang
126+ ?~ tree ~[leaf+"tree source missing"]
127+ ?~ alias ~[leaf+"alias source missing"]
128+ ?~ client ~[leaf+"client source missing"]
129+ ?~ alias-client ~[leaf+"alias client missing"]
130+ ?~ tree-no-event ~[leaf+"tree without event missing"]
131+ ?~ no-event-client ~[leaf+"no-event client missing"]
132+ ?~ owner-positive ~[leaf+"owner source missing"]
133+ ?~ owner-negative ~[leaf+"invalid owner source missing"]
134+ ?~ sibling-values ~[leaf+"sibling source missing"]
135+ ?~ expected ~[leaf+"expected output missing"]
136+ =/ actual=@t
137+ (construction-output real-prelude real-internal (text tree) (text alias) (text client) (text alias-client) (text tree-no-event) (text no-event-client) (text owner-positive) (text owner-negative) (text sibling-values))
138+ ?. =((to-wain:format (cat 3 actual '\0a')) expected)
139+ ~[leaf+"generic construction output differs from expected"]
140+ ~
141+--
tests/lib/jibb-generic-recursion.hoon+1−1
@@ -156,7 +156,7 @@
156156 ?~ expected ~[leaf+"recursive expected output is missing"]
157157 =/ invalid=(list [label=@t src=@t])
158158 :~ ['unused mold hole declined' (text bad-mold)]
159− ['constructor declined' (text bad-constructor)]
159+ ['invalid constructor declined' (text bad-constructor)]
160160 ['matching declined' (text bad-match)]
161161 ['nonuniform recursion declined' (text bad-parameter)]
162162 ['identical local shadow declined' (text local-shadow)]
tests/type-generic-construction/about.txtnew file+9
@@ -0,0 +1,9 @@
1+source: docs/spec/grammar.md:57,135 — “Molds may be recursive”; “recursive molds go by name and module”.
2+
3+This shared eval/ship fixture checks a narrow source-proven tree construction
4+slice. It exercises both recognized source shapes, type arguments and aliases,
5+nominal origins, contextual field and payload constraints, shared fuel, and
6+unrelated pending state. The source reports are explicitly partial; a separate
7+owner check requires the entire library to succeed before interface export.
8+The view and substitution scan limits do not bound all inference or retries.
9+No runtime lowering, watchability, island acceptance or backend claim is made.
tests/type-generic-construction/alias-client.txtnew file+12
@@ -0,0 +1,12 @@
1+/= first /ui/tree
2+/= again /ui/tree
3+/= second /ui/tree-two
4+/= via /ui/tree-alias
5+++ text-event |= x=@t x
6+++ ship-event |= x=@t ~zod
7+++ via-leaf ^- (alias.via @t) [%text value="x"]
8+++ via-node ^- (alias.via @p) [%node event=ship-event children=~]
9+++ cross-alias |= x=(alias.via @t) ^- (element.first @t) [%node event=text-event children=~[x]]
10+++ cross-owner |= x=(element.again @t) ^- (alias.via @t) [%node event=text-event children=~[x]]
11+++ wrong-owner |= x=(element.second @t) ^- (alias.via @t) [%node event=text-event children=~[x]]
12+++ wrong-arg |= x=(alias.via @p) ^- (element.first @t) [%node event=text-event children=~[x]]
tests/type-generic-construction/alias.txtnew file+3
@@ -0,0 +1,3 @@
1+/= source /ui/tree
2++$ alias |$ [A] (element.source A)
3+++ identity |= x=(alias A) x
tests/type-generic-construction/client.txtnew file+24
@@ -0,0 +1,24 @@
1+/= first /ui/tree
2+/= second /ui/tree-two
3+++ text-event |= value=@t value
4+++ ship-event |= value=@t ~zod
5+++ leaf ^- (element.first @t) [%text value="hi"]
6+++ ship-leaf ^- (element.first @p) [%text value="hi"]
7+++ node ^- (element.first @t) [%node event=text-event children=~]
8+++ ship-node ^- (element.first @p) [%node event=ship-event children=~]
9+++ nested ^- (element.first @t) [%node event=text-event children=~[[%text value="x"]]]
10+++ wrong-event ^- (element.first @p) [%node event=text-event children=~]
11+++ wrong-leaf ^- (element.first @t) [%text value=~zod]
12+++ wrong-label ^- (element.first @t) [%text other="hi"]
13+++ bad-tag ^- (element.first @t) [%missing value="hi"]
14+++ foreign |= x=(element.second @t) ^- (element.first @t) [%node event=text-event children=~[x]]
15+++ generic |= event=$-(@t A) ^- (element.first A) [%node event=event children=~]
16+++ pending |= x=(element.first @t) [%missing x]
17+++ extra ^- (element.first @t) [%text value="hi" extra="bad"]
18+++ misplaced ^- (element.first @t) [%text value=~zod]
19+++ bad-child ^- (element.first @t) [%node event=text-event children=~["bad"]]
20+++ bad-children ^- (element.first @t) [%node event=text-event children="bad"]
21+++ label-swap ^- (element.first @t) [%node children=~ event=text-event]
22+++ wrong-nominal |= child=(element.second @p) ^- (element.first @t) [%node event=text-event children=~[child]]
23+++ wrong-argument |= child=(element.first @p) ^- (element.first @t) [%node event=text-event children=~[child]]
24+++ keep-generic |= f=$-(@t A) ^- (element.first A) [%node event=f children=~[[%text value="x"]]]
tests/type-generic-construction/expected.txtnew file+65
@@ -0,0 +1,65 @@
1+complete owner: ok
2+owner exports constructed nominal values: ok
3+invalid owner declined: ok
4+view spends one step: ok
5+view retains argument and child identity: ok
6+zero view fuel declined: ok
7+malformed proof declined: ok
8+foreign recursive tail retained: ok
9+zero inference fuel declined: ok
10+one-step leaf retains nominal identity: ok
11+one-step node succeeds: ok
12+nested one-step declined: ok
13+nested two-step succeeds: ok
14+siblings share fuel: ok
15+same-state retry agrees: ok
16+failed retry agrees: ok
17+argument binding retains source identity: ok
18+pending tag retained: ok
19+pending hole retained: ok
20+projection evidence retained: ok
21+hidden variable declined: ok
22+scan exact limit: ok
23+scan over limit declined: ok
24+faced scan exact limit: ok
25+faced scan over limit declined: ok
26+structural substitution declined: ok
27+partial inference; module not checked
28+++ text-event: $-(@t @t)
29+++ ship-event: $-(@t @p)
30+++ leaf: (element.first @t)
31+++ ship-leaf: (element.first @p)
32+++ node: (element.first @t)
33+++ ship-node: (element.first @p)
34+++ nested: (element.first @t)
35+++ wrong-event: recursive mold case does not fit its source shape
36+++ wrong-leaf: recursive mold case does not fit its source shape
37+++ wrong-label: recursive mold case does not fit its source shape
38+++ bad-tag: recursive mold constructor has no matching case
39+++ foreign: list item constraint is not supported by partial inference
40+++ generic: $-($-(@t A) (element.first A))
41+++ pending: tag needs context in partial inference
42+++ extra: recursive mold case does not fit its source shape
43+++ misplaced: recursive mold case does not fit its source shape
44+++ bad-child: list item constraint is not supported by partial inference
45+++ bad-children: recursive mold case needs unsupported inference
46+++ label-swap: recursive mold case does not fit its source shape
47+++ wrong-nominal: list item constraint is not supported by partial inference
48+++ wrong-argument: list item constraint is not supported by partial inference
49+++ keep-generic: $-($-(@t A) (element.first A))
50+partial inference; module not checked
51+++ text-event: $-(@t @t)
52+++ ship-event: $-(@t @p)
53+++ via-leaf: (alias.via @t)
54+++ via-node: (alias.via @p)
55+++ cross-alias: $-((alias.via @t) (element.first @t))
56+++ cross-owner: $-((element.again @t) (alias.via @t))
57+++ wrong-owner: list item constraint is not supported by partial inference
58+++ wrong-arg: list item constraint is not supported by partial inference
59+partial inference; module not checked
60+++ leaf: (element.first @t)
61+++ node: (element.first @p)
62+++ nested: (element.first @t)
63+++ generic: $-(A (element.first A))
64+++ wrong-owner: list item constraint is not supported by partial inference
65+++ wrong-field: recursive mold case needs unsupported inference
tests/type-generic-construction/no-event-client.txtnew file+8
@@ -0,0 +1,8 @@
1+/= first /ui/tree
2+/= second /ui/tree-two
3+++ leaf ^- (element.first @t) [%text value="x"]
4+++ node ^- (element.first @p) [%node children=~]
5+++ nested ^- (element.first @t) [%node children=~[[%text value="x"]]]
6+++ generic |= x=A ^- (element.first A) [%node children=~]
7+++ wrong-owner |= x=(element.second @t) ^- (element.first @t) [%node children=~[x]]
8+++ wrong-field ^- (element.first @t) [%node children="x"]
tests/type-generic-construction/owner-negative.txtnew file+10
@@ -0,0 +1,10 @@
1++$ element |$ [M]
2+ [%text value=@t]
3+ [%node event=$-(@t M) children=(list (element M))]
4+++ text-id |= x=(element @t) x
5+++ ship-id |= x=(element @p) x
6+++ generic-id |= x=(element A) x
7+++ text-event |= x=@t x
8+++ made-leaf ^- (element @t) [%text value="x"]
9+++ made-node ^- (element @t) [%node event=text-event children=~[[%text value="x"]]]
10+++ invalid ^- (element @t) [%text value=~zod]
tests/type-generic-construction/owner-positive.txtnew file+9
@@ -0,0 +1,9 @@
1++$ element |$ [M]
2+ [%text value=@t]
3+ [%node event=$-(@t M) children=(list (element M))]
4+++ text-id |= x=(element @t) x
5+++ ship-id |= x=(element @p) x
6+++ generic-id |= x=(element A) x
7+++ text-event |= x=@t x
8+++ made-leaf ^- (element @t) [%text value="x"]
9+++ made-node ^- (element @t) [%node event=text-event children=~[[%text value="x"]]]
tests/type-generic-construction/sibling-values.txtnew file+4
@@ -0,0 +1,4 @@
1+++ leaf [%text value="x"]
2+++ node [%node event=text-event children=~]
3+++ nested [%node event=text-event children=~[[%text value="x"]]]
4+++ siblings [%node event=text-event children=~[[%text value="x"] [%text value="y"]]]
tests/type-generic-construction/tree-no-event.txtnew file+4
@@ -0,0 +1,4 @@
1++$ element |$ [M]
2+ [%text value=@t]
3+ [%node children=(list (element M))]
4+++ generic-id |= x=(element A) x
tests/type-generic-construction/tree.txtnew file+6
@@ -0,0 +1,6 @@
1++$ element |$ [M]
2+ [%text value=@t]
3+ [%node event=$-(@t M) children=(list (element M))]
4+++ text-id |= x=(element @t) x
5+++ ship-id |= x=(element @p) x
6+++ generic-id |= x=(element A) x
tests/type-generic-recursion/about.txt+2−2
@@ -5,8 +5,8 @@ recursive identity: two distinct tagged cases, a text leaf and a node with a
55message-producing gate and a list of recursive children. The test-only tree is
66not a chosen element representation. A complete import-free owner module must
77check before its interface is published. Same-origin aliases retain identity,
8−while different origins and unproved casts, construction and matching remain
9−unsupported. An opaque recursive type prevents premature numeric defaulting;
8+while different origins, unproved casts and matching remain unsupported.
9+A wrong-aura constructor fails the complete owner check. An opaque recursive type prevents premature numeric defaulting;
1010its gate-freedom remains unknown. Source/cache/prelude changes, cycles,
1111nonuniform recursion, invalid unused molds and local shadows fail closed.
1212
tests/type-generic-recursion/bad-constructor.txt+1−1
@@ -5,4 +5,4 @@
55++ ship-id |= x=(element @p) x
66++ generic-id |= x=(element A) x
77
8−++ construct ^- (element @t) [%text value="hi"]
8+++ construct ^- (element @t) [%text value=~zod]
tests/type-generic-recursion/expected.txt+1−1
@@ -11,7 +11,7 @@ alteration rejects repeated line: ok
1111gate freedom unknown: ok
1212gate defaulting conservative: ok
1313unused mold hole declined: ok
14−constructor declined: ok
14+invalid constructor declined: ok
1515matching declined: ok
1616nonuniform recursion declined: ok
1717identical local shadow declined: ok
tools/testdata/ship-selftest+6
@@ -311,6 +311,7 @@ def generator_checks(tmp):
311311 'tests/lib/jibb-positional-face-guards.hoon',
312312 'tests/lib/jibb-generic-alias.hoon',
313313 'tests/lib/jibb-generic-recursion.hoon',
314+ 'tests/lib/jibb-generic-construction.hoon',
314315 'tests/lib/jibb-api-evidence.hoon',
315316 'tests/lib/jibb-ask-guards.hoon',
316317 'tests/lib/jibb-request-guards.hoon',
@@ -330,6 +331,11 @@ def generator_checks(tmp):
330331 'tree', 'tree-changed', 'alias', 'client', 'bad-mold',
331332 'bad-constructor', 'bad-match', 'bad-parameter', 'local-shadow',
332333 'local-distinct', 'local-only', 'list-shadow', 'chain', 'expected')} <= set(normal.files))
334+ check('generic construction sources and golden are assembled',
335+ {f'tests/type-generic-construction/{name}.txt' for name in (
336+ 'tree', 'alias', 'client', 'alias-client', 'tree-no-event',
337+ 'no-event-client', 'owner-positive', 'owner-negative',
338+ 'sibling-values', 'expected')} <= set(normal.files))
333339 check('direct gate source and golden are assembled',
334340 {'tests/type-direct-gate/source.txt', 'tests/type-direct-gate/expected.txt'}
335341 <= set(normal.files))
tools/type-generic-construction-testnew file+49
@@ -0,0 +1,49 @@
1+#!/usr/bin/env python3
2+"""Check source-backed generic recursive construction under eval."""
3+from pathlib import Path
4+import argparse
5+import difflib
6+import subprocess
7+import sys
8+import tempfile
9+
10+root = Path(__file__).resolve().parent.parent
11+parser = argparse.ArgumentParser(description=__doc__)
12+parser.add_argument('--lib', default=str(root / 'jibb-dev/lib/jibb.hoon'))
13+parser.add_argument('--update', action='store_true')
14+args = parser.parse_args()
15+fixture = root / 'tests/type-generic-construction'
16+source = (root / 'tests/lib/jibb-generic-construction.hoon').read_text()
17+source = source[source.index('\n|%\n') + 1:]
18+names = ['tree', 'alias', 'client', 'alias-client', 'tree-no-event',
19+ 'no-event-client', 'owner-positive', 'owner-negative', 'sibling-values']
20+cords = [item for name in names
21+ for item in ('--cord', f'{name}-text={fixture / (name + ".txt")}')]
22+cords += ['--cord', f'prelude={root / "jibb-dev/lib/jibb/prelude.jibb"}',
23+ '--cord', f'internal={root / "jibb-dev/lib/jibb/internal.jibb"}']
24+bindings = ''.join(f'=/ {name}=wain (to-wain:format {name}-text)\n'
25+ for name in names)
26+program = ("=/ jibb .\n=/ real-prelude=@t prelude\n"
27+ "=/ real-internal=@t internal\n" + bindings +
28+ "=/ expected=wain ~\n=/ suite\n" + source +
29+ "\n^- @t\n(construction-output:suite prelude internal " +
30+ ' '.join(name + '-text' for name in names) + ")\n")
31+with tempfile.TemporaryDirectory(prefix='jibb-generic-construction-') as tmp:
32+ path = Path(tmp) / 'test.hoon'
33+ path.write_text(program)
34+ result = subprocess.run([str(root / 'tools/hoon'), '--lib', args.lib, '--raw',
35+ '-f', str(path), *cords], stdout=subprocess.PIPE)
36+if result.returncode:
37+ sys.exit(result.returncode)
38+expected_path = fixture / 'expected.txt'
39+if args.update:
40+ expected_path.write_bytes(result.stdout)
41+else:
42+ expected = expected_path.read_bytes()
43+ if result.stdout != expected:
44+ sys.stderr.writelines(difflib.unified_diff(
45+ expected.decode().splitlines(keepends=True),
46+ result.stdout.decode().splitlines(keepends=True),
47+ fromfile='expected.txt', tofile='actual'))
48+ sys.exit(1)
49+print('source-backed generic construction guards passed')
tools/type-generic-recursion-test+1−1
@@ -32,7 +32,7 @@ program = ("=/ jibb .\n=/ real-prelude=@t prelude\n"
3232 "=/ expected=wain ~\n=/ suite\n" + source +
3333 "\n^- @t\n=/ invalid=(list [label=@t src=@t])\n"
3434 " :~ ['unused mold hole declined' bad-mold-text]\n"
35− " ['constructor declined' bad-constructor-text]\n"
35+ " ['invalid constructor declined' bad-constructor-text]\n"
3636 " ['matching declined' bad-match-text]\n"
3737 " ['nonuniform recursion declined' bad-parameter-text]\n"
3838 " ['identical local shadow declined' local-shadow-text]\n"