on master help sign in
Infer the todo API request interfaces

Infer supported APIs with multiple plain, typed or one-diff paths, retaining checked ordinary definitions, mark molds, poke constructors and source-qualified request evidence. Check all paths before publishing the interface. Allow a closed named gate sample to match an equivalent unfaced sample without erasing nested record faces. Exercise the unchanged todo API and its Hoon fixtures, named folds and atomic failures under eval and on the ship. Runtime execution and watchability proof remain unfinished.

commita1c5f35da4455b512e9b8162e513573555fa7ead authoriko <mail@iko.soy> dateSat 31 Oct 2026, 10:06 (7 hours ago) parentdde791c
15 files changed+558−35
docs/compiler.md+3−1 docs/development.md+4 docs/status.md+5−4 jibb-dev/lib/jibb.hoon+243−29 tests/lib/jibb-closed-gate-guards.hoon+60 tests/lib/jibb-todo-api.hoon+85 tests/type-todo-api/about.txt+1 tests/type-todo-api/expected.txt+14 tests/type-todo-api/negative-expected.txt+17 tests/type-todo-api/negative.txt+5 tests/type-todo-api/source.txt+14 tests/type-todo-api/unsupported.txt+13 tools/testdata/ship-selftest+3−1 tools/type-closed-gate-guards-test+29 tools/type-todo-api-test+62
docs/compiler.md+3−1
@@ -102,7 +102,9 @@ For a unique supported declaration, `+api-request-parts` retains that evidence w
102102
103103`+api-support-analysis-source` additionally infers supported ordinary API molds and values, resolves every declared mark, and retains their source, dependency and path evidence. Unsupported helpers or marks fail closed. An unused `/-` core is not independently validated, ordinary definitions that need derived gates remain unsupported, and this result is not a checked API interface or an exported request.
104104
105−The ordinary import resolver can expose a derived request-gate type for one plain API path with one full mark, no parameters, diffs or scry, and no other imports or definitions. Its sample is a reply gate from the full mark type to a fresh `M`; its result is `(ask M)`. Imported uses instantiate `M` independently. The complete checked source bundle, prelude, reserved scope, reader inputs, canonical API and case identities travel in a nonrecursive graph certificate across library imports. Cached interfaces must match those inputs and cannot hide an active ancestor; unknown legacy entries are rebuilt from their unique supplied source. Request-bearing closures are limited to 256 paths and requests. These are trusted type-analysis artifacts: no request lowering, runtime schema validation or value watchability proof is supplied. Other API request shapes remain unsupported.
105+The ordinary import resolver can infer derived request-gate types for multiple supported API paths. A case may take one typed path parameter before its reply gate, or one diff fold after the reply; a case with both, multiple parameters or diffs, or a separate scry path remains unsupported. Each reply maps the full mark type to a fresh `M` and the request result is `(ask M)`. Checked ordinary API values and molds, declared mark molds and a supported poke constructor are exported too. Every selected `/sur` core must have a directly used, successfully read mold; unselected core bindings are not independently validated, and Jibb module imports remain unsupported in this API fragment. The unchanged todo API fits this fragment.
106+
107+Each case's canonical API identity and source-derived schema travel across imports in a nonrecursive graph certificate containing the exact source bundle, prelude, reserved scope and trusted reader results. Cached interfaces must match those inputs and cannot hide an active ancestor; unknown legacy entries are rebuilt from their unique supplied source. Request-bearing closures are limited to 256 paths and requests. Closed gate samples can also fit when an unnamed input slot corresponds to a named one, provided products are identical and nested value faces and ambiguous cell tails remain intact. This is type inference only: request lowering, runtime schema validation and value watchability proofs are not implemented.
106108
107109`+golden-types-with-prelude` accepts case-local Jibb module sources alongside `/sur` and mark results and the vendored prelude source. It resolves reachable ordinary library modules in dependency order, caching completed interfaces per invocation and exporting supported closed value schemes, direct generic unions, and simple or nonrecursive generic mold aliases whose bodies the fragment supports. It detects missing sources and cycles and rejects a dependency if its interface cannot be inferred by the fragment. Qualified wings can also read nested fields of an imported value, instantiating that value’s scheme independently at each use; missing, ambiguous and non-record fields remain unsupported.
108110
docs/development.md+4
@@ -73,6 +73,8 @@ git clone https://github.com/urbit/tools /root/src/tools
7373| `tools/type-generic-alias-test` | Runs public/private generic union alias and provenance guards under eval and on the ship |
7474| `tools/type-positional-face-guards-test` | Runs positional-call and nested-face guards under eval and on the ship |
7575| `tools/type-qath-guards-test` | Runs source-provenance, recursive unfolding and face guards under eval and on the ship |
76+| `tools/type-todo-api-test` | Exercises the actual todo API through the real Hoon readers and partial checker |
77+| `tools/type-closed-gate-guards-test` | Checks closed gate sample labels and face uncertainty under eval and on the ship |
7678| `tools/type-ask-guards-test` | Runs ask origin and canonical list/unit bootstrap guards under eval and on the ship |
7779| `tools/type-request-guards-test` | Runs request evidence, source consistency and cache provenance guards under eval and on the ship |
7880| `tools/type-proj-guards-test` | Runs projection evidence, name-scope and dependency guards under eval and on the ship |
@@ -106,6 +108,8 @@ tools/type-list-alias-test # prelude list-alias guards pass
106108tools/type-generic-alias-test # generic union alias guards pass
107109tools/type-positional-face-guards-test # positional sample face guards pass
108110tools/type-qath-guards-test # qath source and unfolding guards pass
111+tools/type-todo-api-test # real todo API partial inference and reader guards pass
112+tools/type-closed-gate-guards-test # closed gate fit guards pass
109113tools/type-ask-guards-test # ask source and bootstrap guards pass
110114tools/type-request-guards-test # request evidence and cache guards pass
111115tools/type-proj-guards-test # projection evidence and scope guards pass
docs/status.md+5−4
@@ -33,11 +33,11 @@ For a unique supported path declaration, the request-parts analysis derives orde
3333
3434A separate supporting-definition analysis infers supported ordinary API molds and values and resolves all declared marks, retaining their source and dependency evidence. It fails closed on unsupported helpers or marks; unused `/-` cores are not independently validated, and definitions depending on derived gates remain unsupported. This does not check or export the whole API.
3535
36−Ordinary imports can infer the derived request-gate type for an API consisting of one plain path and one full mark, with no parameters, diffs, scry or additional definitions. The callback consumes that mark's type and produces a fresh `M`; the request result is `(ask M)`. Source-qualified request evidence and exact trusted graph inputs survive transitive imports and cached interfaces, while stale or ambiguous evidence fails closed. Unknown legacy cached entries are rebuilt from their supplied source. This is a narrow type-only interface: request execution, schema validation and proof that a request value is watchable remain unimplemented.
36+Ordinary imports can infer derived request-gate types for multiple supported API paths. A case may take one typed segment before the reply or one diff fold after it; the unchanged todo API's plain, typed and diff requests infer, including a named closed fold. Checked ordinary API definitions, mark molds and a supported poke constructor are exported. Each `/sur` core must have a directly used, successfully read mold; unselected bindings are not independently validated. Jibb module imports, scry paths, multiple parameters or diffs and a parameter together with a fold remain unsupported. Source-qualified request evidence and exact trusted graph inputs survive transitive imports and cached interfaces, while stale or ambiguous evidence fails closed. This is a type-only interface: request execution, schema validation and proof that a request value is watchable remain unimplemented. In one loaded-machine run, the real-todo eval took 195.83 s wall and 67.83 s user CPU; the prior prototype took 111.81 s wall and 64.60 s user. Both include Hoon compilation and reader setup and overlapped other work, so they are not isolated checker benchmarks.
3737
3838The Hoon reader's tagged-union source proof also handles `$~` default wrappers. A shared eval and ship golden exercises the unmodified Tlon `epic.saga` mold as a named payload in a labeled downstream extension, alongside overlap, recursive, shadow and malformed-wrapper controls. The tagged-source helper peels at most four consecutive defaults; defaults inside dependency proofs spend the existing semantic-node budget. Other unverified nested unions and dependencies remain unsupported.
3939
40−The repository spec records the subsequent type-system and prelude decisions, including the chosen `like` result-mapping gate; those changes also need synchronization to the Claude document. An initial inference fragment and a standalone Hoon type reader are being tested. The fragment explicitly reports that it does not check a module; partial bridges pass Hoon `*` in supported nonrecursive molds, selected Hoon atoms, term singletons, the null singleton, the two loobean singletons and their exact fork, nested faced or unfaced cells, supported disjoint unions and opaque standard collections from `/sur` molds and marks into inference. Collections retain their names and supported arguments for printing and unification; nested supported union arguments are retained too. The bridge trusts unchanged results from the source-aware reader; a materialized type graph alone cannot reveal cases Hoon already collapsed. The source-validated standard prelude `list` mold shares this representation with imported lists; other Jibb-written collection molds and most prelude collection operations remain unsupported; malformed names and arities fail closed. Selected self-recursive `/sur` molds, including Tlon `notes.import-node` with list-wrapped self references, cross as opaque module-and-name identities; aliases, foreign references, recursive marks, unfolding and construction remain unsupported. The reader handles mark samples, including a source-proved nonrecursive union from a single Ford-imported `/sur` core, recursive molds whose origins can be verified through Ford imports, nested collections, direct ordered maps, including source-validated named operands such as Tlon `notes.log`, certain ordered-map fields, direct `$^` unions whose cases can be proved disjoint, and one verified explicit union argument in a list, set, map or unit. Tagged `$%` cases can additionally use restricted local named payload molds, bare `~` fields and one level of local named tagged unions, as in real Tlon `notes`, with bounded source proof; unsupported or ambiguous dependencies fail closed. Ambiguous recursive origins fail closed; recognizable explicit unions are checked from source before Hoon can collapse their cases, as @iko confirmed for imported `$%`, `$?` and `$^`, while other nested unions, nested ordered maps, general union disjointness and complete import integration remain unfinished. Opt-in structured reader APIs preserve successful graphs and distinguish definite unsupported-aura and core evidence from failures the reader cannot prove; unverified union disjointness and name-only vase recognition remain unsupported. They revalidate and re-walk failed types. An opt-in partial checker entry point consumes unchanged reader results and reports definite aura or core errors at imported mold use sites when their inputs are unique. Ordered replay covers nested cell samples, record and function casts, and non-generic same-module aliases; unsupported prefixes, cycles and shadowing stop it. Union molds, generic applications, transitive modules and unsupported reader outcomes remain generic. Replay repeats inference and may add work. The partial checker resolves a subset of ordinary library imports in dependency order using supplied Jibb sources and a source-derived partial prelude interface, caches completed interfaces per run, detects missing sources and cycles, and resolves nested fields of imported values with independent scheme instantiation. The source-backed internal prelude defines `poke` and the public prelude re-exports it as a transparent type. A dedicated trusted preparation path supplies private comparison and arithmetic schemes to the Jibb-written `gth`, `add` and `dec` wrappers; ordinary imports cannot access them. The public `add` and `dec` wrappers use private arithmetic interfaces; supported inference preserves the shared atom constraint that excludes loobeans. Primitive execution, jet recognition and length remain unfinished. The partial checker resolves its supported source shape and rejects ordinary imports of the private path. An imported API with one mark and a matching direct `+$ poke` declaration can export a constructor and retain agent/mark metadata when the representation has the supported agent case. A separate partial analysis resolves supported API path declarations and their full and diff mark types, including the todo paths; it does not by itself create requests or check an API module. Ordinary imports additionally infer the narrow one-path, one-full-mark request-gate type described above. Complete private module checking, other API shapes, request lowering, root API and kind checking, stores, complete `ask` and `pure` checking and runtime effects remain unfinished. A possible store or root API with `+$ poke` reports unsupported kind or scope, while definite duplicates and unrelated shadowing still report conflicts. The prelude includes the doc-exact generic `cmd` union; the prelude inventory is incomplete. The inference fragment supports unrestricted `@`, selected one-way widenings including aura-family ancestors, generalized atom constraints arising from explicit capitals or bare sample binders, acyclic local value definitions with captures, selected single self-recursive top-level and local gates, simple and nonrecursive generic mold aliases, selected faced record casts and field reads, and contextual tags, `~` and constructors for closed disjoint unions, including direct nonrecursive generic unions and propagation through complete loobean and unit-shaped branches in casts and known union-sample calls, including nested branches. Known loobean result context also propagates through supported complete branches and binder-only cell arms; binder-only arms over union subjects remain unsupported. A directly known named union can instantiate an unconstrained generic gate sample with its origin, generic arguments and faces preserved; unnamed, pending, recursive and variable-bound cases remain unsupported. A known gate with an unresolved result variable can receive a known enclosing result type before its arguments are inferred; unknown calls and unresolved targets remain conservative. Multi-argument calls form a right-nested sample, retain known faces and shared type variables, and can use known sample positions to contextualize list and union arguments. A definite mismatch points to the failing argument when the fragment can establish it, otherwise to the combined sample; unsupported contextual forms still fail closed. For a supported recursive gate, the checker infers the sample first and uses its exact type, including field faces, for one monomorphic self reference. It generalizes after the body is checked; subsequent uses instantiate it independently. Incompatible recursive arguments can report the gate and argument sites. For a complete loobean branch with one direct self-call arm, a separately inferred closed base type can seed the recursive result before the whole body is checked. Open or pending base types do not seed it; other branch shapes remain subject to the fragment’s existing limits. A supported single local recursive gate also uses a monomorphic self reference and generalizes variables free of the enclosing environment after checking its body; captured variables remain shared. The checker collects written capitals per `++` before inference, excluding nested definitions: a capital used in an enclosing definition is shared with its locals even when written later, while a capital introduced only in a local definition is generalized there. Sibling locals can introduce independent capitals with the same spelling; generic mold parameters keep their own scope. Top-level groups of syntactic gates are inferred together with monomorphic placeholders and shared substitutions, then generalized only after the whole group checks; downstream uses and library interfaces instantiate those schemes independently. A dependency graph identifies groups with two iterative depth-first passes; its conservative local-reference edges can reject otherwise valid programs. Supported local mutually recursive gate groups also use shared monomorphic placeholders and generalize only after the group checks, preserving captured outer variables. A bounded dependency proof reports certain non-gate cycles through values and gates when an explicit atom or loobean result cast and simple unshadowed expressions establish the cycle. Other mixed gate/value cycles, polymorphic recursion, unresolved literal constraints and unresolved branch joins remain unsupported. Reporting repeats inference for a single recursive gate outside a group; grouped reports use the stored schemes. Its performance cost remains unmeasured. A single-arm cell pattern accepts two or more binders, the last capturing the remaining tail; duplicate and shadowed binders are rejected. A structured single-arm pattern also works for a known non-union cell when coverage is proven, including nested literal and wildcard positions. Intermediate union or collection tails, nested patterns, wildcards and faced records remain unsupported. String interpolation accepts known atom auras, unrestricted `@`, tapes and isolated decimal literals; a known cell asks for a printer, and an explicitly generic atom asks for an aura. Loobeans, term singletons, non-tape collections, open types and a type fixed only later remain unsupported. A known closed union with two or more arms supports patterns that provably cover or exclude each remaining case, including payload binders, imported and generic cases, and binder-free catch-alls. Partial boolean patterns can split known `?` positions into their two noun values, up to 64 alternatives across the union; binders retain their original case type. Missing alternatives and covered arms are reported. Other partial patterns, expansions above the limit, binders spanning distinct cases, differing arm result types and some complex two-arm subjects remain unsupported. A cast or known call sample can supply result context through whole-case arms and supported finite loobean refinements for a known union accessed by a wing, a grouped wing or a supported shallow call, including nested cell and list positions; other partial-pattern contexts and subject forms remain unsupported. The Grammar’s `step` block infers in this fragment, but its module is not yet checked. A bare tag proved absent from a known union reports the union and tag sites in casts, single-argument calls, unit payloads, list items and supported branch arms; imported unnamed unions print their cases. Ordered fit replay can identify a missing tag nested in a syntactic tuple, stopping at an earlier mismatch or uncertainty. Known faced records support uniquely named field changes, including nested wings, with field context passed to replacements and the original record type retained. Missing fields, empty changes and definite mismatches report sites; the expected-type site is the edit because the original field declaration location is not retained. Unknown or partly unfaced spines, ambiguous fields and repeated edits remain unsupported. A known record cast supplies field context to its tuple or record initializer, including nested list and union fields; mismatch diagnostics use the cast mold because the original field declaration location is not retained. Isolated cart-style `init` and `update` expressions infer, while the complete cart-store fails closed because its kind and declaration scope remain unchecked. Uncontextualized list literals infer a fresh element type, including empty and nested lists, with independently generalized uses. Unresolved item tags and uncertain widening remain unsupported. Backtick unit construction uses the validated standard prelude unit template, infers its payload without losing pending constraints, and makes the prelude’s `some` available. Ordinary result annotations also let its `bind`, `biff`, `both`, `clap`, `clef` and `flit` infer. A known unit in a cast, call, list item or record field supplies context to its payload, including nested units and lists; mismatches identify the payload and context. Unrelated union identities, some variable-dependent fits, and missing or altered prelude unit templates remain unsupported. Contextual null-headed cells fit their cases, but do not supply a `|=%$` constructor; unresolved nulls remain pending and are reported separately from tags. A known union of `~` and a null-headed cell supports a complete `~` / `[~ value]` branch in either order, including shadow checks; unknown subjects, missing or duplicate arms, and unresolved branch results remain unsupported. This fragment infers the prelude’s `fall` and `need` signatures. With the validated prelude list and a known cast or gate sample, empty, nonempty and nested list literals infer their element types, including contextual union tags; a mismatched element names both sites. In that context `~` and zero-valued terms fit an empty list, and two-item unfaced cells form cons lists, including nested cons and imported-list contexts; invalid heads or tails name both sites. A known gate sample also supplies context to matching positions in a single tuple or record argument, including nested cells; zero literals can fit list positions. A bound zero constant remains unsupported rather than a definite mismatch, and an unknown callee does not supply context. Faced or longer cells and a missing or altered prelude list fail closed. A known list result also supplies context through complete loobean, unit-shaped and list-shaped branches, including recursive and nested branches. For a known standard list with a supported element type, a complete `~` / `[item rest]` branch infers the element and tail types in either arm order and checks binders for shadowing. Unknown subjects, unsupported element types and other or incomplete list patterns remain unsupported. A value with type variable `A` can fit a field whose type is that same `A`, including in generic union payloads and nested or repeated record fields. Other variable-dependent face merges remain unsupported. Forced equality of distinct written capitals now reports both source sites, following @iko’s decision; forcing one to a concrete type remains unsupported. Imported record spines with repeated faces fail closed where used, but only with a generic unsupported diagnostic. Hoon may erase unfaced nested-cell boundaries, so the check can conservatively reject valid names in distinct erased scopes. Supported closed unions print using an in-scope name or imported alias; an unnamed nongeneric union prints its cases, parenthesized when nested. The latter is print-only. Generic union types retain all arguments in their printed name; if that name is unavailable, the partial printer reports the limitation. Different import faces for the same origin remain compatible; compatibility across distinct union origins and some generic-argument comparisons remain unsupported. Nonrecursive generic aliases can reorder, repeat or omit parameters while retaining a union’s origin, including through local scopes and library interfaces. A generic union declared directly inside a body remains unsupported, as do differing-variable face constraints in a union payload. A transitive generic union whose name is unavailable is reported as unsupported instead of hiding its arguments. Unfixed decimal literals default to `@ud` in gate-free inferred types; recursive molds, general branch tag context, other constraint cases and defaulting inside gate types still need work. Collection recognition checks the originating core and fails closed on shadows or unsupported Ford subject shapes; a wrapper regression also rejects forged subjects, samples and tails. Its golden fixtures include three real Tlon files. A Clay test passes a Ford-built nested union through the reader into partial inference. The partial prelude includes `murn`, `join`, `snip`, `rear` and the agreed shared-type `clap`; supported calls infer, while backend validation remains outstanding, and [prelude-inventory.md](prelude-inventory.md) is a source audit, not the complete inventory or signatures. The milestone remains unfinished.
40+The repository spec records the subsequent type-system and prelude decisions, including the chosen `like` result-mapping gate; those changes also need synchronization to the Claude document. An initial inference fragment and a standalone Hoon type reader are being tested. The fragment explicitly reports that it does not check a module; partial bridges pass Hoon `*` in supported nonrecursive molds, selected Hoon atoms, term singletons, the null singleton, the two loobean singletons and their exact fork, nested faced or unfaced cells, supported disjoint unions and opaque standard collections from `/sur` molds and marks into inference. Collections retain their names and supported arguments for printing and unification; nested supported union arguments are retained too. The bridge trusts unchanged results from the source-aware reader; a materialized type graph alone cannot reveal cases Hoon already collapsed. The source-validated standard prelude `list` mold shares this representation with imported lists; other Jibb-written collection molds and most prelude collection operations remain unsupported; malformed names and arities fail closed. Selected self-recursive `/sur` molds, including Tlon `notes.import-node` with list-wrapped self references, cross as opaque module-and-name identities; aliases, foreign references, recursive marks, unfolding and construction remain unsupported. The reader handles mark samples, including a source-proved nonrecursive union from a single Ford-imported `/sur` core, recursive molds whose origins can be verified through Ford imports, nested collections, direct ordered maps, including source-validated named operands such as Tlon `notes.log`, certain ordered-map fields, direct `$^` unions whose cases can be proved disjoint, and one verified explicit union argument in a list, set, map or unit. Tagged `$%` cases can additionally use restricted local named payload molds, bare `~` fields and one level of local named tagged unions, as in real Tlon `notes`, with bounded source proof; unsupported or ambiguous dependencies fail closed. Ambiguous recursive origins fail closed; recognizable explicit unions are checked from source before Hoon can collapse their cases, as @iko confirmed for imported `$%`, `$?` and `$^`, while other nested unions, nested ordered maps, general union disjointness and complete import integration remain unfinished. Opt-in structured reader APIs preserve successful graphs and distinguish definite unsupported-aura and core evidence from failures the reader cannot prove; unverified union disjointness and name-only vase recognition remain unsupported. They revalidate and re-walk failed types. An opt-in partial checker entry point consumes unchanged reader results and reports definite aura or core errors at imported mold use sites when their inputs are unique. Ordered replay covers nested cell samples, record and function casts, and non-generic same-module aliases; unsupported prefixes, cycles and shadowing stop it. Union molds, generic applications, transitive modules and unsupported reader outcomes remain generic. Replay repeats inference and may add work. The partial checker resolves a subset of ordinary library imports in dependency order using supplied Jibb sources and a source-derived partial prelude interface, caches completed interfaces per run, detects missing sources and cycles, and resolves nested fields of imported values with independent scheme instantiation. The source-backed internal prelude defines `poke` and the public prelude re-exports it as a transparent type. A dedicated trusted preparation path supplies private comparison and arithmetic schemes to the Jibb-written `gth`, `add` and `dec` wrappers; ordinary imports cannot access them. The public `add` and `dec` wrappers use private arithmetic interfaces; supported inference preserves the shared atom constraint that excludes loobeans. Primitive execution, jet recognition and length remain unfinished. The partial checker resolves its supported source shape and rejects ordinary imports of the private path. An imported API with one mark and a matching direct `+$ poke` declaration can export a constructor and retain agent/mark metadata when the representation has the supported agent case. A separate partial analysis resolves supported API path declarations and their full and diff mark types, including the todo paths; it does not by itself create requests or check an API module. Ordinary imports additionally infer the supported multi-path request-gate types described above. Complete private module checking, other API shapes, request lowering, root API and kind checking, stores, complete `ask` and `pure` checking and runtime effects remain unfinished. A possible store or root API with `+$ poke` reports unsupported kind or scope, while definite duplicates and unrelated shadowing still report conflicts. The prelude includes the doc-exact generic `cmd` union; the prelude inventory is incomplete. The inference fragment supports unrestricted `@`, selected one-way widenings including aura-family ancestors, generalized atom constraints arising from explicit capitals or bare sample binders, acyclic local value definitions with captures, selected single self-recursive top-level and local gates, simple and nonrecursive generic mold aliases, selected faced record casts and field reads, and contextual tags, `~` and constructors for closed disjoint unions, including direct nonrecursive generic unions and propagation through complete loobean and unit-shaped branches in casts and known union-sample calls, including nested branches. Known loobean result context also propagates through supported complete branches and binder-only cell arms; binder-only arms over union subjects remain unsupported. A directly known named union can instantiate an unconstrained generic gate sample with its origin, generic arguments and faces preserved; unnamed, pending, recursive and variable-bound cases remain unsupported. A known gate with an unresolved result variable can receive a known enclosing result type before its arguments are inferred; unknown calls and unresolved targets remain conservative. Multi-argument calls form a right-nested sample, retain known faces and shared type variables, and can use known sample positions to contextualize list and union arguments. A definite mismatch points to the failing argument when the fragment can establish it, otherwise to the combined sample; unsupported contextual forms still fail closed. For a supported recursive gate, the checker infers the sample first and uses its exact type, including field faces, for one monomorphic self reference. It generalizes after the body is checked; subsequent uses instantiate it independently. Incompatible recursive arguments can report the gate and argument sites. For a complete loobean branch with one direct self-call arm, a separately inferred closed base type can seed the recursive result before the whole body is checked. Open or pending base types do not seed it; other branch shapes remain subject to the fragment’s existing limits. A supported single local recursive gate also uses a monomorphic self reference and generalizes variables free of the enclosing environment after checking its body; captured variables remain shared. The checker collects written capitals per `++` before inference, excluding nested definitions: a capital used in an enclosing definition is shared with its locals even when written later, while a capital introduced only in a local definition is generalized there. Sibling locals can introduce independent capitals with the same spelling; generic mold parameters keep their own scope. Top-level groups of syntactic gates are inferred together with monomorphic placeholders and shared substitutions, then generalized only after the whole group checks; downstream uses and library interfaces instantiate those schemes independently. A dependency graph identifies groups with two iterative depth-first passes; its conservative local-reference edges can reject otherwise valid programs. Supported local mutually recursive gate groups also use shared monomorphic placeholders and generalize only after the group checks, preserving captured outer variables. A bounded dependency proof reports certain non-gate cycles through values and gates when an explicit atom or loobean result cast and simple unshadowed expressions establish the cycle. Other mixed gate/value cycles, polymorphic recursion, unresolved literal constraints and unresolved branch joins remain unsupported. Reporting repeats inference for a single recursive gate outside a group; grouped reports use the stored schemes. Its performance cost remains unmeasured. A single-arm cell pattern accepts two or more binders, the last capturing the remaining tail; duplicate and shadowed binders are rejected. A structured single-arm pattern also works for a known non-union cell when coverage is proven, including nested literal and wildcard positions. Intermediate union or collection tails, nested patterns, wildcards and faced records remain unsupported. String interpolation accepts known atom auras, unrestricted `@`, tapes and isolated decimal literals; a known cell asks for a printer, and an explicitly generic atom asks for an aura. Loobeans, term singletons, non-tape collections, open types and a type fixed only later remain unsupported. A known closed union with two or more arms supports patterns that provably cover or exclude each remaining case, including payload binders, imported and generic cases, and binder-free catch-alls. Partial boolean patterns can split known `?` positions into their two noun values, up to 64 alternatives across the union; binders retain their original case type. Missing alternatives and covered arms are reported. Other partial patterns, expansions above the limit, binders spanning distinct cases, differing arm result types and some complex two-arm subjects remain unsupported. A cast or known call sample can supply result context through whole-case arms and supported finite loobean refinements for a known union accessed by a wing, a grouped wing or a supported shallow call, including nested cell and list positions; other partial-pattern contexts and subject forms remain unsupported. The Grammar’s `step` block infers in this fragment, but its module is not yet checked. A bare tag proved absent from a known union reports the union and tag sites in casts, single-argument calls, unit payloads, list items and supported branch arms; imported unnamed unions print their cases. Ordered fit replay can identify a missing tag nested in a syntactic tuple, stopping at an earlier mismatch or uncertainty. Known faced records support uniquely named field changes, including nested wings, with field context passed to replacements and the original record type retained. Missing fields, empty changes and definite mismatches report sites; the expected-type site is the edit because the original field declaration location is not retained. Unknown or partly unfaced spines, ambiguous fields and repeated edits remain unsupported. A known record cast supplies field context to its tuple or record initializer, including nested list and union fields; mismatch diagnostics use the cast mold because the original field declaration location is not retained. Isolated cart-style `init` and `update` expressions infer, while the complete cart-store fails closed because its kind and declaration scope remain unchecked. Uncontextualized list literals infer a fresh element type, including empty and nested lists, with independently generalized uses. Unresolved item tags and uncertain widening remain unsupported. Backtick unit construction uses the validated standard prelude unit template, infers its payload without losing pending constraints, and makes the prelude’s `some` available. Ordinary result annotations also let its `bind`, `biff`, `both`, `clap`, `clef` and `flit` infer. A known unit in a cast, call, list item or record field supplies context to its payload, including nested units and lists; mismatches identify the payload and context. Unrelated union identities, some variable-dependent fits, and missing or altered prelude unit templates remain unsupported. Contextual null-headed cells fit their cases, but do not supply a `|=%$` constructor; unresolved nulls remain pending and are reported separately from tags. A known union of `~` and a null-headed cell supports a complete `~` / `[~ value]` branch in either order, including shadow checks; unknown subjects, missing or duplicate arms, and unresolved branch results remain unsupported. This fragment infers the prelude’s `fall` and `need` signatures. With the validated prelude list and a known cast or gate sample, empty, nonempty and nested list literals infer their element types, including contextual union tags; a mismatched element names both sites. In that context `~` and zero-valued terms fit an empty list, and two-item unfaced cells form cons lists, including nested cons and imported-list contexts; invalid heads or tails name both sites. A known gate sample also supplies context to matching positions in a single tuple or record argument, including nested cells; zero literals can fit list positions. A bound zero constant remains unsupported rather than a definite mismatch, and an unknown callee does not supply context. Faced or longer cells and a missing or altered prelude list fail closed. A known list result also supplies context through complete loobean, unit-shaped and list-shaped branches, including recursive and nested branches. For a known standard list with a supported element type, a complete `~` / `[item rest]` branch infers the element and tail types in either arm order and checks binders for shadowing. Unknown subjects, unsupported element types and other or incomplete list patterns remain unsupported. A value with type variable `A` can fit a field whose type is that same `A`, including in generic union payloads and nested or repeated record fields. Other variable-dependent face merges remain unsupported. Forced equality of distinct written capitals now reports both source sites, following @iko’s decision; forcing one to a concrete type remains unsupported. Imported record spines with repeated faces fail closed where used, but only with a generic unsupported diagnostic. Hoon may erase unfaced nested-cell boundaries, so the check can conservatively reject valid names in distinct erased scopes. Supported closed unions print using an in-scope name or imported alias; an unnamed nongeneric union prints its cases, parenthesized when nested. The latter is print-only. Generic union types retain all arguments in their printed name; if that name is unavailable, the partial printer reports the limitation. Different import faces for the same origin remain compatible; compatibility across distinct union origins and some generic-argument comparisons remain unsupported. Nonrecursive generic aliases can reorder, repeat or omit parameters while retaining a union’s origin, including through local scopes and library interfaces. A generic union declared directly inside a body remains unsupported, as do differing-variable face constraints in a union payload. A transitive generic union whose name is unavailable is reported as unsupported instead of hiding its arguments. Unfixed decimal literals default to `@ud` in gate-free inferred types; recursive molds, general branch tag context, other constraint cases and defaulting inside gate types still need work. Collection recognition checks the originating core and fails closed on shadows or unsupported Ford subject shapes; a wrapper regression also rejects forged subjects, samples and tails. Its golden fixtures include three real Tlon files. A Clay test passes a Ford-built nested union through the reader into partial inference. The partial prelude includes `murn`, `join`, `snip`, `rear` and the agreed shared-type `clap`; supported calls infer, while backend validation remains outstanding, and [prelude-inventory.md](prelude-inventory.md) is a source audit, not the complete inventory or signatures. The milestone remains unfinished.
4141
4242The shared prelude interface reserves every name present in the supplied vendored source and makes only supported inferred entries available, including `list`, `unit`, `same`, `fall`, `need`, `bond`, `lure`, `weld`, `snoc` and `turn`. The validated standard `list` mold is injected when the prelude’s own gates are inferred; `weld` and `turn` have ordinary explicit result casts. An absent or altered `list` is not injected, and an altered declaration cannot leak through direct or transitive mold aliases. The same validated template is available when prelude mold aliases are prepared, so supported direct and transitive aliases can use it. Unsupported entries such as most list helpers remain reserved but unavailable; backend validation of the helpers remains outstanding. All callers use the same prelude origin, so a library can export a type containing unqualified `(unit A)` to another module. The source-only `+prepare-prelude` rejects empty, malformed, conflicting or import-bearing source; `+prepare-prelude-with-internal` also accepts the supported private internal import. Prepared values are trusted and must come unchanged from the matching preparation arm. When canonical public list or unit templates are missing or altered, independently inferred private molds remain available, while molds that depend on those templates do not. The module-scope fixture compares the prepared and source-taking paths against the real prelude and a two-module golden under eval and on the ship. The source and checker remain incomplete, so this does not establish complete shadowing coverage or a checked module.
4343
@@ -58,7 +58,7 @@ What the plan asks for, and where it is:
5858Besides the plan's list:
5959
6060- 638 golden cases: 418 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.
61−- The 638-case corpus passed under `urbit eval` in 90.4 s while other checks were running. The fake ship passed all 660 tests, including the importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, ask, default-wrapper importer, projection, arithmetic, generic-alias, positional-face, API-evidence and request guards, with 64.3 s for sync and 80.5 s for tests. 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.
61+- The 638-case corpus passed under `urbit eval` in 273.8 s while other checks and a separate system build were running. The fake ship passed all 662 tests, including the 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 and todo API guards, with 227.9 s for sync and 198.3 s for tests under the same load. 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.
6262- `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.
6363- 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.
6464- 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).
@@ -150,7 +150,8 @@ Besides the plan's list:
150150| f0f6954 | 2026-10-31 | Analyze supported API helper definitions and declared marks |
151151| 3f55af7 | 2026-10-31 | Define source-backed transparent ask and pure |
152152| ae1ec26 | 2026-10-31 | Prove selected default-wrapped Hoon unions from source |
153−| This change | 2026-10-31 | Infer a source-backed request-gate type through ordinary imports |
153+| dde791c | 2026-10-31 | Infer a source-backed request-gate type through ordinary imports |
154+| This change | 2026-10-31 | Infer supported multi-path requests and named closed folds |
154155
155156## Checking it
156157
jibb-dev/lib/jibb.hoon+243−29
@@ -4628,14 +4628,25 @@
46284628:: proofs to fewer than 1,024 cells; the projection planner has a separate
46294629:: 4,096-step bound. These are inference limits, not language limits.
46304630::
4631−:: A single plain API path with one full mark can expose a polymorphic
4632−:: request-gate type through ordinary imports. Its source-derived schema,
4633−:: canonical API path and complete dependency evidence travel with checked
4634−:: interfaces. The closure is bound to the exact supplied bundle, prelude,
4635−:: scope and trusted reader results; unknown cached entries are rebuilt.
4636−:: Request-bearing closures are limited to 256 source paths and requests.
4637−:: This is type inference only: there is no request lowering, runtime schema
4638−:: validator or proof that an arbitrary ask value is watchable.
4631+:: Supported API imports derive request-gate types atomically for multiple
4632+:: paths, including one typed segment or one diff fold per case. Their checked
4633+:: ordinary definitions, mark molds and supported poke constructor are also
4634+:: exported. A selected /sur mold is checked through its trusted reader result;
4635+:: unused cores and other bindings are not independently validated. APIs
4636+:: with /= imports, separate scry paths, multiple parameters or diffs, or a
4637+:: parameter together with a fold remain unsupported. The unchanged todo API
4638+:: fits this fragment, including named closed fold gates.
4639+::
4640+:: Each case's source-derived schema and canonical API identity travel in a
4641+:: dependency closure bound to the exact supplied bundle, prelude, scope and
4642+:: trusted reader results. Unknown cached entries are rebuilt. Request-bearing
4643+:: closures are limited to 256 source paths and requests. This is type
4644+:: inference only: no request lowering, runtime schema validator or proof that
4645+:: an arbitrary ask value is watchable is supplied.
4646+::
4647+:: A closed gate sample may omit one outer face from a known input slot when
4648+:: its product is identical. Distinct written names, nested value faces,
4649+:: ambiguous cell tails and raw-variable-origin fits retain their prior rules.
46394650::
46404651:: In one synthetic eval of a 48-module deep and 48-module wide request-free
46414652:: graph, baseline versus certificate inference took 21.53 s versus 23.10 s
@@ -4967,6 +4978,42 @@
49674978 * |
49684979 ==
49694980::
4981+:: Closed function samples may omit a slot face, but a known name must
4982+:: match another known name. Keep nested value faces and require exact
4983+:: invariant compatibility; a faced tail against a cell is ambiguous.
4984+++ itype-closed-slot-match
4985+ |= [a=itype b=itype]
4986+ ^- ?
4987+ ?: &(?=(%face -.a) ?=(%face -.b))
4988+ ?. =(name.a name.b) |
4989+ ?=(^ (itype-unify body.a body.b ~))
4990+ ?: ?=(%face -.a) ?=(^ (itype-unify body.a b ~))
4991+ ?: ?=(%face -.b) ?=(^ (itype-unify a body.b ~))
4992+ ?=(^ (itype-unify a b ~))
4993+++ itype-closed-sample-match
4994+ |= [a=itype b=itype fuel=@ud]
4995+ ^- ?
4996+ ?: =(0 fuel) |
4997+ ?. &(?=(%cell -.a) ?=(%cell -.b)) |
4998+ ?. (itype-closed-slot-match hed.a hed.b) |
4999+ =/ x=itype tal.a
5000+ =/ y=itype tal.b
5001+ ?: &(?=(%cell -.x) ?=(%cell -.y))
5002+ ?=(^ (itype-unify x y ~))
5003+ ?: |(&(?=(%face -.x) ?=(%cell -.y)) &(?=(%cell -.x) ?=(%face -.y))) |
5004+ (itype-closed-slot-match x y)
5005+++ itype-closed-gates-match
5006+ |= [a=itype b=itype]
5007+ ^- ?
5008+ ?. &(?=(%fn -.a) ?=(%fn -.b)) |
5009+ ?. &(?=(%cell -.sample.a) ?=(%cell -.sample.b)) |
5010+ ?. =(product.a product.b) |
5011+ ?^ (itype-vars a) |
5012+ ?^ (itype-vars b) |
5013+ ?: |((itype-has-tag a) (itype-has-tag b)) |
5014+ ?. (itype-closed-sample-match sample.a sample.b 1.024) |
5015+ ?=(^ (itype-unify product.a product.b ~))
5016+::
49705017++ itype-face-asym-collection-args
49715018 |= [as=(list itype) bs=(list itype) sub=isub]
49725019 ^- ?
@@ -5012,6 +5059,12 @@
50125059:: labels. Ignore one raw sample-slot face per written argument while
50135060:: checking value faces as usual. An unknown or variable-bound cell spine
50145061:: cannot justify this distinction, so keep the existing conservative guard.
5062+++ itype-positional-slot-face-asym
5063+ |= [actual=itype expected=itype sub=isub]
5064+ ^- ?
5065+ ?: &(?=(%fn -.actual) ?=(%fn -.expected) (itype-closed-gates-match actual expected)) |
5066+ (itype-face-asym actual expected sub)
5067+::
50155068++ itype-positional-face-asym
50165069 |= [args=(list expr) actual=itype expected=itype sub=isub]
50175070 ^- ?
@@ -5019,11 +5072,11 @@
50195072 ?~ t.args
50205073 =/ slot=itype expected
50215074 =? slot ?=(%face -.slot) body.slot
5022− (itype-face-asym actual slot sub)
5075+ (itype-positional-slot-face-asym actual slot sub)
50235076 ?. &(?=(%cell -.actual) ?=(%cell -.expected)) &
50245077 =/ slot=itype hed.expected
50255078 =? slot ?=(%face -.slot) body.slot
5026− ?: (itype-face-asym hed.actual slot sub) &
5079+ ?: (itype-positional-slot-face-asym hed.actual slot sub) &
50275080 $(args t.args, actual tal.actual, expected tal.expected)
50285081::
50295082++ itype-output-may-be-cell
@@ -5333,6 +5386,7 @@
53335386 $(actual body.actual, expected body.expected, skip-width |, spine-proof |)
53345387 ?: ?=(%face -.actual) $(actual body.actual, skip-width |, spine-proof |)
53355388 ?: ?=(%face -.expected) $(expected body.expected, skip-width |, spine-proof |)
5389+ ?: &(!uncertain (itype-closed-gates-match actual expected)) [%ok sub next]
53365390 ?: (itype-face-asym actual expected sub) [%unknown ~]
53375391 ?: |((itype-has-any-atom actual sub) (itype-has-any-atom expected sub))
53385392 [%unknown ~]
@@ -10852,17 +10906,24 @@
1085210906 =/ parsed (parse file src)
1085310907 ?: ?=(%| -.parsed) [%| 'request source does not parse']
1085410908 =/ mod=module p.parsed
10855− ?. ?=([[%mark *] ~] imports.mod) [%| 'request imports need unsupported inference']
10909+ ?. ?=(?([[%mark *] ~] [[%mark *] [%mark *] ~]) imports.mod) [%| 'request imports need unsupported inference']
1085610910 ?. ?=([[%mold *] ~] defs.mod) [%| 'request definitions need unsupported inference']
1085710911 ?. =(%paths name.i.defs.mod) [%| 'request declaration needs unsupported inference']
1085810912 ?. ?=([* ~] paths.ev) [%| 'request count needs unsupported inference']
1085910913 =/ p=iapi-path-typed i.paths.ev
1086010914 =/ parts=(unit iapi-request-parts) (api-request-parts `ev name.path.p)
1086110915 ?~ parts [%| 'request components need unsupported inference']
10862− ?^ parameters.u.parts [%| 'request parameters need unsupported inference']
10863− ?^ fold.u.parts [%| 'request fold needs unsupported inference']
10916+ ?. ?=(?(~ [* ~]) parameters.u.parts) [%| 'request parameters need unsupported inference']
10917+ ?: &(?=(^ fold.u.parts) ?=(^ parameters.u.parts)) [%| 'request parameter and fold need unsupported inference']
1086410918 ?^ scry.path.p [%| 'request scry needs unsupported inference']
10865− ?. ?=([* ~] marks.support) [%| 'request marks need unsupported inference']
10919+ =/ valid-marks=?
10920+ ?~ fold.u.parts ?=([* ~] marks.support)
10921+ ?=([* [* ~]] marks.support)
10922+ ?. valid-marks [%| 'request marks need unsupported inference']
10923+ =/ fold-open=?
10924+ ?~ fold.u.parts |
10925+ |(?=(^ (itype-vars u.fold.u.parts)) (itype-has-tag u.fold.u.parts))
10926+ ?: fold-open [%| 'request fold needs unsupported inference']
1086610927 ?^ (itype-vars reply-sample.u.parts) [%| 'request payload needs unsupported inference']
1086710928 ?: (itype-has-tag reply-sample.u.parts) [%| 'request payload needs unsupported inference']
1086810929 =/ ask=(unit ischeme) (request-compatible-ask prepared.ev)
@@ -10870,8 +10931,13 @@
1087010931 =/ result=(unit itype) (template-apply u.ask ~[[%var 0]])
1087110932 ?~ result [%| 'request ask needs unsupported inference']
1087210933 =/ callback=itype [%fn reply-sample.u.parts [%var 0]]
10873− =/ scheme=ischeme [[%fn callback u.result] ~[0]]
10874− =/ mark=iapi-mark-type i.marks.support
10934+ =/ sample=itype
10935+ ?^ fold.u.parts [%cell callback u.fold.u.parts]
10936+ ?~ parameters.u.parts callback
10937+ [%cell typ.i.parameters.u.parts callback]
10938+ =/ scheme=ischeme [[%fn sample u.result] ~[0]]
10939+ =/ exports=imold-types
10940+ (turn marks.support |=(m=iapi-mark-type [name.m [typ.m ~]]))
1087510941 =/ prelude=iprelude prepared.ev
1087610942 =/ public=ifile iface.prelude
1087710943 ?~ source.public [%| 'request prelude source needs unsupported inference']
@@ -10881,7 +10947,7 @@
1088110947 ?~ source.i.private [%| 'request internal source needs unsupported inference']
1088210948 =/ req=irequest-evidence
1088310949 [file name.path.p p src u.source.public u.source.i.private values.prelude molds.prelude externals marks]
10884− =/ interface=ifile [~[[name.path.p scheme]] ~[[name.mark [typ.mark ~]]] file ~ ~ ~ `src `[(silt `(list path)`~[file]) ~[req] modules `public values.prelude molds.prelude externals marks]]
10950+ =/ interface=ifile [~[[name.path.p scheme]] exports file ~ ~ ~ `src `[(silt `(list path)`~[file]) ~[req] modules `public values.prelude molds.prelude externals marks]]
1088510951 [%& [support u.parts interface]]
1088610952:: Graph-aware request artifact; resolver use remains type-only.
1088710953++ one-request-interface-graph
@@ -10894,17 +10960,24 @@
1089410960 =/ parsed (parse file src)
1089510961 ?: ?=(%| -.parsed) [%| 'request source does not parse']
1089610962 =/ mod=module p.parsed
10897− ?. ?=([[%mark *] ~] imports.mod) [%| 'request imports need unsupported inference']
10963+ ?. ?=(?([[%mark *] ~] [[%mark *] [%mark *] ~]) imports.mod) [%| 'request imports need unsupported inference']
1089810964 ?. ?=([[%mold *] ~] defs.mod) [%| 'request definitions need unsupported inference']
1089910965 ?. =(%paths name.i.defs.mod) [%| 'request declaration needs unsupported inference']
1090010966 ?. ?=([* ~] paths.ev) [%| 'request count needs unsupported inference']
1090110967 =/ p=iapi-path-typed i.paths.ev
1090210968 =/ parts=(unit iapi-request-parts) (api-request-parts `ev name.path.p)
1090310969 ?~ parts [%| 'request components need unsupported inference']
10904− ?^ parameters.u.parts [%| 'request parameters need unsupported inference']
10905− ?^ fold.u.parts [%| 'request fold needs unsupported inference']
10970+ ?. ?=(?(~ [* ~]) parameters.u.parts) [%| 'request parameters need unsupported inference']
10971+ ?: &(?=(^ fold.u.parts) ?=(^ parameters.u.parts)) [%| 'request parameter and fold need unsupported inference']
1090610972 ?^ scry.path.p [%| 'request scry needs unsupported inference']
10907− ?. ?=([* ~] marks.support) [%| 'request marks need unsupported inference']
10973+ =/ valid-marks=?
10974+ ?~ fold.u.parts ?=([* ~] marks.support)
10975+ ?=([* [* ~]] marks.support)
10976+ ?. valid-marks [%| 'request marks need unsupported inference']
10977+ =/ fold-open=?
10978+ ?~ fold.u.parts |
10979+ |(?=(^ (itype-vars u.fold.u.parts)) (itype-has-tag u.fold.u.parts))
10980+ ?: fold-open [%| 'request fold needs unsupported inference']
1090810981 ?^ (itype-vars reply-sample.u.parts) [%| 'request payload needs unsupported inference']
1090910982 ?: (itype-has-tag reply-sample.u.parts) [%| 'request payload needs unsupported inference']
1091010983 =/ ask=(unit ischeme) (request-compatible-ask prepared.ev)
@@ -10912,8 +10985,13 @@
1091210985 =/ result=(unit itype) (template-apply u.ask ~[[%var 0]])
1091310986 ?~ result [%| 'request ask needs unsupported inference']
1091410987 =/ callback=itype [%fn reply-sample.u.parts [%var 0]]
10915− =/ scheme=ischeme [[%fn callback u.result] ~[0]]
10916− =/ mark=iapi-mark-type i.marks.support
10988+ =/ sample=itype
10989+ ?^ fold.u.parts [%cell callback u.fold.u.parts]
10990+ ?~ parameters.u.parts callback
10991+ [%cell typ.i.parameters.u.parts callback]
10992+ =/ scheme=ischeme [[%fn sample u.result] ~[0]]
10993+ =/ exports=imold-types
10994+ (turn marks.support |=(m=iapi-mark-type [name.m [typ.m ~]]))
1091710995 =/ prelude=iprelude prepared.ev
1091810996 =/ public=ifile iface.prelude
1091910997 ?~ source.public [%| 'request prelude source needs unsupported inference']
@@ -10923,7 +11001,7 @@
1092311001 ?~ source.i.private [%| 'request internal source needs unsupported inference']
1092411002 =/ req=irequest-evidence
1092511003 [file name.path.p p src u.source.public u.source.i.private values.prelude molds.prelude externals marks]
10926− =/ interface=ifile [~[[name.path.p scheme]] ~[[name.mark [typ.mark ~]]] file ~ ~ ~ `src `[(silt `(list path)`~[file]) ~[req] modules `public values.prelude molds.prelude externals marks]]
11004+ =/ interface=ifile [~[[name.path.p scheme]] exports file ~ ~ ~ `src `[(silt `(list path)`~[file]) ~[req] modules `public values.prelude molds.prelude externals marks]]
1092711005 [%& [support u.parts interface]]
1092811006::
1092911007++ api-mark-exports
@@ -10971,6 +11049,132 @@
1097111049 =/ sc=ischeme [[%fn u.mark typ.u.poke] ~]
1097211050 `[~[[n sc]] (api-mark-exports marks marks mark-externals) at ~[[n i.t.t.at n]] ~ ~ ~ ~]
1097311051::
11052+:: Source-proven ordinary helpers validate each selected /sur dependency.
11053+:: Unused or less direct cores remain unsupported by this first interface.
11054+++ api-sur-covered
11055+ |= [surs=(list term) defs=(list def) externals=iexternals all=(list term)]
11056+ ^- ?
11057+ ?~ surs &
11058+ =/ found=(list def)
11059+ %+ skim defs
11060+ |= d=def
11061+ ?. ?=(%mold -.d) |
11062+ ?. ?=(%ref -.shape.d) |
11063+ ?. ?=([@ [@ ~]] names.shape.d) |
11064+ =(i.t.names.shape.d i.surs)
11065+ ?~ found |
11066+ =/ selected=def i.found
11067+ ?. ?=(%mold -.selected) |
11068+ =/ sh=shape shape.selected
11069+ ?. ?=(%ref -.sh) |
11070+ ?. ?=([@ [@ ~]] names.sh) |
11071+ =/ checked=(unit itype) (find-imported-mold i.surs i.names.sh externals all)
11072+ ?~ checked |
11073+ $(surs t.surs)
11074+::
11075++$ imulti-request-gates [values=ienv requests=(list irequest-evidence)]
11076+++ api-request-gates
11077+ |= [paths=(list iapi-path-typed) ev=iapi-path-evidence ask=ischeme file=path src=@t public=@t internal=@t externals=iexternals marks=imark-externals fuel=@ud]
11078+ ^- (unit imulti-request-gates)
11079+ ?~ paths `[~ ~]
11080+ ?: =(0 fuel) ~
11081+ =/ p=iapi-path-typed i.paths
11082+ =/ parts=(unit iapi-request-parts) (api-request-parts `ev name.path.p)
11083+ ?~ parts ~
11084+ ?. ?=(?(~ [* ~]) parameters.u.parts) ~
11085+ ?: &(?=(^ fold.u.parts) ?=(^ parameters.u.parts)) ~
11086+ ?^ scry.path.p ~
11087+ ?^ (itype-vars reply-sample.u.parts) ~
11088+ ?: (itype-has-tag reply-sample.u.parts) ~
11089+ =/ open-fold=?
11090+ ?~ fold.u.parts |
11091+ |(?=(^ (itype-vars u.fold.u.parts)) (itype-has-tag u.fold.u.parts))
11092+ ?: open-fold ~
11093+ =/ result=(unit itype) (template-apply ask ~[[%var 0]])
11094+ ?~ result ~
11095+ =/ callback=itype [%fn reply-sample.u.parts [%var 0]]
11096+ =/ sample=itype
11097+ ?^ fold.u.parts [%cell callback u.fold.u.parts]
11098+ ?~ parameters.u.parts callback
11099+ [%cell typ.i.parameters.u.parts callback]
11100+ =/ scheme=ischeme [[%fn sample u.result] ~[0]]
11101+ =/ prep=iprelude prepared.ev
11102+ =/ req=irequest-evidence
11103+ [file name.path.p p src public internal values.prep molds.prep externals marks]
11104+ =/ rest=(unit imulti-request-gates)
11105+ $(paths t.paths, fuel (dec fuel))
11106+ ?~ rest ~
11107+ `[[[name.path.p scheme] values.u.rest] [req requests.u.rest]]
11108+::
11109+++ api-poke-from-checked
11110+ |= [file=path mod=module marks=imark-externals values=(list term) molds=(list term) prelude=(unit ifile)]
11111+ ^- (unit [values=ienv derived=(list iderived)])
11112+ =/ pokes=(list def)
11113+ (skim defs.mod |=(d=def &(?=(%mold -.d) =(%poke name.d))))
11114+ ?~ pokes `[~ ~]
11115+ ?. ?=([* ~] pokes) ~
11116+ =/ declared=(list term) (collect-mark-imports imports.mod)
11117+ =/ ns=(unit (list term)) (api-poke-mark-names pokes declared)
11118+ ?~ ns ~
11119+ ?. ?=([@ ~] u.ns) ~
11120+ =/ matching=(list import)
11121+ %+ skim imports.mod
11122+ |= i=import
11123+ ?. ?=(%mark -.i) |
11124+ =(mark.i i.u.ns)
11125+ ?. ?=([* ~] matching) ~
11126+ =/ checked=(unit ifile)
11127+ (api-poke-interface file mod(defs pokes, imports matching) marks values molds prelude)
11128+ ?~ checked ~
11129+ `[values.u.checked derived.u.checked]
11130+::
11131+:: The partial interface is published atomically: all paths, ordinary
11132+:: source definitions, declared marks and the poke must check. Unselected
11133+:: /sur bindings, runtime validation and value watchability remain unproved.
11134+++ multi-request-interface-graph
11135+ |= [file=path src=@t externals=iexternals marks=imark-externals modules=jbundle active=(list path) cache=jcache prepared=(unit iprelude)]
11136+ ^- (each [iface=ifile cache=jcache] @t)
11137+ =/ out (api-support-analysis-graph file src externals marks modules active cache prepared)
11138+ ?: ?=(%| -.out) [%| p.out]
11139+ =/ support=iapi-support-evidence p.out
11140+ =/ ev=iapi-path-evidence paths.support
11141+ =/ parsed (parse file src)
11142+ ?: ?=(%| -.parsed) [%| 'request source does not parse']
11143+ =/ mod=module p.parsed
11144+ ?. (levy imports.mod |=(i=import ?=(?(%mark %sur) -.i)))
11145+ [%| 'request imports need unsupported inference']
11146+ =/ surs=(list term) (collect-sur-imports imports.mod)
11147+ ?. (api-sur-covered surs defs.mod externals surs)
11148+ [%| 'API source dependency needs unsupported inference']
11149+ ?~ paths.ev [%| 'request count needs unsupported inference']
11150+ =/ ask=(unit ischeme) (request-compatible-ask prepared.ev)
11151+ ?~ ask [%| 'request ask needs unsupported inference']
11152+ =/ prep=iprelude prepared.ev
11153+ =/ public=ifile iface.prep
11154+ ?~ source.public [%| 'request prelude source needs unsupported inference']
11155+ =/ private=(list iwidth-dependency)
11156+ (skim dependencies.public |=(d=iwidth-dependency =(at.d /lib/jibb/internal/jibb)))
11157+ ?. ?=([* ~] private) [%| 'request internal source needs unsupported inference']
11158+ ?~ source.i.private [%| 'request internal source needs unsupported inference']
11159+ =/ gates=(unit imulti-request-gates)
11160+ (api-request-gates paths.ev ev u.ask file src u.source.public u.source.i.private externals marks 256)
11161+ ?~ gates [%| 'request paths need unsupported inference']
11162+ =/ poke=(unit [values=ienv derived=(list iderived)])
11163+ (api-poke-from-checked file mod marks values.prep molds.prep `public)
11164+ ?~ poke [%| 'API poke needs unsupported inference']
11165+ =/ mark-molds=imold-types
11166+ (turn marks.support |=(m=iapi-mark-type [name.m [typ.m ~]]))
11167+ =/ exports=imold-types (weld mark-molds molds.support)
11168+ =/ names=(list term) (turn exports |=(m=[name=term template=ischeme] name.m))
11169+ ?. (levy names |=(n=term (unique-value-name n names)))
11170+ [%| 'API exported molds need unsupported inference']
11171+ =/ env=ienv (weld values.u.gates (weld values.u.poke values.support))
11172+ =/ graph=irequest-graph
11173+ [(silt `(list path)`~[file]) requests.u.gates modules `public values.prep molds.prep externals marks]
11174+ =/ iface=ifile
11175+ [env exports file derived.u.poke elaborations.support ~ `src `graph]
11176+ [%& [iface dependencies.ev]]
11177+::
1097411178++ infer-jibb-file
1097511179 |= [at=path src=@t externals=iexternals mark-externals=imark-externals modules=jbundle active=(list path) prelude-values=(list term) prelude-molds=(list term) cache=jcache prelude=(unit ifile)]
1097611180 ^- jfile-result
@@ -10986,11 +11190,21 @@
1098611190 [%& [checked [[at checked] cache]]]
1098711191 ?~ prelude [%| (crip "API module needs unsupported inference: {(spud at)}")]
1098811192 =/ prepared=iprelude [u.prelude prelude-values prelude-molds]
10989− =/ request=(each ione-request-interface @t)
10990− (one-request-interface-graph at src externals mark-externals modules active cache `prepared)
10991− ?: ?=(%| -.request) [%| (crip "API module needs unsupported inference: {(spud at)}")]
10992− =/ checked=ifile iface.p.request
10993− [%& [checked [[at checked] dependencies.paths.support.p.request]]]
11193+ =/ narrow=?
11194+ ?. ?=([[%mark *] ~] imports.mod) |
11195+ ?. ?=([[%mold *] ~] defs.mod) |
11196+ ?. =(%paths name.i.defs.mod) |
11197+ !?=(%union -.shape.i.defs.mod)
11198+ ?: narrow
11199+ =/ request=(each ione-request-interface @t)
11200+ (one-request-interface-graph at src externals mark-externals modules active cache `prepared)
11201+ ?: ?=(%| -.request) [%| (crip "API module needs unsupported inference: {(spud at)}")]
11202+ =/ checked=ifile iface.p.request
11203+ [%& [checked [[at checked] dependencies.paths.support.p.request]]]
11204+ =/ multi=(each [iface=ifile cache=jcache] @t)
11205+ (multi-request-interface-graph at src externals mark-externals modules active cache `prepared)
11206+ ?: ?=(%| -.multi) [%| (crip "API module needs unsupported inference: {(spud at)}")]
11207+ [%& [iface.p.multi [[at iface.p.multi] cache.p.multi]]]
1099411208 ?. (module-scope-valid mod prelude-values prelude-molds)
1099511209 ?: (pending-declared-poke at mod prelude-values prelude-molds)
1099611210 [%| (crip "module kind or scope needs unsupported inference: {(spud at)}")]
tests/lib/jibb-closed-gate-guards.hoonnew file+60
@@ -0,0 +1,60 @@
1+:: Closed-gate sample labels must not erase nested faces or uncertain origins.
2+/+ jibb
3+|%
4+++ closed-gate-guard-errors
5+ ^- (list tape)
6+ =/ t=itype:jibb [%atom %t]
7+ =/ p=itype:jibb [%atom %p]
8+ =/ n=itype:jibb [%atom %ud]
9+ =/ tag=itype:jibb [%const [%term %tas %delta '%delta']]
10+ =/ payload=itype:jibb [%cell tag n]
11+ =/ bare=itype:jibb [%cell payload t]
12+ =/ named=itype:jibb [%cell [%face %diff payload] [%face %state t]]
13+ =/ differently-named=itype:jibb [%cell [%face %other payload] [%face %state t]]
14+ =/ face-one=itype:jibb [%face %first t]
15+ =/ face-two=itype:jibb [%face %second t]
16+ =/ inner-one=itype:jibb [%cell face-one n]
17+ =/ inner-two=itype:jibb [%cell face-two n]
18+ =/ nested-a=itype:jibb [%cell [%face %diff inner-one] [%face %state t]]
19+ =/ nested-b=itype:jibb [%cell inner-two t]
20+ =/ tail-a=itype:jibb [%cell payload [%face %rest [%cell t n]]]
21+ =/ tail-b=itype:jibb [%cell payload [%cell t n]]
22+ =/ samples-mismatch=itype:jibb [%cell payload p]
23+ =/ cases=(list [label=@t actual=itype:jibb expected=itype:jibb sub=isub:jibb want=?])
24+ :~ ['named fold to bare' [%fn named t] [%fn bare t] ~ &]
25+ ['same named gate' [%fn named t] [%fn named t] ~ &]
26+ ['different slot names' [%fn named t] [%fn differently-named t] ~ |]
27+ ['different nested names' [%fn nested-a t] [%fn nested-b t] ~ |]
28+ ['faced tail versus cell' [%fn tail-a t] [%fn tail-b t] ~ |]
29+ ['different result face' [%fn bare face-one] [%fn bare face-two] ~ |]
30+ ['faced result versus bare' [%fn bare face-one] [%fn bare t] ~ |]
31+ ['wrong sample aura' [%fn named t] [%fn samples-mismatch t] ~ |]
32+ ['wrong result aura' [%fn named p] [%fn bare t] ~ |]
33+ ['open sample' [%fn [%var 0] t] [%fn bare t] ~ &]
34+ ['variable-bound sample' [%fn [%var 0] t] [%fn bare t] ~[[0 named]] |]
35+ ['variable-bound actual function' [%var 0] [%fn bare t] ~[[0 [%fn named t]]] |]
36+ ['variable-bound expected function' [%fn bare t] [%var 0] ~[[0 [%fn named t]]] |]
37+ ==
38+ =/ outcomes=(list @t)
39+ %+ turn cases
40+ |= c=[label=@t actual=itype:jibb expected=itype:jibb sub=isub:jibb want=?]
41+ =/ fit=ifit:jibb (itype-fit:jibb actual.c expected.c sub.c 1)
42+ ?: =(want.c ?=(%ok -.fit)) ''
43+ (cat 3 label.c ': unexpected fit')
44+ =/ bad=(list @t) (skim outcomes |=(s=@t !=(s '')))
45+ =/ build
46+ |= depth=@ud
47+ ^- [a=itype:jibb b=itype:jibb]
48+ =/ a=itype:jibb [%face %inside t]
49+ =/ b=itype:jibb t
50+ |-
51+ ?: =(depth 0) [a b]
52+ $(depth (dec depth), a [%fn [%cell a t] t], b [%fn [%cell b t] t])
53+ =/ deep (build 12)
54+ =/ deep-fit=ifit:jibb (itype-fit:jibb a.deep b.deep ~ 0)
55+ =? bad !?=(%unknown -.deep-fit) ['nested gate face uncertainty changed' bad]
56+ (turn bad trip)
57+++ test-closed-gate-guards
58+ ^- tang
59+ (turn closed-gate-guard-errors |=(reason=tape leaf+reason))
60+--
tests/lib/jibb-todo-api.hoonnew file+85
@@ -0,0 +1,85 @@
1+:: The exact todo API reaches partial client inference through real /sur and mark readers.
2+/+ jibb, jibb-import
3+/* prelude %jibb /lib/jibb/prelude/jibb
4+/* internal %jibb /lib/jibb/internal/jibb
5+/* sur %txt /tests/type-api-paths/todo-sur/txt
6+/* action %txt /tests/type-api-paths/todo-action/txt
7+/* todos %txt /tests/type-api-paths/todos/txt
8+/* update %txt /tests/type-api-paths/todo-update/txt
9+/* item %txt /tests/type-api-paths/todo-item/txt
10+/* api %txt /tests/type-api-paths/api/txt
11+/* source %txt /tests/type-todo-api/source/txt
12+/* negative %txt /tests/type-todo-api/negative/txt
13+/* unsupported %txt /tests/type-todo-api/unsupported/txt
14+/* expected %txt /tests/type-todo-api/expected/txt
15+/* negative-expected %txt /tests/type-todo-api/negative-expected/txt
16+|%
17+++ text
18+ |= lines=wain
19+ ^- @t
20+ (rap 3 (turn lines |=(line=@t (cat 3 line '\0a'))))
21+++ todo-run
22+ |= [prelude=@t internal=@t sur=@t action=@t todos=@t update=@t item=@t api=@t source=@t negative=@t unsupported=@t]
23+ ^- [report=@t bad-report=@t guards=@t]
24+ =/ prepared=(unit iprelude:jibb) (prepare-prelude-with-internal:jibb prelude internal)
25+ ?~ prepared ['prelude unavailable' 'prelude unavailable' 'prelude unavailable']
26+ =/ todo=type -:(ride -:!>(..zuse) sur)
27+ =/ subject=type [%cell [%face %todo todo] -:!>(..zuse)]
28+ =/ ma=* (read-mark:jibb-import %todo-action -:(ride subject action))
29+ =/ mt=* (read-mark:jibb-import %todos -:(ride subject todos))
30+ =/ mu=* (read-mark:jibb-import %todo-update -:(ride subject update))
31+ =/ mi=* (read-mark:jibb-import %todo-item -:(ride subject item))
32+ =/ mark-inputs=imark-externals:jibb ~[[%todo-action ma] [%todos mt] [%todo-update mu] [%todo-item mi]]
33+ =/ item-input=* (read-named:jibb-import %todo %item todo)
34+ =/ inputs=iexternals:jibb ~[[%todo %item item-input]]
35+ =/ modules=jbundle:jibb ~[[/ui/api/todo/jibb api]]
36+ =/ report=@t (golden-types-with-prepared-prelude:jibb /tests/type-todo-api/source/jibb source inputs mark-inputs modules prepared)
37+ =/ bad-report=@t (golden-types-with-prepared-prelude:jibb /tests/type-todo-api/negative/jibb negative inputs mark-inputs modules prepared)
38+ =/ guards=@t
39+ =/ out (infer-jibb-file:jibb /ui/api/todo/jibb api inputs mark-inputs ~ ~[/ui/api/todo/jibb] values.u.prepared molds.u.prepared ~ `iface.u.prepared)
40+ ?: ?=(%| -.out) (cat 3 'actual API failed: ' p.out)
41+ =/ iface=ifile:jibb iface.p.out
42+ ?. =(source.iface `api) 'actual API source changed'
43+ =/ names=(list term) (turn values.iface |=(x=[name=term scheme=ischeme:jibb] name.x))
44+ =/ required-values=(list term) ~[%todo-action %todos %changes %todo-by-id]
45+ ?. (levy required-values |=(x=term (lien names |=(y=term =(x y))))) 'derived value missing'
46+ =/ mnames=(list term) (turn molds.iface |=(x=[name=term template=ischeme:jibb] name.x))
47+ =/ required-molds=(list term) ~[%item %todo-action %todos %todo-update %todo-item]
48+ ?. (levy required-molds |=(x=term (lien mnames |=(y=term =(x y))))) 'expected mold export missing'
49+ ?: (lien mnames |=(x=term =(%todo x))) '/sur import face was re-exported'
50+ ?~ graph.iface 'API certificate missing'
51+ ?. =((lent requests.u.graph.iface) 3) 'request evidence count wrong'
52+ =/ missing-mark=imark-externals:jibb (skim mark-inputs |=(x=[mark=term result=*] !=(%todo-update mark.x)))
53+ =/ duplicate-mark=imark-externals:jibb [[%todo-action ma] mark-inputs]
54+ =/ duplicate-sur=iexternals:jibb [[%todo %item item-input] inputs]
55+ =/ cases=(list [label=@t src=@t externals=iexternals:jibb marks=imark-externals:jibb])
56+ :~ ['missing selected sur mold' api ~ mark-inputs]
57+ ['duplicate selected sur mold' api duplicate-sur mark-inputs]
58+ ['missing mark' api inputs missing-mark]
59+ ['duplicate mark' api inputs duplicate-mark]
60+ ['unsupported request sibling' unsupported inputs mark-inputs]
61+ ['unrelated invalid mold' (cat 3 api '\0a+$ invalid missing\0a') inputs mark-inputs]
62+ ['unrelated invalid value' (cat 3 api '\0a++ invalid missing\0a') inputs mark-inputs]
63+ ['private import' (cat 3 '/= secret /lib/jibb/internal\0a' api) inputs mark-inputs]
64+ ==
65+ =/ failures=(list @t)
66+ %+ turn cases
67+ |= c=[label=@t src=@t externals=iexternals:jibb marks=imark-externals:jibb]
68+ =/ bad (infer-jibb-file:jibb /ui/api/todo/jibb src.c externals.c marks.c ~ ~[/ui/api/todo/jibb] values.u.prepared molds.u.prepared ~ `iface.u.prepared)
69+ ?: ?=(%| -.bad) ''
70+ (cat 3 label.c ': unexpectedly published')
71+ =/ bad=(list @t) (skim failures |=(x=@t !=(x '')))
72+ ?~ bad 'real todo interface and atomic guards passed'
73+ (of-wain:format bad)
74+ [report bad-report guards]
75+++ test-todo-api
76+ ^- tang
77+ =/ out (todo-run prelude internal (text sur) (text action) (text todos) (text update) (text item) (text api) (text source) (text negative) (text unsupported))
78+ ?. =((to-wain:format (cat 3 report.out '\0a')) expected)
79+ ~[leaf+"todo API report differs from expected"]
80+ ?. =((to-wain:format (cat 3 bad-report.out '\0a')) negative-expected)
81+ ~[leaf+"todo API negative report differs from expected"]
82+ ?. =(guards.out 'real todo interface and atomic guards passed')
83+ ~[leaf+(trip guards.out)]
84+ ~
85+--
tests/type-todo-api/about.txtnew file+1
@@ -0,0 +1 @@
1+The exact my-app todo API is checked with its actual /sur and mark reader fixtures. The eval runner verifies that the API fixture matches the application source. Positive and negative reports cover request callback polymorphism, the named one-diff fold, typed path arguments, poke and exported molds. The interface guards check source and request evidence and reject missing or duplicate inputs, unsupported sibling declarations and private imports. Reports are partial inference, not a complete module check or runtime/watchability proof.
tests/type-todo-api/expected.txtnew file+14
@@ -0,0 +1,14 @@
1+partial inference; module not checked
2+++ fold: $-([d=[%todo-update ([%added item=[id=@ud [done=? text=@t]]] [%toggled id=@ud])] s=(list [id=@ud [done=? text=@t]])] (list [id=@ud [done=? text=@t]]))
3+++ todos-cord: (ask @t)
4+++ todos-ship: (ask @p)
5+++ by-id-cord: (ask @t)
6+++ by-id-ship: (ask @p)
7+++ changes-cord: (ask @t)
8+++ changes-ship: (ask @p)
9+++ action: poke
10+++ item-id: $-([id=@ud [done=? text=@t]] @ud)
11+++ action-id: $-(([%add text=@t] [%toggle id=@ud]) ([%add text=@t] [%toggle id=@ud]))
12+++ todos-id: $-((list [id=@ud [done=? text=@t]]) (list [id=@ud [done=? text=@t]]))
13+++ update-id: $-(([%added item=[id=@ud [done=? text=@t]]] [%toggled id=@ud]) ([%added item=[id=@ud [done=? text=@t]]] [%toggled id=@ud]))
14+++ item-mark-id: $-([id=@ud [done=? text=@t]] [id=@ud [done=? text=@t]])
tests/type-todo-api/negative-expected.txtnew file+17
@@ -0,0 +1,17 @@
1+partial inference; module not checked
2+++ no-sur-export: sample mold is not supported by partial inference
3+++ bad-param: call's sample and argument types do not unify
4+/tests/type-todo-api/negative.jibb:3:15: called gate
5+++ bad-param (todo-by-id.api ~zod |=(x=todo-item.api "ok"))
6+ ^
7+/tests/type-todo-api/negative.jibb:3:30: argument has type @p
8+++ bad-param (todo-by-id.api ~zod |=(x=todo-item.api "ok"))
9+ ^
10+++ missing-fold: call constraint is not supported by partial inference
11+++ bad-action: call's sample and argument types do not unify
12+/tests/type-todo-api/negative.jibb:5:16: called gate
13+++ bad-action (todo-action.api [%typo text="hello"])
14+ ^
15+/tests/type-todo-api/negative.jibb:5:32: argument
16+++ bad-action (todo-action.api [%typo text="hello"])
17+ ^
tests/type-todo-api/negative.txtnew file+5
@@ -0,0 +1,5 @@
1+/= api /ui/api/todo
2+++ no-sur-export |= x=todo.api x
3+++ bad-param (todo-by-id.api ~zod |=(x=todo-item.api "ok"))
4+++ missing-fold (changes.api |=(xs=todos.api "ok"))
5+++ bad-action (todo-action.api [%typo text="hello"])
tests/type-todo-api/source.txtnew file+14
@@ -0,0 +1,14 @@
1+/= api /ui/api/todo
2+++ fold |= [d=[%todo-update todo-update.api] s=todos.api] s
3+++ todos-cord (todos.api |=(xs=todos.api "ok"))
4+++ todos-ship (todos.api |=(xs=todos.api ~zod))
5+++ by-id-cord (todo-by-id.api 1 |=(x=todo-item.api "ok"))
6+++ by-id-ship (todo-by-id.api 2 |=(x=todo-item.api ~zod))
7+++ changes-cord (changes.api |=(xs=todos.api "ok") fold)
8+++ changes-ship (changes.api |=(xs=todos.api ~zod) fold)
9+++ action (todo-action.api [%add text="hello"])
10+++ item-id |= x=item.api id.x
11+++ action-id |= x=todo-action.api x
12+++ todos-id |= x=todos.api x
13+++ update-id |= x=todo-update.api x
14+++ item-mark-id |= x=todo-item.api x
tests/type-todo-api/unsupported.txtnew file+13
@@ -0,0 +1,13 @@
1+:: /ui/api/todo.jibb
2+/- todo
3+/% %todo-action
4+/% %todos
5+/% %todo-update
6+/% %todo-item
7+
8++$ item item.todo
9++$ poke todo-action
10++$ paths
11+ [%todos /todos todos]
12+ [%changes /changes (facts todos todo-update todo-item)]
13+ [%todo-by-id /todo/[id=@ud] todo-item]
tools/testdata/ship-selftest+3−1
@@ -310,7 +310,9 @@ def generator_checks(tmp):
310310 'tests/lib/jibb-generic-alias.hoon',
311311 'tests/lib/jibb-api-evidence.hoon',
312312 'tests/lib/jibb-ask-guards.hoon',
313− 'tests/lib/jibb-request-guards.hoon'}
313+ 'tests/lib/jibb-request-guards.hoon',
314+ 'tests/lib/jibb-closed-gate-guards.hoon',
315+ 'tests/lib/jibb-todo-api.hoon'}
314316 <= normal_tests, sorted(normal_tests))
315317 for label, kwargs in (('custom library', {'lib': STUB}),
316318 ('custom goldens', {'golden': CASES}),
tools/type-closed-gate-guards-testnew file+29
@@ -0,0 +1,29 @@
1+#!/usr/bin/env python3
2+"""Run the shared closed gate fit guards under eval."""
3+from pathlib import Path
4+import argparse
5+import subprocess
6+import sys
7+import tempfile
8+
9+root = Path(__file__).resolve().parent.parent
10+parser = argparse.ArgumentParser(description=__doc__)
11+parser.add_argument('--lib', default=str(root / 'jibb-dev/lib/jibb.hoon'))
12+args = parser.parse_args()
13+tests = (root / 'tests/lib/jibb-closed-gate-guards.hoon').read_text()
14+tests = tests[tests.index('\n|%\n') + 1:]
15+expression = ('=/ jibb .\n=/ suite\n' + tests +
16+ '\n=/ failures=(list tape) closed-gate-guard-errors:suite\n^- @t\n' +
17+ "?~ failures 'closed gate guards passed'\n" +
18+ '(of-wain:format (turn failures crip))\n')
19+with tempfile.TemporaryDirectory(prefix='jibb-positional-guards-') as tmp:
20+ program = Path(tmp) / 'guards.hoon'
21+ program.write_text(expression)
22+ result = subprocess.run([str(root / 'tools/hoon'), '--lib', args.lib,
23+ '--raw', '-f', str(program)], stdout=subprocess.PIPE)
24+if result.returncode:
25+ sys.exit(result.returncode)
26+if result.stdout != b'closed gate guards passed\n':
27+ sys.stderr.buffer.write(result.stdout)
28+ sys.exit(1)
29+print('closed gate fit guards passed')
tools/type-todo-api-testnew file+62
@@ -0,0 +1,62 @@
1+#!/usr/bin/env python3
2+"""Check the exact todo API through the real /sur and mark readers."""
3+from pathlib import Path
4+import argparse
5+import subprocess
6+import sys
7+import tempfile
8+
9+root = Path(__file__).resolve().parent.parent
10+parser = argparse.ArgumentParser(description=__doc__)
11+parser.add_argument('--lib', default=str(root / 'jibb-dev/lib/jibb.hoon'))
12+args = parser.parse_args()
13+fixture = root / 'tests/type-api-paths/api.txt'
14+actual = root / 'my-app/ui/api/todo.jibb'
15+if fixture.read_bytes() != actual.read_bytes():
16+ sys.exit('todo API fixture differs from my-app/ui/api/todo.jibb')
17+reader = (root / 'jibb-dev/lib/jibb-import.hoon').read_text()
18+tests = (root / 'tests/lib/jibb-todo-api.hoon').read_text()
19+tests = tests[tests.index('\n|%\n') + 1:]
20+names = ('sur', 'action', 'todos', 'update', 'item', 'api', 'source',
21+ 'negative', 'unsupported', 'expected', 'negative-expected')
22+program = ('=/ jibb .\n=/ jibb-import\n' + reader + '\n' +
23+ ''.join('=/ ' + name + '=wain (to-wain:format ' + name + '-text)\n'
24+ for name in names) +
25+ '=/ suite\n' + tests +
26+ '\n=/ out (todo-run:suite prelude internal sur-text action-text todos-text update-text item-text api-text source-text negative-text unsupported-text)\n'
27+ "^- @t\n(of-wain:format ~[report.out '===NEGATIVE===' bad-report.out '===GUARD===' guards.out])\n")
28+fixtures = {'sur': 'tests/type-api-paths/todo-sur.txt',
29+ 'action': 'tests/type-api-paths/todo-action.txt',
30+ 'todos': 'tests/type-api-paths/todos.txt',
31+ 'update': 'tests/type-api-paths/todo-update.txt',
32+ 'item': 'tests/type-api-paths/todo-item.txt',
33+ 'api': 'tests/type-api-paths/api.txt',
34+ **{name: 'tests/type-todo-api/' + name + '.txt'
35+ for name in ('source', 'negative', 'unsupported', 'expected', 'negative-expected')}}
36+with tempfile.TemporaryDirectory(prefix='jibb-todo-api-') as tmp:
37+ path = Path(tmp) / 'todo.hoon'
38+ path.write_text(program)
39+ command = [str(root / 'tools/hoon'), '--lib', args.lib, '--raw', '-f', str(path),
40+ '--cord', 'prelude=' + str(root / 'jibb-dev/lib/jibb/prelude.jibb'),
41+ '--cord', 'internal=' + str(root / 'jibb-dev/lib/jibb/internal.jibb')]
42+ for name, filename in fixtures.items():
43+ command += ['--cord', name + '-text=' + str(root / filename)]
44+ result = subprocess.run(command, stdout=subprocess.PIPE)
45+if result.returncode:
46+ sys.exit(result.returncode)
47+try:
48+ positive, rest = result.stdout.split(b'\n===NEGATIVE===\n', 1)
49+ negative, guard = rest.split(b'\n===GUARD===\n', 1)
50+except ValueError:
51+ sys.stderr.buffer.write(result.stdout)
52+ sys.exit('todo API test output was malformed')
53+if positive + b'\n' != (root / 'tests/type-todo-api/expected.txt').read_bytes():
54+ sys.stderr.buffer.write(positive + b'\n')
55+ sys.exit('todo API positive report differs from expected')
56+if negative + b'\n' != (root / 'tests/type-todo-api/negative-expected.txt').read_bytes():
57+ sys.stderr.buffer.write(negative + b'\n')
58+ sys.exit('todo API negative report differs from expected')
59+if guard != b'real todo interface and atomic guards passed\n':
60+ sys.stderr.buffer.write(guard)
61+ sys.exit('todo API interface guards failed')
62+print('real todo API reader and partial checker passed')