on master help sign in
Retain source-backed API path evidence

Preserve the prepared scope, reader results and dependency sources alongside declaration analysis, and classify a named declaration without claiming request-value provenance. Exercise synthetic and real todo evidence under eval and on the ship.

commit445a0a1023adc3df5bee57fae8d46929c528eb34 authoriko <mail@iko.soy> dateSat 31 Oct 2026, 08:13 (13 hours ago) parent640a442
9 files changed+228−3
docs/compiler.md+2 docs/development.md+2 docs/status.md+6−2 jibb-dev/lib/jibb.hoon+38 tests/lib/jibb-api-evidence.hoon+134 tests/type-api-evidence/about.txt+3 tests/type-api-evidence/expected.txt+1 tools/testdata/ship-selftest+2−1 tools/type-api-evidence-test+40
docs/compiler.md+2
@@ -96,6 +96,8 @@ The partial report can diagnose certain non-gate value dependency cycles, includ
9696
9797The source-backed `+api-path-analysis-source` is a separate partial analysis, not an API check: it resolves reachable Jibb imports and declared mark graphs and returns path and scry segments, full and diff mark identities and their types. It also checks derived-name collisions. Full and diff payloads must be direct declared mark names; typed scry parameters must be a same-name, same-aura subset of the watch parameters, and unknown or duplicate segment binders fail closed. It creates no request gates. The caller must pass a trusted prepared prelude and unchanged source-aware reader results.
9898
99+`+api-path-evidence-source` retains that analysis with the API source, complete prepared prelude (including reserved names), supplied reader results and resolved Jibb dependency cache. `+api-path-watchability` classifies a unique declaration as watchable or not watchable, or reports unknown when evidence is absent or ambiguous. The artifact and reader results are trusted host inputs; the host must bind them to the source versions used later. This is declaration evidence, not proof about an arbitrary request value, a checked API module, a runtime validator or an island watch.
100+
99101`+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.
100102
101103The supplied prelude is parsed into a **partial interface**: all names actually declared in the source are reserved, while only the molds and values this fragment can infer are available. These include the validated standard `list` mold, `unit`, the doc-exact generic `cmd` union, `same`, `fall`, `need`, `bond`, `lure`, `weld`, `snoc`, `turn`, `flop`, `rear`, `drop`, `skim`, `skip`, `murn`, `join`, `snip`, `levy`, `lien`, `cork`, `corl`, `cury` and `curr`; unsupported entries remain unavailable. The validated `list` is injected while the prelude’s gates are inferred; `weld` and `turn` use ordinary explicit result casts. An absent or altered `list` is not injected, and an altered declaration cannot leak through a direct or transitive mold alias. The same validated template is available when prelude mold aliases are prepared, so supported direct and transitive aliases can use it. A named prelude union keeps the fixed `/lib/jibb/prelude/jibb` origin in every module, and its name remains unqualified when a library exports a type containing it. The prelude inventory is incomplete, so reserving the names in this source does not establish complete shadowing coverage. The source-backed internal prelude defines `poke`, which the public prelude re-exports as an ordinary transparent type. A dedicated trusted internal-prelude preparation path also supplies private comparison and arithmetic schemes for the Jibb-written `gth`, `add` and `dec` wrappers. Ordinary module resolution rejects that primitive path even if a caller supplies a spoofed root or bundle. The low-level prepared interface is still trusted host input; the host must select the vendored sources and canonicalize paths. Primitive execution and shape-based jet recognition are not implemented. The source-backed `add` and `dec` wrappers use private arithmetic interfaces whose shared atom variable excludes loobeans; the partial checker preserves that constraint through supported inference and prints `where A is an atom other than ?`. Length and primitive execution remain unavailable. The partial checker resolves its supported source shape, preserves its public name, and rejects ordinary imports of the private internal path. An imported API with one mark and a matching direct `+$ poke` declaration can export a derived constructor and retain agent/mark metadata when the source representation has the supported agent case. Other API shapes, complete private module checking, root API and kind checking, stores, `ask`, `pure`, lowering and runtime effects remain unfinished. A possible store or root API declaring `+$ poke` reports that kind or scope needs unsupported inference; definite duplicate declarations and unrelated shadowing still report conflicts. Prepared interfaces and caches are trusted caller inputs; the future host and linker must ensure the private source validated during preparation is the same version used to lower derived constructors. A `*` payload may contain gates, so the fragment treats it conservatively; full gate-free checks are unfinished. The shared 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.
docs/development.md+2
@@ -66,6 +66,7 @@ git clone https://github.com/urbit/tools /root/src/tools
6666| `tools/type-mark-test` | Checks the partial checker’s mark import boundary against `tests/type-mark/expected.txt` under eval |
6767| `tools/type-poke-test` | Checks source-backed prelude and poke-only API reports against `tests/type-poke/expected.txt` under eval |
6868| `tools/type-api-paths-test` | Checks partial API path analysis with real todo mark samples against `tests/type-api-paths/expected.txt` under eval |
69+| `tools/type-api-evidence-test` | Runs source, scope, reader-result and declaration-watchability evidence guards under eval and on the ship |
6970| `tools/type-hole-guards-test` | Runs the shared hole-defaulting and API interface guards under eval; the same checks run on the ship |
7071| `tools/type-list-alias-test` | Runs the shared prelude list-alias and provenance guards under eval and on the ship |
7172| `tools/type-generic-alias-test` | Runs public/private generic union alias and provenance guards under eval and on the ship |
@@ -95,6 +96,7 @@ tools/type-import-diagnostic-test # direct structured reader diagnostics match
9596tools/type-mark-test # mark/checker bridge golden report matches
9697tools/type-poke-test # source-backed poke/API bridge report matches
9798tools/type-api-paths-test # partial API path analysis report matches
99+tools/type-api-evidence-test # API declaration evidence guards pass
98100tools/type-hole-guards-test # hole defaulting and API guards pass
99101tools/type-list-alias-test # prelude list-alias guards pass
100102tools/type-generic-alias-test # generic union alias guards pass
docs/status.md+6−2
@@ -25,6 +25,8 @@ 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. 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. `ask` is still unfinished.
2727
28+The API path analyzer can retain its source, complete prepared prelude, reader results and resolved dependencies as evidence, and classify a unique declaration as watchable, not watchable or unknown. It does not yet prove the provenance of request values or check island subscriptions. Prepared artifacts and reader results remain trusted host inputs.
29+
2830The 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 create requests or check an API module. Complete private module checking, other API shapes, request gates, root API and kind checking, stores, `ask`, `pure`, lowering 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 newly agreed shared-type `clap`, parsed but still awaiting checker and backend validation, and [prelude-inventory.md](prelude-inventory.md) is a source audit, not the complete inventory or signatures. The milestone remains unfinished.
2931
3032The 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. Preparation rejects empty, malformed, conflicting or import-bearing source; the prepared value is trusted and must come unchanged from `+prepare-prelude`. 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.
@@ -46,7 +48,7 @@ What the plan asks for, and where it is:
4648Besides the plan's list:
4749
4850- 636 golden cases: 416 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.
49−- The 636-case corpus passed under `urbit eval` in 85.6 s while other checks were running. The fake ship passed all 653 tests, including the importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, projection, arithmetic, generic-alias and positional-face guards, with 55.4 s for sync and 67.3 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.
51+- The 636-case corpus passed under `urbit eval` in 84.9 s while other checks were running. The fake ship passed all 655 tests, including the importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, projection, arithmetic, generic-alias, positional-face and API-evidence guards, with 55.4 s for sync and 66.3 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.
5052- `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.
5153- 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.
5254- 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).
@@ -132,7 +134,8 @@ Besides the plan's list:
132134| cf17925 | 2026-10-31 | Infer supported single-field projections |
133135| 1a891e2 | 2026-10-31 | Infer source-backed addition and decrement without loobean operands; record the watchability decision |
134136| 81af5fd | 2026-10-31 | Preserve public names of identity-generic union aliases and record the `like` decision |
135−| This change | 2026-10-31 | Add the source-backed `like` result mapper and infer supported positional calls |
137+| 640a442 | 2026-10-31 | Add the source-backed `like` result mapper and infer supported positional calls |
138+| This change | 2026-10-31 | Retain source-backed API path evidence and classify declarations |
136139
137140## Checking it
138141
@@ -145,6 +148,7 @@ tools/type-import-test # the importer/checker bridge fixture
145148tools/type-tlon-test # the real Tlon reader/checker fixture
146149tools/type-mark-test # the mark/checker bridge fixture
147150tools/type-poke-test # the source-backed poke/API fixture
151+tools/type-api-evidence-test # API declaration evidence guards
148152tools/type-api-paths-test # partial API path metadata fixture
149153tools/type-hole-guards-test # hole defaulting and API interface guards
150154tools/type-list-alias-test # prelude list-alias guards
jibb-dev/lib/jibb.hoon+38
@@ -10469,6 +10469,44 @@
1046910469 ?~ paths [%| 'API path declaration needs unsupported analysis']
1047010470 [%& u.paths]
1047110471::
10472+:: Analysis evidence retains the API source, the prepared interface, the
10473+:: caller's reader results and the resolved Jibb dependency cache alongside
10474+:: the typed path declarations. Reader results and prepared interfaces are
10475+:: trusted inputs: this does not verify their underlying Hoon source/version,
10476+:: create requests or check the provenance of arbitrary values.
10477++$ iapi-path-evidence
10478+ [at=path source=@t prepared=iprelude externals=iexternals marks=imark-externals dependencies=jcache paths=(list iapi-path-typed)]
10479+++ api-path-evidence-source
10480+ |= [file=path src=@t externals=iexternals marks=imark-externals modules=jbundle prepared=(unit iprelude)]
10481+ ^- (each iapi-path-evidence @t)
10482+ ?. ?=([%ui %api @ %jibb ~] file) [%| 'API path evidence requires /ui/api/<agent>.jibb']
10483+ =/ parsed (parse file src)
10484+ ?: ?=(%| -.parsed) [%| 'API path evidence source does not parse']
10485+ =/ duplicate=(unit idiag) (duplicate-defs defs.p.parsed)
10486+ ?^ duplicate [%| (show-duplicate-fields file src u.duplicate)]
10487+ ?~ prepared [%| 'API path evidence needs the prepared prelude']
10488+ =/ public=ifile iface.u.prepared
10489+ =/ deps=jimports-result
10490+ (resolve-jibb-imports imports.p.parsed externals marks modules ~[file] values.u.prepared molds.u.prepared ~ `public)
10491+ ?: ?=(%| -.deps) [%| p.deps]
10492+ =/ paths=(unit (list iapi-path-typed))
10493+ (api-path-analysis p.parsed marks values.u.prepared molds.u.prepared)
10494+ ?~ paths [%| 'API path declaration needs unsupported analysis']
10495+ [%& [file src u.prepared externals marks cache.p.deps u.paths]]
10496+::
10497+:: This query classifies a unique path declaration only. Missing evidence
10498+:: or a nonunique name is unknown; it proves nothing about an ask value.
10499+:: Like prepared interfaces, the supplied evidence is a trusted host input.
10500+++ api-path-watchability
10501+ |= [evidence=(unit iapi-path-evidence) case-name=term]
10502+ ^- ?(%watchable %not-watchable %unknown)
10503+ ?~ evidence %unknown
10504+ =/ found=(list iapi-path-typed)
10505+ (skim paths.u.evidence |=(p=iapi-path-typed =(name.path.p case-name)))
10506+ ?. ?=([* ~] found) %unknown
10507+ ?~ scry.path.i.found %watchable
10508+ %not-watchable
10509+::
1047210510++ api-mark-exports
1047310511 |= [marks=(list term) all=(list term) externals=imark-externals]
1047410512 ^- imold-types
tests/lib/jibb-api-evidence.hoonnew file+134
@@ -0,0 +1,134 @@
1+:: Path-declaration evidence retains sources, scope and reader-derived graphs.
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/scry/txt
11+|%
12+++ text
13+ |= lines=wain
14+ ^- @t
15+ (rap 3 (turn lines |=(line=@t (cat 3 line '\0a'))))
16+++ synthetic-evidence-status
17+ ^- @t
18+ :: A deliberately partial prelude preserves names even when inference fails.
19+ =/ public=@t '++ forbidden (missing "x")\0a+$ hidden (missing @t)\0a'
20+ =/ prepared=(unit iprelude:jibb) (prepare-prelude:jibb public)
21+ ?~ prepared 'partial prelude did not prepare'
22+ ?. (lien values.u.prepared |=(x=term =(%forbidden x))) 'unavailable value not reserved'
23+ ?. ?=(~ (ienv-lookup:jibb %forbidden values.iface.u.prepared)) 'forbidden value unexpectedly inferred'
24+ ?. (lien molds.u.prepared |=(x=term =(%hidden x))) 'unavailable mold not reserved'
25+ ?. ?=(~ (find-jibb-mold:jibb %hidden iface.u.prepared)) 'hidden mold unexpectedly inferred'
26+ =/ api=@t '/= dep /ui/dep\0a/% %todo\0a+$ paths [%items /items todo]\0a'
27+ =/ dep=@t '/= leaf /ui/leaf\0a++ value thing.leaf\0a'
28+ =/ leaf=@t '++ thing "x"\0a'
29+ =/ marks=imark-externals:jibb ~[[%todo [%& [%atom %t ~]]]]
30+ =/ externals=iexternals:jibb ~[[%unused %other [%& [%atom %p ~]]]]
31+ =/ modules=jbundle:jibb ~[[/ui/dep/jibb dep] [/ui/leaf/jibb leaf]]
32+ =/ result (api-path-evidence-source:jibb /ui/api/demo/jibb api externals marks modules prepared)
33+ ?: ?=(%| -.result) p.result
34+ =/ evidence=iapi-path-evidence:jibb p.result
35+ ?. =(at.evidence /ui/api/demo/jibb) 'API path not retained'
36+ ?. =(source.evidence api) 'API source not retained'
37+ ?. =(prepared.evidence u.prepared) 'complete prepared prelude not retained'
38+ ?. =(source.iface.prepared.evidence `public) 'prelude source not retained'
39+ ?. =(externals.evidence externals) 'external reader graph not retained'
40+ ?. =(marks.evidence marks) 'mark reader graph not retained'
41+ =/ d=(unit ifile:jibb) (find-jibb-cache:jibb /ui/dep/jibb dependencies.evidence)
42+ ?~ d 'dependency not retained'
43+ ?. =(source.u.d `dep) 'dependency source not retained'
44+ =/ l=(unit ifile:jibb) (find-jibb-cache:jibb /ui/leaf/jibb dependencies.evidence)
45+ ?~ l 'transitive dependency not retained'
46+ ?. =(source.u.l `leaf) 'transitive dependency source not retained'
47+ ?. ?=([* ~] paths.evidence) 'incorrect path count'
48+ ?. =(full.path.i.paths.evidence %todo) 'full mark name not retained'
49+ ?. =([%atom %t] full.i.paths.evidence) 'full mark type not retained'
50+ ?. =(%watchable (api-path-watchability:jibb `evidence %items)) 'normal path not watchable'
51+ ?. =(%unknown (api-path-watchability:jibb `evidence %missing)) 'missing path not unknown'
52+ ?. =(%unknown (api-path-watchability:jibb ~ %items)) 'missing evidence not unknown'
53+ =/ dup=iapi-path-evidence:jibb evidence(paths [i.paths.evidence paths.evidence])
54+ ?. =(%unknown (api-path-watchability:jibb `dup %items)) 'duplicate case query not unknown'
55+ =/ scry=@t '/% %todo\0a+$ paths [%items /items todo scry=/peek]\0a'
56+ =/ sr (api-path-evidence-source:jibb /ui/api/demo/jibb scry ~ marks ~ prepared)
57+ ?: ?=(%| -.sr) 'scry declaration rejected'
58+ ?. =(%not-watchable (api-path-watchability:jibb `p.sr %items)) 'scry path watchable'
59+ =/ clash=@t '/% %todo\0a+$ paths [%forbidden /one todo]\0a'
60+ ?. ?=([%| *] (api-path-evidence-source:jibb /ui/api/demo/jibb clash ~ marks ~ prepared)) 'reserved value did not reject derived path'
61+ ?. ?=([%| *] (api-path-evidence-source:jibb /ui/api/demo/jibb api externals ~ modules prepared)) 'missing mark accepted'
62+ ?. ?=([%| *] (api-path-evidence-source:jibb /ui/api/demo/jibb api externals (weld marks marks) modules prepared)) 'duplicate mark accepted'
63+ ?. ?=([%| *] (api-path-evidence-source:jibb /ui/demo/jibb api externals marks modules prepared)) 'non-API path accepted'
64+ ?. ?=([%| *] (api-path-evidence-source:jibb /ui/api/demo/jibb api externals marks modules ~)) 'missing prelude accepted'
65+ =/ direct=@t '/= secret /lib/jibb/internal\0a/% %todo\0a+$ paths [%items /items todo]\0a'
66+ ?. ?=([%| *] (api-path-evidence-source:jibb /ui/api/demo/jibb direct ~ marks ~ prepared)) 'direct private import accepted'
67+ =/ indirect=jbundle:jibb ~[[/ui/dep/jibb '/= secret /lib/jibb/internal\0a++ value "x"\0a']]
68+ ?. ?=([%| *] (api-path-evidence-source:jibb /ui/api/demo/jibb api ~ marks indirect prepared)) 'transitive private import accepted'
69+ =/ cyclic=jbundle:jibb ~[[/ui/dep/jibb '/= back /ui/dep\0a++ value "x"\0a']]
70+ ?. ?=([%| *] (api-path-evidence-source:jibb /ui/api/demo/jibb api ~ marks cyclic prepared)) 'cycle accepted'
71+ 'normal watchable; scry not watchable; missing and duplicate unknown'
72+++ real-evidence-status
73+ |= [prelude=@t internal=@t sur=@t action=@t todos=@t update=@t item=@t api=@t]
74+ ^- @t
75+ =/ prepared (prepare-prelude-with-internal:jibb prelude internal)
76+ ?~ prepared 'prelude did not prepare'
77+ =/ todo=type -:(ride -:!>(..zuse) sur)
78+ =/ subject=type [%cell [%face %todo todo] -:!>(..zuse)]
79+ =/ ma=* (read-mark:jibb-import %todo-action -:(ride subject action))
80+ =/ mt=* (read-mark:jibb-import %todos -:(ride subject todos))
81+ =/ mu=* (read-mark:jibb-import %todo-update -:(ride subject update))
82+ =/ mi=* (read-mark:jibb-import %todo-item -:(ride subject item))
83+ =/ marks=imark-externals:jibb ~[[%todo-action ma] [%todos mt] [%todo-update mu] [%todo-item mi]]
84+ =/ result (api-path-evidence-source:jibb /ui/api/todo/jibb api ~ marks ~ prepared)
85+ ?: ?=(%| -.result) p.result
86+ =/ evidence=iapi-path-evidence:jibb p.result
87+ ?. =(source.evidence api) 'API source not retained'
88+ ?. =(marks.evidence marks) 'mark reader graphs not retained'
89+ ?. =((lent paths.evidence) 3) 'incorrect path count'
90+ =/ changes=(list iapi-path-typed:jibb)
91+ (skim paths.evidence |=(p=iapi-path-typed:jibb =(%changes name.path.p)))
92+ ?. ?=([* ~] changes) 'changes path missing'
93+ ?. =(full.path.i.changes %todos) 'full mark missing'
94+ ?. ?=([* ~] diffs.i.changes) 'diff missing'
95+ ?. =(name.i.diffs.i.changes %todo-update) 'diff mark missing'
96+ =/ by-id=(list iapi-path-typed:jibb)
97+ (skim paths.evidence |=(p=iapi-path-typed:jibb =(%todo-by-id name.path.p)))
98+ ?. ?=([* ~] by-id) 'by-id path missing'
99+ ?. ?=(^ scry.path.i.by-id) 'scry path missing'
100+ =/ typed=(list pseg:jibb) (skim segments.path.i.by-id |=(p=pseg:jibb ?=(%typed -.p)))
101+ ?. ?=([* ~] typed) 'typed path missing'
102+ ?> ?=(%typed -.i.typed)
103+ ?. &(=(name.i.typed %id) =(aura.i.typed %ud)) 'typed path incorrect'
104+ ?. =(%watchable (api-path-watchability:jibb `evidence %todos)) 'watchable todos not proved'
105+ ?. =(%not-watchable (api-path-watchability:jibb `evidence %todo-by-id)) 'scry-only case incorrectly classified'
106+ ?. =(%unknown (api-path-watchability:jibb `evidence %missing)) 'unknown case incorrectly classified'
107+ =/ original (api-path-analysis-source:jibb /ui/api/todo/jibb api ~ marks ~ prepared)
108+ ?: ?=(%| -.original) 'original analyzer rejected todo source'
109+ ?. =(paths.evidence p.original) 'typed path evidence differs from original analyzer'
110+ ?. =(prepared.evidence u.prepared) 'actual complete prepared prelude not retained'
111+ =/ todo-full=(unit itype:jibb) (find-imported-mark:jibb %todos marks ~[%todo-action %todos %todo-update %todo-item])
112+ =/ todo-diff=(unit itype:jibb) (find-imported-mark:jibb %todo-update marks ~[%todo-action %todos %todo-update %todo-item])
113+ ?~ todo-full 'real full reader type unavailable'
114+ ?~ todo-diff 'real diff reader type unavailable'
115+ ?. =(full.i.changes u.todo-full) 'real full reader type differs'
116+ ?. =(typ.i.diffs.i.changes u.todo-diff) 'real diff reader type differs'
117+ 'real todo path evidence passed'
118+++ test-api-evidence-synthetic
119+ ^- tang
120+ =/ outcome=@t synthetic-evidence-status
121+ ?: =(outcome 'normal watchable; scry not watchable; missing and duplicate unknown') ~
122+ ~[leaf+(trip outcome)]
123+++ test-api-evidence-real
124+ ^- tang
125+ ?~ sur ~[leaf+"API evidence fixture sur is missing"]
126+ ?~ action ~[leaf+"API evidence fixture action is missing"]
127+ ?~ todos ~[leaf+"API evidence fixture todos is missing"]
128+ ?~ update ~[leaf+"API evidence fixture update is missing"]
129+ ?~ item ~[leaf+"API evidence fixture item is missing"]
130+ ?~ api ~[leaf+"API evidence fixture api is missing"]
131+ =/ outcome=@t (real-evidence-status prelude internal (text sur) (text action) (text todos) (text update) (text item) (text api))
132+ ?: =(outcome 'real todo path evidence passed') ~
133+ ~[leaf+(trip outcome)]
134+--
tests/type-api-evidence/about.txtnew file+3
@@ -0,0 +1,3 @@
1+source: Overview, Agent APIs: path declarations may have a normal watch path or a scry path.
2+tests: The shared evidence guard checks normal, scry, missing and duplicate path-name classification, retained prelude scope and source/reader graphs, and the real todo full and diff types. Classification concerns declarations; it does not inspect request values or check an entire module.
3+expect: declaration-level path metadata only
tests/type-api-evidence/expected.txtnew file+1
@@ -0,0 +1 @@
1+normal watchable; scry not watchable; missing and duplicate unknown
tools/testdata/ship-selftest+2−1
@@ -306,7 +306,8 @@ def generator_checks(tmp):
306306 'tests/lib/jibb-typecheck.hoon', 'tests/lib/jibb-bridge.hoon',
307307 'tests/lib/jibb-arithmetic-guards.hoon',
308308 'tests/lib/jibb-positional-face-guards.hoon',
309− 'tests/lib/jibb-generic-alias.hoon'}
309+ 'tests/lib/jibb-generic-alias.hoon',
310+ 'tests/lib/jibb-api-evidence.hoon'}
310311 <= normal_tests, sorted(normal_tests))
311312 for label, kwargs in (('custom library', {'lib': STUB}),
312313 ('custom goldens', {'golden': CASES}),
tools/type-api-evidence-testnew file+40
@@ -0,0 +1,40 @@
1+#!/usr/bin/env python3
2+"""Check partial path-declaration evidence against synthetic and real todo inputs."""
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+reader = (root / 'jibb-dev/lib/jibb-import.hoon').read_text()
14+tests = (root / 'tests/lib/jibb-api-evidence.hoon').read_text()
15+tests = tests[tests.index('\n|%\n') + 1:]
16+expression = ('=/ jibb .\n=/ jibb-import\n' + reader + '\n' +
17+ ''.join('=/ ' + name + '=wain (to-wain:format ' + name + '-text)\n'
18+ for name in ('sur', 'action', 'todos', 'update', 'item', 'api')) +
19+ '=/ suite\n' + tests +
20+ '\n=/ synthetic=@t synthetic-evidence-status:suite\n' +
21+ '=/ actual=@t (real-evidence-status:suite prelude internal sur-text action-text todos-text update-text item-text api-text)\n' +
22+ "^- @t\n?: !=(actual 'real todo path evidence passed') actual\nsynthetic\n")
23+fixtures = {'sur': 'todo-sur', 'action': 'todo-action', 'todos': 'todos',
24+ 'update': 'todo-update', 'item': 'todo-item', 'api': 'scry'}
25+with tempfile.TemporaryDirectory(prefix='jibb-api-evidence-') as tmp:
26+ program = Path(tmp) / 'evidence.hoon'
27+ program.write_text(expression)
28+ command = [str(root / 'tools/hoon'), '--lib', args.lib, '--raw', '-f', str(program),
29+ '--cord', 'prelude=' + str(root / 'jibb-dev/lib/jibb/prelude.jibb'),
30+ '--cord', 'internal=' + str(root / 'jibb-dev/lib/jibb/internal.jibb')]
31+ for name, filename in fixtures.items():
32+ command += ['--cord', name + '-text=' + str(root / 'tests/type-api-paths' / (filename + '.txt'))]
33+ result = subprocess.run(command, stdout=subprocess.PIPE)
34+if result.returncode:
35+ sys.exit(result.returncode)
36+expected = (root / 'tests/type-api-evidence/expected.txt').read_bytes()
37+if result.stdout != expected:
38+ sys.stderr.buffer.write(result.stdout)
39+ sys.exit(1)
40+print('partial API path evidence guards passed')