on master help sign in
Report projected union overlaps from the full module check

The partial report found overlaps with the pairwise projection first and fell back to the source witness, but the full module check used only the source witness. So two auras, ? and %.y, or equal nested tags failed the full check as "module mold needs unsupported inference" with no sites, although the partial report showed both overlapping cases. Both paths now call one helper, +mold-overlap-witness, so the full check reports "module mold <name> has overlapping cases" with the same pair and structured evidence. The basic full-overlap test covers the three shapes and the exact legacy text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U4FzmSdubYxmZGDp2VJRAQ

commit447a592caee6103b8690a9eed7c6333cf8f39f69 authorClaude <noreply@anthropic.com> dateMon 2 Nov 2026, 16:32 (6 hours ago) parenta211c34
10 files changed+63−24
docs/compiler.md+1−1 jibb-dev/lib/jibb.hoon+19−10 tests/lib/jibb-full-overlap-basic.hoon+23−10 tests/type-full-overlap/about.txt+1−1 tests/type-full-overlap/atoms.txt+4 tests/type-full-overlap/expected-basic.txt+4 tests/type-full-overlap/flag.txt+4 tests/type-full-overlap/nested.txt+4 tools/testdata/ship-selftest+2−1 tools/type-full-overlap-test+1−1
docs/compiler.md+1−1
@@ -66,7 +66,7 @@ Before the empty-capital optimization below, single sequential eval runs includi
6666
6767Restricted-number promotion only applies to written capital variables. If none are present, it returns immediately instead of traversing the environment. In one startup-inclusive pair of runs on 2,000 independent gates, output was byte-identical and user CPU fell from 71.67 to 38.26 seconds; wall time was 72.80 versus 51.66 seconds while other checks were running. This does not establish a bound for dependent modules or a complete-checker benchmark.
6868
69−A bounded source proof can also establish distinct literal heads when a union case is a one-hop application of a uniquely resolved local generic mold. It compares literal runtime nouns and checks the original mold normally before publishing an interface. The same source proof improves the partial report, which still says the mold is not checked. The new proof declines nested or qualified applications, opaque references, recursive-index states, holes and deferred obligations; other valid unions can remain unsupported. A 256-node source budget and a separate 256-entry inherited-state budget apply per attempt. They do not bound the existing pairwise union pass, parsing, atom or name sizes, or downstream checking. Prepared interfaces and inference state remain trusted inputs. `tools/type-union-source-test` and the shared ship guard exercise source resolution, reporting, rollback and these bounds. A separate positive witness can identify overlap in two-field, literal-headed cases when a unique one-hop local, one-parameter generic alias is applied to a concrete primitive payload. It compares the literal noun values and proves the payloads have a shared noun, so an empty or unknown payload is not a witness. The partial report identifies both original case sites. `infer-jibb-file-detailed` returns structured overlap evidence for the first failed mold, including the originating path, exact source, mold name and both case spans. Ordinary Jibb imports preserve a child’s evidence through source and cache checks; the legacy `infer-jibb-file` API remains a text-result adapter and renders both excerpts. Specialized API and prepared-prelude paths retain their existing generic or text-only error boundaries. This witness uses a shared 256-node source/pair budget per attempt; generic-wrapper parameter validation has a separate 256-entry cap. An unsupported earlier case may hide a later overlap. Existing pairwise union analysis, parsing, atom and name sizes, source-excerpt rendering, and downstream checking remain outside these budgets. `tools/type-union-alias-overlap-test` checks the source proof and partial report; `tools/type-full-overlap-test` checks the detailed and legacy full-module paths, source attribution and cache behavior.
69+A bounded source proof can also establish distinct literal heads when a union case is a one-hop application of a uniquely resolved local generic mold. It compares literal runtime nouns and checks the original mold normally before publishing an interface. The same source proof improves the partial report, which still says the mold is not checked. The new proof declines nested or qualified applications, opaque references, recursive-index states, holes and deferred obligations; other valid unions can remain unsupported. A 256-node source budget and a separate 256-entry inherited-state budget apply per attempt. They do not bound the existing pairwise union pass, parsing, atom or name sizes, or downstream checking. Prepared interfaces and inference state remain trusted inputs. `tools/type-union-source-test` and the shared ship guard exercise source resolution, reporting, rollback and these bounds. A separate positive witness can identify overlap in two-field, literal-headed cases when a unique one-hop local, one-parameter generic alias is applied to a concrete primitive payload. It compares the literal noun values and proves the payloads have a shared noun, so an empty or unknown payload is not a witness. The partial report identifies both original case sites. The partial report and the full module check find overlaps with one helper, `mold-overlap-witness`: it takes the first overlapping pair from the pairwise projection, which covers atoms, `?` and literal constants, and nested literal-headed cells, and tries this source witness only when some pair stays unknown. Both paths therefore name the same pair. `infer-jibb-file-detailed` returns structured overlap evidence for the first failed mold, including the originating path, exact source, mold name and both case spans. Ordinary Jibb imports preserve a child’s evidence through source and cache checks; the legacy `infer-jibb-file` API remains a text-result adapter and renders both excerpts. Specialized API and prepared-prelude paths retain their existing generic or text-only error boundaries. This witness uses a shared 256-node source/pair budget per attempt; generic-wrapper parameter validation has a separate 256-entry cap. An unsupported earlier case may hide a later overlap. The pairwise projection has no budget; the full check runs it only on a union mold whose inference failed. Parsing, atom and name sizes, source-excerpt rendering, and downstream checking also remain outside these budgets. `tools/type-union-alias-overlap-test` checks the source proof and partial report; `tools/type-full-overlap-test` checks the detailed and legacy full-module paths, source attribution and cache behavior.
7070
7171A single-arm cell pattern can bind two or more positions, with its last binder capturing the remaining tail. Duplicate and shadowed names are rejected. A definite non-cell spine fails; an intermediate union or collection that might contain a cell remains unsupported. For a known non-union cell, a separate one-arm rule also accepts structured patterns, including nested literal and wildcard positions, when the existing coverage proof establishes the whole pattern. It binds payloads with their original types and checks result context; unknown, partially covered, union or recursive spines stay unsupported.
7272
jibb-dev/lib/jibb.hoon+19−10
@@ -11067,6 +11067,20 @@
1106711067 [at.first at.second (union-relate a (project-union-mold second molds ~))]
1106811068 (weld pairs $(cases t.cases))
1106911069::
11070+:: The partial report and the full module check share one overlap proof,
11071+:: so both name the same pair. The projection proves atoms, flags and
11072+:: nested literal cells; the source slice is tried only when some pair
11073+:: is unknown, for its one-hop generic applications. A missing witness
11074+:: does not mean the cases are disjoint.
11075+++ mold-overlap-witness
11076+ |= [cases=(list shape) molds=imolds]
11077+ ^- (unit upair)
11078+ =/ pairs=(list upair) (union-pairs cases molds)
11079+ =/ overlapping=(list upair) (skim pairs |=(p=upair =(%overlap relation.p)))
11080+ ?^ overlapping `i.overlapping
11081+ ?. (lien pairs |=(p=upair =(%unknown relation.p))) ~
11082+ (union-source-overlap cases molds 256)
11083+::
1107011084:: A closed structural mold supplies no equation to determine a hole.
1107111085:: Applications, references, unbound capitals and unions are unproved.
1107211086++ simple-mold-hole
@@ -11205,19 +11219,14 @@
1120511219 (of-wain:format ~[(crip "+$ {(trip name.d)}: type hole needs a determined type") text])
1120611220 =? sh ?=(%generic -.sh) body.sh
1120711221 ?. ?=(%union -.sh) (crip "+$ {(trip name.d)}: not checked")
11208− =/ pairs=(list upair) (union-pairs cases.sh molds)
11209− =/ overlapping=(list upair) (skim pairs |=(p=upair =(%overlap relation.p)))
11210− =. overlapping
11211− ?^ overlapping overlapping
11212− ?. (lien pairs |=(p=upair =(%unknown relation.p))) ~
11213− =/ witness=(unit upair) (union-source-overlap cases.sh molds 256)
11214− ?~(witness ~ ~[u.witness])
11215− ?^ overlapping
11216− =/ p=upair i.overlapping
11222+ =/ witness=(unit upair) (mold-overlap-witness cases.sh molds)
11223+ ?^ witness
11224+ =/ p=upair u.witness
1121711225 =/ summary=@t (crip "+$ {(trip name.d)}: overlapping cases; mold not checked")
1121811226 =/ first=@t (show-error src [file line.beg.first.p col.beg.first.p "first union case"])
1121911227 =/ second=@t (show-error src [file line.beg.second.p col.beg.second.p "overlapping case"])
1122011228 (of-wain:format ~[summary first second])
11229+ =/ pairs=(list upair) (union-pairs cases.sh molds)
1122111230 ?: (lien pairs |=(p=upair =(%unknown relation.p)))
1122211231 ?. (union-source-distinct-tags cases.sh molds 256)
1122311232 (crip "+$ {(trip name.d)}: case disjointness unresolved; mold not checked")
@@ -11564,7 +11573,7 @@
1156411573 ?. valid ~
1156511574 =? sh ?=(%generic -.sh) body.sh
1156611575 ?. ?=(%union -.sh) ~
11567− (union-source-overlap cases.sh molds 256)
11576+ (mold-overlap-witness cases.sh molds)
1156811577++ module-mold-types-detail
1156911578 |= [molds=imolds state=istate]
1157011579 ^- (each imold-types imold-failure)
tests/lib/jibb-full-overlap-basic.hoon+23−10
@@ -11,6 +11,9 @@
1111/* child %txt /tests/type-full-overlap/child/txt
1212/* generic %txt /tests/type-full-overlap/generic/txt
1313/* duplicate-generic %txt /tests/type-full-overlap/duplicate-generic/txt
14+/* atoms %txt /tests/type-full-overlap/atoms/txt
15+/* flag %txt /tests/type-full-overlap/flag/txt
16+/* nested %txt /tests/type-full-overlap/nested/txt
1417/* expected %txt /tests/type-full-overlap/expected-basic/txt
1518|%
1619+$ overlap-evidence [file=path source=@t name=term cases=upair:jibb]
@@ -46,8 +49,9 @@
4649 |= [file=path source=@t modules=jbundle:jibb]
4750 ^- detailed-result
4851 (infer-jibb-file-detailed:jibb file source ~ ~ modules ~[file] ~ ~ ~ ~)
52+:: The first two cases overlap, written on consecutive lines from .line.
4953++ expected-overlap
50− |= [file=path source=@t name=term]
54+ |= [file=path source=@t name=term line=@ud]
5155 ^- ?
5256 =/ expected=(unit upair:jibb) (source-pair file source name)
5357 ?~ expected |
@@ -61,9 +65,9 @@
6165 =(name.ev name)
6266 =(cases.ev u.expected)
6367 =(%overlap relation.cases.ev)
64− =(3 line.beg.first.cases.ev)
68+ =(line line.beg.first.cases.ev)
6569 =(3 col.beg.first.cases.ev)
66− =(4 line.beg.second.cases.ev)
70+ =(+(line) line.beg.second.cases.ev)
6771 =(3 col.beg.second.cases.ev)
6872 ==
6973++ plain-failure
@@ -75,7 +79,7 @@
7579 ?: ?=(%& -.result) |
7680 &(!=(0 message.p.result) ?=(~ overlap.p.result))
7781++ run
78− |= [same=@t zero=@t unsupported=@t precedence=@t duplicate=@t invalid=@t distinct=@t parent=@t child=@t generic=@t duplicate-generic=@t]
82+ |= [same=@t zero=@t unsupported=@t precedence=@t duplicate=@t invalid=@t distinct=@t parent=@t child=@t generic=@t duplicate-generic=@t atoms=@t flag=@t nested=@t]
7983 ^- (list [@t ?])
8084 =/ root=path /ui/root/jibb
8185 =/ names256=(list @t) (turn (gulf 0 255) |=(n=@ud (scot %ud n)))
@@ -94,6 +98,11 @@
9498 ?: ?=(%& -.legacy) |
9599 =/ lines=wain (to-wain:format p.legacy)
96100 &((lien lines |=(line=@t =(line '/ui/root.jibb:3:3: first union case'))) (lien lines |=(line=@t =(line '/ui/root.jibb:4:3: overlapping case'))))
101+ :: A projected overlap renders the same error as a source witness.
102+ =/ legacy-atoms=jfile-result:jibb (infer-jibb-file:jibb root atoms ~ ~ ~ ~[root] ~ ~ ~ ~)
103+ =/ legacy-projected=?
104+ ?: ?=(%& -.legacy-atoms) |
105+ =((to-wain:format p.legacy-atoms) ~['module mold bad has overlapping cases' '/ui/root.jibb:2:3: first union case' ' @t' ' ^' '/ui/root.jibb:3:3: overlapping case' ' @ud' ' ^'])
97106 =/ imported=detailed-result (detailed root parent ~[[child-file child]])
98107 =/ child-pair=(unit upair:jibb) (source-pair child-file child %child-bad)
99108 =/ import-valid=?
@@ -110,9 +119,12 @@
110119 :~ ['256 generic parameters accepted by scan' (module-mold-params-valid:jibb names256 *(set @t) 256)]
111120 ['257 generic parameters exhaust scan' !(module-mold-params-valid:jibb names257 *(set @t) 256)]
112121 ['duplicate generic parameters fail scan' !(module-mold-params-valid:jibb ~['A' 'A'] *(set @t) 256)]
113− ['same head has source-bound overlap' (expected-overlap root same %bad)]
114− ['zero spelling has source-bound overlap' (expected-overlap root zero %bad)]
115− ['generic wrapper has source-bound overlap' (expected-overlap root generic %bad)]
122+ ['same head has source-bound overlap' (expected-overlap root same %bad 3)]
123+ ['zero spelling has source-bound overlap' (expected-overlap root zero %bad 3)]
124+ ['generic wrapper has source-bound overlap' (expected-overlap root generic %bad 3)]
125+ ['two auras have source-bound overlap' (expected-overlap root atoms %bad 2)]
126+ ['flag and yes have source-bound overlap' (expected-overlap root flag %bad 2)]
127+ ['nested equal tags have source-bound overlap' (expected-overlap root nested %bad 2)]
116128 ['duplicate generic parameters remain unstructured' (plain-failure root duplicate-generic)]
117129 ['unsupported remains unstructured' (plain-failure root unsupported)]
118130 ['later overlap exists in precedence source' (source-witness root precedence %bad)]
@@ -122,6 +134,7 @@
122134 ['distinct alias source parses and checks' &(?=(%& -.good-parsed) ?=(%& -.good-result))]
123135 ['parent itself has different positive overlap' (source-witness root parent %parent-bad)]
124136 ['legacy full-module error contains both excerpts' legacy-excerpts]
137+ ['legacy projected overlap error is exact' legacy-projected]
125138 ['import overlap retains exact child source' import-valid]
126139 ==
127140++ text
@@ -129,14 +142,14 @@
129142 ^- @t
130143 (rap 3 (turn lines |=(line=@t (cat 3 line '\0a'))))
131144++ output
132− |= [same=@t zero=@t unsupported=@t precedence=@t duplicate=@t invalid=@t distinct=@t parent=@t child=@t generic=@t duplicate-generic=@t]
145+ |= [same=@t zero=@t unsupported=@t precedence=@t duplicate=@t invalid=@t distinct=@t parent=@t child=@t generic=@t duplicate-generic=@t atoms=@t flag=@t nested=@t]
133146 ^- @t
134− =/ result=(list [@t ?]) (run same zero unsupported precedence duplicate invalid distinct parent child generic duplicate-generic)
147+ =/ result=(list [@t ?]) (run same zero unsupported precedence duplicate invalid distinct parent child generic duplicate-generic atoms flag nested)
135148 (of-wain:format (turn result |=(entry=[@t ?] (crip "{(trip -.entry)}: {?:(+.entry "ok" "BAD")}"))))
136149++ test-full-overlap-basic
137150 ^- tang
138151 ?~ expected ~[leaf+"expected output missing"]
139− =/ actual=@t (output (text same) (text zero) (text unsupported) (text precedence) (text duplicate) (text invalid) (text distinct) (text parent) (text child) (text generic) (text duplicate-generic))
152+ =/ actual=@t (output (text same) (text zero) (text unsupported) (text precedence) (text duplicate) (text invalid) (text distinct) (text parent) (text child) (text generic) (text duplicate-generic) (text atoms) (text flag) (text nested))
140153 ?. =((to-wain:format (cat 3 actual '\0a')) expected)
141154 ~[leaf+"full-module overlap output differs"]
142155 ~
tests/type-full-overlap/about.txt+1−1
@@ -1,3 +1,3 @@
11source: docs/spec/grammar.md, Molds: “Cases must differ as nouns”.
2−tests: Full-module overlap evidence retains the defining source and both case spans through ordinary Jibb imports, and legacy errors render the excerpts. Distinct cases check; duplicate names, stale caches, cycles, private imports and other unsupported inputs retain their own earlier errors. Specialized API and prelude paths remain outside this diagnostic slice.
2+tests: Full-module overlap evidence retains the defining source and both case spans through ordinary Jibb imports, and legacy errors render the excerpts. Overlaps that the partial report proves by projection (two auras, `?` and `%.y`, equal nested literal heads) carry the same evidence. Distinct cases check; duplicate names, stale caches, cycles, private imports and other unsupported inputs retain their own earlier errors. Specialized API and prelude paths remain outside this diagnostic slice.
33expect: shared eval and ship guards
tests/type-full-overlap/atoms.txtnew file+4
@@ -0,0 +1,4 @@
1++$ bad
2+ @t
3+ @ud
4+++ identity |=(x x)
tests/type-full-overlap/expected-basic.txt+4
@@ -4,6 +4,9 @@ duplicate generic parameters fail scan: ok
44same head has source-bound overlap: ok
55zero spelling has source-bound overlap: ok
66generic wrapper has source-bound overlap: ok
7+two auras have source-bound overlap: ok
8+flag and yes have source-bound overlap: ok
9+nested equal tags have source-bound overlap: ok
710duplicate generic parameters remain unstructured: ok
811unsupported remains unstructured: ok
912later overlap exists in precedence source: ok
@@ -13,4 +16,5 @@ invalid source rejects without overlap: ok
1316distinct alias source parses and checks: ok
1417parent itself has different positive overlap: ok
1518legacy full-module error contains both excerpts: ok
19+legacy projected overlap error is exact: ok
1620import overlap retains exact child source: ok
tests/type-full-overlap/flag.txtnew file+4
@@ -0,0 +1,4 @@
1++$ bad
2+ ?
3+ %.y
4+++ identity |=(x x)
tests/type-full-overlap/nested.txtnew file+4
@@ -0,0 +1,4 @@
1++$ bad
2+ [%a [%b @ud]]
3+ [%a [%b @t]]
4+++ identity |=(x x)
tools/testdata/ship-selftest+2−1
@@ -379,7 +379,8 @@ def generator_checks(tmp):
379379 {f'tests/type-full-overlap/{name}.txt' for name in (
380380 'same', 'zero', 'unsupported', 'precedence', 'duplicate',
381381 'invalid', 'distinct', 'parent', 'child', 'generic',
382− 'duplicate-generic', 'expected-basic', 'expected-graph')} <= set(normal.files))
382+ 'duplicate-generic', 'atoms', 'flag', 'nested', 'expected-basic',
383+ 'expected-graph')} <= set(normal.files))
383384 check('deferred transaction and resolver sources are assembled',
384385 {'tests/type-deferred/nested-src.txt', 'tests/type-deferred/principal-src.txt',
385386 'tests/type-deferred/expected.txt', 'tests/type-deferred-resolver/good.txt',
tools/type-full-overlap-test+1−1
@@ -14,7 +14,7 @@ parser.add_argument('--update', action='store_true')
1414args = parser.parse_args()
1515fixture = root / 'tests/type-full-overlap'
1616cases = {
17− 'basic': ['same', 'zero', 'unsupported', 'precedence', 'duplicate', 'invalid', 'distinct', 'parent', 'child', 'generic', 'duplicate-generic'],
17+ 'basic': ['same', 'zero', 'unsupported', 'precedence', 'duplicate', 'invalid', 'distinct', 'parent', 'child', 'generic', 'duplicate-generic', 'atoms', 'flag', 'nested'],
1818 'graph': ['parent', 'child', 'distinct'],
1919}
2020for kind, names in cases.items():