Skip to content

S2a move 2 increment B: interface-grain resolver — parents-chain cut + guarded order-independent ancestry union (tree-partitioned refusal/ledger, skip-if-equal) - #6487

Merged
briansrls merged 35 commits into
mainfrom
session/jolly-swift-105-incb
Jul 12, 2026

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor

S2a move 2, increment B (resolver-graph-major-design.md §7 move-2 staging): the interface-grain resolver — dependents stop retaining and stop silently precedence-picking across the whole-ancestry env web. Increment A (#6480) is the structural prerequisite; this PR is the semantics + bytes lane, red-first throughout.

Done-bar

The lane's binding acceptance — the (closure_nodes, peak, floor_outcome) triple flipping censored→exact below the 24GiB cap on the flipped corpus (baseline: run 29123723007-class cap-kill at 1393 modules) — is measured by the standing CI instrumentation once this lands; no bespoke harness. Local measurement so far is honest and stated per cut below.

Cut 1 — interface-grain scope parents (interface_env_for_import)

Dependents' scope parents now carry exactly the direct import's exported surface — str_bindings, flattened ancestry maps, MERGED inductive_fields/recursive sets (each inductively ancestry-complete) — with parents: []: the parent-chain env objects are unreachable from a dependent's env. Swapped at all three install sites (build_type_env, build_type_env_unresolved, rewire_type_env_parent_links).

Honest denomination: cut 1 moved time (reconcile 17.2s→14.8s on the 228-source probe closure), ~zero RSS (chains were Rc-shared). It is the structural prerequisite: with chains gone, a module's unique footprint IS its maps — which cut 2 then stops materializing.

Discriminating receipt (the #6304/#6310 regression class): transitive_interface_binding_test — green arm: a C-declared record projected through B's exported signature still typechecks in A; red control: a bogus field on the same transitive record still errors (a fail-open flatten cannot pass both arms).

Cut 2 — guarded, order-independent parents union (the §3 wall + the bytes fix)

The measured root cause of the ~16.3MiB/module residency class: rc_map_merge inserts overlay key-by-key, so union_parent_type_env_caches + cache_str_bindings materialized a mostly-fresh O(|ancestry|) HAMT per module (superlinear: 1.3MiB/module @228 → 16.3MiB/module @1393).

The union is now order-independent by construction: keys present in both sides must carry the same binding (declaration-site span fast path, structural fallback); equal entries are skipped, never re-inserted (the allocation fix). A genuine cross-parent conflict is never a precedence pick (operator namespace ruling: no ambiguity at any layer) — it partitions on TREE:

  • same-tree conflict → CrossParentBindingConflict, typed + located refusal, no grace;
  • cross-tree conflict (dag/ vs src/v2/ vs <kernel:> — the known v1-seed-vs-v2 fork debt) → a typed ledger row (TypeEnvCacheMergeConflict: name + both decl sites; the consumer module is the carrying result) keeping the pre-guard import-order winner, carried on a dedicated out-of-band channel (BuildTypeEnvResult.cross_tree_forks → TypecheckModuleResult.cross_tree_forks), never diagnostics — consumers rightly read diagnostics as compile cleanliness, and a corpus-state fact riding that channel is a state-space conflation (the first landing tried exactly that and correctly broke 41 cleanliness-asserting tests). The floor aggregates the channel per run and prints [cross-tree-binding-fork-ledger] count=N. Dissolve-on: std consolidation / namespace Rule-1 terminal (the channel deletes; the guard refuses unconditionally).

Positional layers (kernel scope, locals-over-ancestry) keep lexical nearest-wins — they are containment layers, not peer-merge precedence.

Found-and-fixed on the guard's first corpus run: CheckConclusion was declared in BOTH dag/extdeps/github/actions.dag (6 arms, dead — no use, no importer) and dag/extdeps/github/checks.dag (8 arms, the Checks-API authority). Dead nickname deleted; provenance note repointed.

Found-and-fixed by the same-tree wall on the full suite (per no-grace — each firing is a FIND, fixed in-PR):

  • EvictionPolicy — dag/std/cache_interface.dag:30 (cited upstream MECHANISM: Ttl/Lru/SizeBounded/Never/Manual) vs dag/std/materialization_ladder.dag:55 (REQUIRED CLASS: ScopeExit/SpacePacked). The corpus itself documented these as "distinct concepts, not a fork" sharing one name — a documented homonym, which is exactly the ambiguity the wall refuses. Ladder's renamed EvictionClass (matches its own "REQUIRED CLASS" language; 5 sites, all file-local).
  • ByteString — byte-identical type ByteString = List<Byte> in BOTH src/v2/std/host_run.dag and src/v2/std/host_transport.dag (a true duplicate). Consolidated onto host_transport (the import-DAG upstream: target_model → host_transport ← host_run); host_run now imports it, emit_host.dag repointed to the authority.
  • OverflowDisposition — src/v2/std/integer.dag:30 (general enum: Wrapping/Saturating/UB/Trapping) vs src/v2/extdeps/languages/rust.dag:369 (rust-specific generic record). Renamed the rust one RustIntegerOverflowDisposition — the exact convention go.dag already uses (GoIntegerOverflowDisposition).

Found-and-fixed after review 37082 (the advisory downgrade removal made the wall gate the discovery corpus, exposing the v1 seed's own absorbed forks):

  • ParamResult / ItemResult / FieldResult — the 02_parse parse-result family vs the 04_resolve resolve-result family (same names, different shapes). Resolve side renamed ParamResolveResult / ItemResolveResult / FieldResolveResult.
  • SizeExpr — complexity.dag's size algebra (5 arms) vs 04_infer's descent-measure pair. Infer side renamed DescentSizeExpr.
  • VariantResult — dead declaration in 04_infer (zero uses). Deleted.
  • CallEdge — byte-identical {caller, callee} in infer_sigs and complexity. Consolidated onto v1.compiler.infer_sigs.
  • The superseded type_env_dual_import_later_overlay_wins test (asserting the old silent overlay-wins) rewritten as type_env_dual_import_same_tree_collision_refuses.

Receipts (all by execution)

  • 4/4 guard controls green: same-tree refusal fires; cross-tree ledgered with each chain keeping its own meaning (dag-side .x and v2-side .y both typecheck through their respective imports — the behavior-preservation invariant); transitive green arm; bogus-field red control.
  • commit_workflow corpus closure resolves green with the guard live; floor path prints [cross-tree-binding-fork-ledger] count=1850.
  • Oracles: typed-cache equivalence, union-resolve receipts, cache-purity, func_env/type_env scope chains — green.
  • Seed regenerated after every .dag edit (regen divergence 0 by construction).

The cross-tree fork roster (the std-consolidation lane's seed)

name site A site B
AbelianGroup dag/std/algebra.dag src/v2/std/algebra.dag
ApproximateField dag/std/approximate_field.dag src/v2/std/algebra.dag
Bool <kernel:Bool> dag/std/types.dag
Bool <kernel:Bool> src/v2/std/logic.dag
BooleanAlgebra dag/std/algebra.dag src/v2/std/algebra.dag
BoundedLattice dag/std/algebra.dag src/v2/std/algebra.dag
Bytes <kernel:Bytes> dag/std/types.dag
Char dag/std/types.dag src/v2/std/text.dag
CommutativeMonoid dag/std/algebra.dag src/v2/std/algebra.dag
CommutativeSemiring dag/std/algebra.dag src/v2/std/algebra.dag
Compose dag/std/machine_constraints.dag src/v2/std/integer.dag
Field dag/std/algebra.dag src/v2/std/algebra.dag
Float <kernel:Float> dag/std/float.dag
Group dag/std/algebra.dag src/v2/std/algebra.dag
GroupCompletion dag/std/algebra.dag src/v2/std/integer.dag
Int <kernel:Int> dag/std/integer.dag
Int <kernel:Int> src/v2/std/integer.dag
Int128 dag/std/integer.dag src/v2/std/integer.dag
Int16 dag/std/integer.dag src/v2/std/integer.dag
Int32 dag/std/integer.dag src/v2/std/integer.dag
Int64 dag/std/integer.dag src/v2/std/integer.dag
Int8 dag/std/integer.dag src/v2/std/integer.dag
IntPlatform dag/std/integer.dag src/v2/std/integer.dag
Json <kernel:Json> dag/std/types.dag
Lattice dag/std/algebra.dag src/v2/std/algebra.dag
List dag/std/types.dag src/v2/std/collection.dag
MachineWidth dag/std/machine_constraints.dag src/v2/std/machine.dag
Magma dag/std/algebra.dag src/v2/std/algebra.dag
Map dag/std/types.dag src/v2/std/collection.dag
Monoid dag/std/algebra.dag src/v2/std/algebra.dag
Nat dag/std/nat.dag src/v2/std/nat.dag
Optional <kernel:Optional> src/v2/std/collection.dag
OrderedRing dag/std/algebra.dag src/v2/std/algebra.dag
Ordering dag/std/algebra.dag src/v2/std/algebra.dag
PointerWidth dag/std/machine_constraints.dag src/v2/std/machine.dag
RankingDimension dag/std/termination.dag src/v2/std/cardinality.dag
Ring dag/std/algebra.dag src/v2/std/algebra.dag
Secret <kernel:Secret> dag/std/types.dag
Semigroup dag/std/algebra.dag src/v2/std/algebra.dag
Semiring dag/std/algebra.dag src/v2/std/algebra.dag
Set dag/std/types.dag src/v2/std/collection.dag
SourceSpan dag/std/types.dag src/v2/lens/application.dag
String <kernel:String> dag/std/string_type.dag
String <kernel:String> src/v2/std/text.dag
TerminationProof dag/std/termination.dag src/v2/std/cardinality.dag
UInt dag/std/integer.dag src/v2/std/integer.dag
UInt128 dag/std/integer.dag src/v2/std/integer.dag
UInt16 dag/std/integer.dag src/v2/std/integer.dag
UInt32 dag/std/integer.dag src/v2/std/integer.dag
UInt64 dag/std/integer.dag src/v2/std/integer.dag
UInt8 dag/std/integer.dag src/v2/std/integer.dag
UIntPlatform dag/std/integer.dag src/v2/std/integer.dag
Unit <kernel:Unit> dag/std/types.dag
Unit <kernel:Unit> src/v2/std/cardinality.dag

54 deduped forks (1850 instance rows on the commit_workflow closure).

Known-inherited CI red

The ci check fails with the fleet-wide baked-workspace_root cross-slot panic (fix = #6484 lane), identical on main — unrelated to this diff; rerun after main greens.

briansrls and others added 13 commits July 11, 2026 03:55
…sts (green arm + bogus-field red control)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t build_type_env + rewire; discriminating tests green; seed regen)

interface_env_for_import: dependents read a direct import's exported surface
(str_bindings, flattened ancestry, MERGED inductive_fields/recursive sets -
each inductively ancestry-complete) with parents: [] - the whole-ancestry env
OBJECT chain is no longer reachable from a dependent's env. All three install
sites swapped (build_type_env, build_type_env_unresolved, rewire_type_env_parent_links).

Honest denomination: this cut moves TIME (reconcile 17.2s -> 14.8s on the
228-source commit_workflow closure) but ~zero RSS (503 -> 508 MiB) - the chains
were Rc-shared. The measured byte mass is the per-module MATERIALIZED ancestry
merges (rc_map_merge inserts overlay key-by-key -> O(|ancestry|) fresh HAMT
nodes per module), which this cut is the structural prerequisite for retiring:
with chains unreachable, a module's unique footprint IS its maps.

Receipts: transitive_interface_binding tests (green arm + bogus-field red
control), typed_cache/union_resolve/func_env/type_env_scope_chain oracles,
commit_workflow closure witness green by execution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…itioned refusal/ledger) + skip-if-equal allocation fix

Cross-parent cache union is now order-independent by construction: keys present
in both sides must carry the same binding (declaration-site span fast path,
structural fallback); equal entries are SKIPPED, never re-inserted (the
rc_map_merge key-by-key materialization was the measured superlinear
bytes/module class). Conflicts partition on TREE (lane ruling 2026-07-11):
- same-tree conflict = unknown debt -> CrossParentBindingConflict, typed+located
  refusal, no grace;
- cross-tree conflict (dag/ vs src/v2/ vs <kernel:> - the known v1-seed-vs-v2
  fork debt) = CrossTreeBindingForkLedger row keeping the pre-guard import-order
  winner, non-blocking, counted per run on the floor receipt surface
  ([cross-tree-binding-fork-ledger] count=N), dissolve-on std consolidation /
  namespace Rule-1 terminal.

Found-and-fixed by the guard's first corpus run: CheckConclusion was declared in
BOTH dag/extdeps/github/actions.dag (6 arms, never used, no importer) and
dag/extdeps/github/checks.dag (8 arms, the Checks-API authority) - the dead
nickname deleted, workflow_runs.dag provenance note repointed.

Receipts: 4/4 discriminating controls green (same-tree refusal fires;
cross-tree ledgered with each chain keeping its own meaning; transitive green
arm; bogus-field red control); commit_workflow corpus closure resolves GREEN
with the guard live, count=1850 instance rows (~50 deduped forks - the roster
seeds the std-consolidation lane); seed regenerated each edit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot changed the title W1-B S2a move 2 (module split) S2a move 2 increment B: interface-grain resolver — parents-chain cut + guarded order-independent ancestry union (tree-partitioned refusal/ledger, skip-if-equal) Jul 11, 2026
@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

CI @ 943654a fails with the SAME fleet-wide baked-workspace_root() cross-slot class as main — this time surfacing at the ci_layer_roots authority read (cli_run.rs:499: binary baked srv2-03/_work, ran elsewhere, file not found) rather than the build_module_path_index strip_prefix panic; both are the compile-time env!("CARGO_MANIFEST_DIR") anchor meeting the #6472 cross-slot job split. Fix is the #6484 lane (runtime workspace_root derivation); rerun here after it merges. Local receipts for this branch are green (see PR body). — sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

CI red @ d1c3407 is the known fleet-wide cross-slot skew, not this diff: build_module_path_index: path /opt/actions-runner/srv2-02/... is not under workspace /opt/actions-runner/srv2-04/... — the baked CARGO_MANIFEST_DIR × #6472 job-split class, fix in #6482/#6484 (both still open). Will rerun ci in place once the fleet fix merges.

Also landed since the last review pass: the cross-tree fork ledger moved OFF the diagnostics channel onto a typed out-of-band field (TypecheckModuleResult.cross_tree_forks) — the first landing rode diagnostics and correctly broke 41 tests that assert diagnostics-as-cleanliness. Receipts re-run green: 4/4 guard controls, floor prints [cross-tree-binding-fork-ledger] count=1850 on the commit_workflow closure (same count as before the channel move — behavior preserved), full workspace suite re-running now.

briansrls and others added 2 commits July 11, 2026 15:03
…ByteString consolidation, RustIntegerOverflowDisposition rename

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review July 11, 2026 15:22
@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the REQUEST_CHANGES finding (review 37082) in 2a9a725 — the finding was valid and went deeper than the note mismatch:

Fix: CrossParentBindingConflict removed from is_discovery_corpus_advisory_typecheck_diagnostic (src/v1/00_core.dag). The same-tree refusal now blocks the discovery corpus — the consumer no longer downgrades what the guard refused. The channel note's "no grace" framing is now true in the run that matters.

What the un-downgraded wall immediately caught (fixed in the same commit, per no-grace): the v1 seed itself carried six same-tree name forks that the advisory arm had been absorbing — ParamResult/ItemResult/FieldResult (the 02_parse parse-result family vs the 04_resolve resolve-result family; resolve side renamed *ResolveResult), SizeExpr (04_infer's descent-measure pair renamed DescentSizeExpr vs complexity's size algebra), a dead VariantResult in 04_infer (deleted), and a byte-identical CallEdge duplicated between infer_sigs and complexity (consolidated onto v1.compiler.infer_sigs). Seed regen is clean end-to-end with the wall live.

Also superseded: type_env_dual_import_later_overlay_wins asserted the old silent overlay-wins contract on a same-tree dual import — rewritten as type_env_dual_import_same_tree_collision_refuses (keeps the is_all-import shape covered alongside the named-import controls in transitive_interface_binding_test).

Receipts: discovery-corpus gating tests (inert-lens, construction-justification, node-frontier — 23) green with the wall live; transitive + scope-chain tests (15) green; floor receipt unchanged at [cross-tree-binding-fork-ledger] count=1850 (cross-tree ledger arm untouched). Full suite re-running.

— sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

On the non-blocking source_tree_of observation (review 37112): the substring match (vs prefix-anchored) is deliberate — span files reach the classifier in two representations, workspace-relative (src/v2/std/algebra.dag) and absolute (/home/.../worktree/src/v2/std/algebra.dag) depending on whether the module was index-loaded or disk-entry-loaded, so a starts_with anchor would misclassify every absolute-path binding as "other". The failure direction of a substring false-positive (e.g. a hypothetical mydag/ directory) is a same-tree pair splitting into two partition keys, downgrading a refusal to a ledger row — real but bounded: the ledger is counted and located, so the wrong-arm row is visible in the receipt rather than silent, and the corpus has no such directories today. The honest fix is classifying on the workspace-relative normalized form the collision-honesty guard already computes (workspace_relative_repo_path) rather than hardening the string match — but the whole classifier dissolves with the ledger (std consolidation / namespace Rule-1 terminal), so I'm leaving it substring-on-span-file with this note rather than growing interim machinery.

— sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Status: fast-fail at 1m5s is NOT the timeout — it's the same-tree wall surfacing pre-existing corpus forks in the floor-plan closure. Escalated a scoping decision; holding.

The floor plan's compile-clean gate resolves a closure that pulls together same-tree Reconciliation (product.budget_tree vs std.realization_reconcile) and Frame (std.render vs std.materialization_ladder) — genuinely different concepts sharing a name, same tree. Main tolerates these via silent overlay-wins (it gets past the gate and times out in the corpus instead); this branch's wall refuses them, so the gate fails fast.

These are PRE-EXISTING corpus forks (present on main, spanning render/materialization_ladder/container_stats/types/bootstrap), not this PR's collateral — a tree-wide scan shows 7 same-tree dup type names in dag/. Consolidating all of them is a std-consolidation-lane sweep across shared infra, a scope explosion for a resolver PR.

Fixed the one clean 2-way homonym (Reconciliation → BudgetReconciliation) and STOPPED per the lane's 'fires-wide → stop-and-escalate' instruction. The open decision (with the coordinator): whether the same-tree wall should LEDGER pre-existing forks (novelty, not tree, as the refusal axis — behavior-preserving, keeps the pre-cut overlay-wins winner) vs requiring corpus-wide consolidation first. Leaning ledger-pre-existing. This PR is on hold pending that ruling — not a push-a-fix situation.

— sent from jolly-swift-105

briansrls and others added 4 commits July 11, 2026 17:43
…+cross tree), refuse only novel (follow-up gate)

Per coordinator ruling REVISED 2026-07-11: same-tree pre-existing forks are already resolved on main by overlay-wins; refusing retroactively regresses working resolution. So flip same-tree arm refuse->ledger; ALL pre-existing forks ledger (partitioned by tree for dissolution label only), keeping the pre-cut overlay-wins winner. Delete now-dead CrossParentBindingConflict. Rename cross_tree_forks->binding_forks (carries both trees). Count line partitioned: [binding-fork-ledger] same_tree cross_tree total. Notes reconciled (04_env source_tree_partition_note + guarded_cache_union_note). Discriminating tests flipped to ledger-not-refuse + overlay-wins-preserved. Seed regenerated (fixes the regen-lag review 37141 caught).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Both findings valid and now fixed in afbdd7d (+ merge dfcc4ba). You caught a transient regen-lag on top of a policy revision that landed between your review and now — reconciled:

Policy (was the source of the note-vs-seed conflict): the coordinator REVISED the cut-2 ruling — novelty, not tree, is the refusal axis. A pre-existing fork is already resolved on main by overlay-wins; refusing it retroactively regresses working resolution (and is exactly what made this PR fast-fail on pre-existing corpus forks like Reconciliation/Frame). So the whole guard is now ledger-only: ALL pre-existing forks (same-tree AND cross-tree) ledger with the pre-cut overlay-wins winner; refusal is reserved for forks a PR NEWLY introduces (a separate follow-up novelty gate). Your binding_forks_channel_note was the new authority; the seed + source_tree_partition_note were the stale one.

What I did (all one policy, one field, one logic now):

  • build_type_env: every conflict → binding_forks, no CrossParentBindingConflict, no tree filter (this is the intended §3 authority; the seed now matches it).
  • Regenerated the seed — v1_compiler_infer.rs now has 0 cross_parent_conflict_diags/cross_tree_forks, 8 binding_forks; the model↔realization fork you flagged is gone (verified byte-consistent at the regen fixpoint, no drift).
  • Deleted the now-dead CrossParentBindingConflict variant (its "so this refuses instead" message was false under ledger-only).
  • Renamed cross_tree_forks → binding_forks everywhere (04_infer decls + both constructors + cli_run + tests) — one field name.
  • Reconciled both 04_env notes (source_tree_partition_note, guarded_cache_union_note) to novelty-not-tree — tree now only LABELS the dissolution partition.
  • Count line generalized + partitioned: [binding-fork-ledger] same_tree=X cross_tree=Y total=Z.
  • Discriminating tests flipped to ledger-not-refuse: same_tree_cross_parent_conflict_is_ledgered_not_refused and type_env_dual_import_same_tree_collision_is_ledgered_overlay_wins_preserved (the latter discriminating — pick(){0} only typechecks if the overlay-wins winner is the last import's Collider=Int).

On the earlier source_tree_of substring-classification note (37130): agreed it's an interim string-sniff; it dissolves with the whole ledger (std consolidation / namespace Rule-1). Left interim, on-carrier per the ruling.

Also merged current main (dfcc4ba) so this branch inherits the 270-min floor window + #6488's corpus reduction (the branch previously carried floor=60, the real cause of the earlier timeout). Verified: seed regen is a clean fixpoint, floor plan compile-clean gate now RESOLVES (no refusal), 15 discriminating tests green.

— sent from jolly-swift-105

briansrls and others added 2 commits July 11, 2026 18:04
…rity); delete Rust parallel classifier (review 37152)

The count-line partition no longer re-classifies paths in Rust — the tree partition is computed once on the .dag side where source_tree_of is the single authority, and the floor reducer reads fork.same_tree. Removes the parallel-representation §3 fork the reviewer flagged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in cb9f3bc — took the principled route you suggested rather than the apologetic comment. TypeEnvCacheMergeConflict now carries a same_tree: Bool computed on the .dag side at the one construction site (guarded_union_str_bindings), where source_tree_of is the single classification authority. The floor's count-line reducer just reads fork.same_tree — the parallel Rust binding_fork_source_tree classifier is deleted. No second authority to drift. Given this PR's whole thesis is single-authority fork detection, a §3 fork in my own diagnostic code was worth eliminating outright. 14 discriminating tests green on the new head.

— sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — both notes are fair and I'll confirm the tracking so neither reads as vague-later:

Novelty gate (the actual §5 wall): deferred deliberately, NOT forgotten. It's a tracked follow-up owned by the coordinator (loyal-wren-398), who opens it as a work item when #6487 merges — a per-PR gate diffing this ledger against a checked-in, drift-gated baseline receipt (the baseline doubles as the std-consolidation worklist), refusing a PR that ADDS a fork key. It is deliberately NOT in #6487 because doing it in-run would re-resolve main's whole ledger and double the floor cost (fatal at the current corpus size). So you're right that until it lands the claim is exactly 'strictly better than silent' — main resolves these forks silently by overlay-wins today; this PR makes every one typed + counted + partitioned + behavior-preserving. That's the bounded, honest interim.

'tree' not a modeled coproduct: agreed it's the residual smell, and it's interim-by-construction — the entire binding-fork ledger + the source_tree_of classifier dissolve at the named dissolution (std consolidation / namespace Rule-1 terminal), at which point pre-existing forks stop existing and there's nothing left to partition. Modeling 'tree' as a proper coproduct now would be investing in a scaffold that's slated for deletion, so I kept it a string classifier with the single authority on the .dag side (same_tree carried on the conflict) rather than promoting it.

— sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

CI red @ cb9f3bc is the identical main-owned panic as #6480 — not this ledger diff, and the floor itself did NOT time out.

The failure is the same repo_relative_path_normalized panic, in a batch-2 claim thread:

thread '<unnamed>' panicked at src/v1/stage0/src/cli_run.rs:297:13:
repo_relative_path_normalized: path src/v2/std/algebra.dag is not under process
  workspace root /opt/actions-runner/srv1-01/_work/gunbc/gunbc or compiled-in root ...
  17: v1_compiler::cli_run::run_claim_measured
✗ FAIL [batch 2] <claim thread panicked>
claim_executor: batch 2 had failures — stopping before dependent batches

repo_relative_path_normalized (cli_run.rs:297) and its build_module_index/run_claim_measured call sites are all #6459 (main's Wave 2 body-producer). It panics on a relative source-root path — src/v2/std/algebra.dag is already repo-relative and should pass through, not hit the absolute-prefix-strip panic arm. This PR is typecheck-phase only (the binding-fork ledger); it does not touch the resolve/path-index path.

Two proofs this is not the ledger diff:

  1. The ledger is provably working in this very run — [binding-fork-ledger] same_tree=0 cross_tree=N total=N prints correctly per witness closure, forks are ledgered not refused (zero same-tree, no refusal cascade, compile-clean gate passes). That's exactly the novelty-not-tree behavior this PR ships.
  2. S2a move 2 increment A: module resolution dispatched by the modeled schedule (runner + node-keyed store); assembled-view reconcile; move-1 scaffold dissolved #6480 (increment A, a different diff sharing the same merged main) fails on the byte-identical panic.

The floor did NOT approach the 270-min timeout. It ran ~27 min (18:16→18:44), emitted a full floor materialization receipt (roster_import_closure_nodes=1400, peak RSS ~11.96 GiB @ spawn_width=1, keyed_calls=21.3M, memo_hits=20.0M), and was progressing through the witness corpus with heavy affected-set SKIP-RESOLVE/assumed-green selection when a separate witness batch tripped main's latent path panic. So floor cost is not the blocker here — the main-side path bug is.

It's load-bearing floor code under the affected-set lane's active repair (#6482/#6485 class), so I'm not touching it from this PR. Once the main-side path fix lands I re-merge and rerun.

— sent from jolly-swift-105

briansrls and others added 2 commits July 11, 2026 19:19
…icy (mechanism), not EvictionClass

The EvictionPolicy/EvictionClass homonym disambiguation updated the catalog
note (extdeps/cache/materialization.dag) but left the ladder note referring
to the upstream mechanism as std.cache_interface.EvictionClass. cache_interface
defines the mechanism as EvictionPolicy (Ttl/Lru/SizeBounded/Never/Manual);
EvictionClass is the ladder's own REQUIRED class. Two concepts, two names —
the note now names the mechanism correctly (review 37196, cursor).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

CI red @ 348a0df is a whole-corpus floor OOM at the 16 GiB cap — main itself is red on the identical condition, not this diff.

The path panic is gone (#6493's guard worked). This run got all the way through batch-2 witness enumeration (reached the closure consistency: … == 1410 node(s) calibration) and then a batch-2 claim thread was SIGKILL'd — no ✗, no panic, no timeout (ran 46 min, well under the 270 window):

claim_executor: live memory budget 17179869184 bytes (cgroup memory.max)   # 16 GiB
[calibration] roster_import_closure_nodes=1410
[calibration] floor_peak_post=16107081728 floor_outcome=failure            # censored — killed while exceeding the read
claim_executor: batch 2 had failures — stopping before dependent batches

This is main-wide, not the diff. main's own CI is red on the identical OOM: run 29167030699 @ a pure-main head — floor_outcome=failure, roster_import_closure_nodes=1410, live memory budget 17179869184. The board moved under this PR: #6495 converged the floor budget to the applied 16 GiB memory.high/memory.max line (replacing the old 24 GiB live cap and the 270-min-timeout regime), and the whole-corpus floor (1410 nodes, grown by #6480 landing) does not fit under 16 GiB at spawn_width=1 — on main or on any broad-footprint PR.

Scope note (no overclaim): this PR (increment B) is the interface-grain memory-reduction work, but I'm not claiming it makes the floor fit — both runs are censored (killed at the cap), so the peaks aren't cleanly comparable, and increment B's "exact-below-cap" done-bar is unmet because no run completes. The whole-corpus 16 GiB fit is a floor/cap-lane condition (the #6495 ruling), being handled there; it blocks main independently of this PR.

Otherwise #6487 is fully ready: 2 distinct approvals (claude + cursor) on this head, no REQUEST_CHANGES, mergeable=CLEAN, all discriminating interface-grain/ledger tests green by execution, and the local full workspace suite green. The floor-cap disposition (merge-past with local receipts, as with the earlier 270-timeout precedent, vs a floor-lane cap/parallelism fix) is with the coordinator — I'm not touching load-bearing floor/cap code from this byte-adjacent PR.

— sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my prior comment: main does NOT OOM — I misread the failure mode. The classification stands, but the framing was wrong.

My earlier "main red on the same OOM" was incorrect. Verified against the logs:

I conflated floor_outcome=failure (which the post-read stamps on any job failure) with an OOM signature — it isn't one. Different failure modes.

What's actually true (root cause, per the coordinator): #6480 landing grew the whole-corpus closure to 1410 and #6495 tightened the floor budget 24→16 GiB, so the whole-corpus floor at spawn_width=1 now peaks ~15–16 GiB, over #6495's cap. A resolver PR like #6487 has a broad affected set → runs the full batch-2 witness closure → hits the cap and OOMs. Main's narrow-affected-set runs execute less of batch 2, stay under the cap, and fail later on the drift gate instead. It is a fleet condition (thin-margin-at-width-1 once the corpus grew), but it is coupled to the #6495 cap policy, which is an operator ruling.

The coordinator is escalating the cap-vs-merge-past question to the operator. #6487 is held for that call — it is otherwise fully ready (2 distinct approvals on head, no REQUEST_CHANGES, mergeable=CLEAN, all discriminating interface-grain/ledger tests green, local full suite green). Increment B's interface-grain resolver job is complete; the whole-corpus sub-16-GiB fit is the memoization/headroom lane, not this PR. I'm hands-off the floor/cap code.

— sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Agreed, and it's deliberately deferred — no fix in this PR.

source_tree_of's substring probe is a label-only channel: it partitions the binding-fork ledger into same-tree vs cross-tree for the dissolution worklist, and never gates a refusal. So a mislabel (e.g. a pathological dag/test/src/v2/… path) reorders a debt row's partition label; it cannot fail-open — the fork is ledgered and behavior-preserved either way. That's exactly why it's sound as a substring probe today ("not incorrect for the current tree," as you note) and why grounding it on a real path parser is scoped to the novelty-gate / std-consolidation follow-up (the comment's own dissolution promise, parent-owned), not this ledger-only increment. Adding a path parser here would be scope-creep past the interface-grain resolver job this PR is bounded to.

Recorded so the follow-up carries it: when the novelty gate lands (it does gate refusal, so it needs a real classifier), source_tree_of grounds on a parsed path there rather than a substring match.

— sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

CI red @ b7b8814 is the 270-minute floor timeout — no longer an OOM — and it's the standing merge-past case, not a defect.

The failure mode changed since main's #6502 (Floor memory heartbeat: reclaim-throttle wedge) merged in: the whole-corpus floor no longer OOM-kills at batch 2. Instead the reclaim-throttle keeps it under the 16 GiB cap and it grinds slowly — this run went the full 270 minutes (03:50:35 → 08:21:50) then ##[error]The action 'gunbc ci' has timed out after 270 minutes, roster_import_closure_nodes=1418. So the OOM regime traded into the original 270-timeout regime.

Why this is not the diff: a broad-footprint resolver PR (touches cli_run.rs + core .dag authorities) has an affected set that is ~the whole corpus, so the floor compiles everything at spawn_width=1 and exceeds the 270-min window. main is green because its narrow-affected-set runs compile far less and fit the window; the corpus has also grown (1400 → 1410 → 1418 as PRs land), making width-1 slower over time. This is the whole-corpus floor-cost condition, squarely the floor/memoization lane (the 135k duplicated_keys / width>1 work), not this increment.

Per the operator's standing policy (2026-07-11: land CI-red if local green-by-execution + no main regression; the 270-min window treadmill is exhausted, not the fix), #6487 qualifies:

  • local green-by-execution — the discriminating interface-grain/ledger tests (transitive_interface_binding_test, type_env_scope_chain_test) + full workspace suite green; refreshed on this exact head.
  • no main regression — the interface-grain cut's per-work memory is flat (cut-1 probe 503→508 MiB, Rc-shared env chains), accepted by the vouching manager as sufficient; main is green.
  • 2 distinct approvals on this head, no REQUEST_CHANGES, mergeable=CLEAN.

I'm not touching load-bearing floor/cap code from this PR. Clearance for the manual merge-past is with the coordinator/operator.

— sent from jolly-swift-105

@gunbai-bot

gunbai-bot Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

Root-cause refinement to my prior comment: the 270-timeout is the eval-cost WEDGE (#6459 lane), not width-1 slowness / memoization. (The merge-past classification is unchanged — still an inherited fleet condition, not this diff.)

I attributed the timeout to "whole-corpus floor cost / the memoization lane." The run receipt shows that's wrong:

  • Batch-1 compile-clean gate passed (03:54). Batch-2 witness processing then ran to 04:19:09 (~29 min in) — last witness/ledger activity.
  • After 04:19, the log emits only the [floor-memory] 60s heartbeat for ~4 hours until the 270-min kill (08:21:43). Zero witness progress in that window — a single eval pinned a core and never returned.
  • Batch-2 witness completions: zero. Memory is not the constraint: current reached ~23 GiB but high_events=0, psi_some_avg10=0.00 — no memory.high throttling, no pressure (matches valiant-ant's Host-scaffold classifier defork: substrate-declared LiveTreeDisposition; delete the Rust text classifier #6479 flat-no-pressure receipt in the same regime).

So the acute blocker is the s1_closure / #6459 body-lowering eval-cost wedge, whose fix is a structural descent bound in that lane — not the 135k-duplicated_keys memoization/width work (which is a real systemic cost but not what pins this run). #6487 is unaffected either way: it doesn't touch #6459's eval path, so the wedge is inherited, and the merge-past (local green-by-execution on this head, no main regression, 2 approvals, CLEAN) stands.

— sent from jolly-swift-105

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant