Repository navigation
Conversation
…tity Two roadmap-authority changes, both about replanning not destroying work. FOCUS CLOSURE. Owner membership alone was the wrong visibility rule. The v1 deletion lane's critical path runs through pderive-source-slot-binding, whose prerequisite namespace-canonical-binding lives in a lane the focus hid — so the page rendered the fourth link of the spine as ready with its blocker invisible. Nothing was red; the page lied by omission. Worse for a dispatched agent than for a person: a worker reads a ready-looking node, starts, and stalls on work the roadmap never mentioned. Visibility is now lane membership UNION the transitive prerequisite closure, reusing std.graph.graph_reverse_reachable_members over the roadmap's own edge relation rather than minting a second walk — the edge is already a dependency edge, reverse reachability from a lane node is exactly its ancestors, and the graph is acyclic by witness so the walk terminates by construction. The closure found FIVE blockers, not the three visible by eye: namespace-canonical-binding has its own prerequisites, so the lane was blocked on a namespace sub-chain of three. Manual inspection would have discovered those one stall at a time. Pulled-in rows are named as prerequisites rather than counted as lane work, so the lane's real size stays honest at 17. The focus label is now "the v1 deletion lane" per the operator's naming. DURABLE IDENTITY. A roadmap node's id is its authored slug (RoadmapNodeId = NonEmptyStr), so a rename is structurally a delete plus a create: edges stop resolving, receipts name a node that no longer exists, and any attempt or session joined to it is orphaned. That is not hypothetical — the 2026-07-27 authority refresh deleted the node carrying the only execution contract and no claim noticed. std.occurrence_identity already states the right law: identity may not derive from filename, span, authored name, structural equality, or content hash. The LAW is reused; the TYPE deliberately is not. OccurrenceId is an ordinal from a per-graph allocator starting at zero, so inserting a node at the front renumbers every node after it and two branches mint the same integer — positional identity wearing an allocator's clothes. Correct for occurrences inside one parse, wrong for an entity that must survive edits, reordering, regeneration and independent branches. What occurrence identity does not claim is PERSISTENCE SCOPE, and that is the only thing missing. 51 collision-resistant identities are minted and persisted, one per declared node, at AUTHORING time — nothing mints during render, compile or regen, because a value re-derivable by evaluation would reintroduce the property the module exists to remove. The slug stays readable and stops being identity. Stage I0 is deliberately additive: no rendering, dependency, receipt or dispatch path changes. The v1-deletion recut can be authored against durable identities immediately without waiting on a consumer migration, and without its new nodes being rewritten a second time afterwards. TWO GATES DECLARED AS OWED, NOT ASSUMED. Permanence is a historical property and every witness here reads the current tree, so an identity silently REPLACED between base and head satisfies all of them — the snapshot corpus is structurally incapable of catching the exact edit this module forbids. The owed row lists the admitted and refused transitions for a base-versus-head registry comparison. Separately, decomposition needs an explicit parent completion policy: containment keeps pointers valid but cannot say whether a parent completes on direct acceptance, on all children, or both, and inferring it would silently close a parent whose children are unfinished. Verified: whole-tree compile 0 blocking errors; ROADMAP.md regenerated; 109 witnesses green with zero failures across roadmap_identity (7, incl. the discriminating identity_is_persisted_not_positional), roadmap_focus (8, incl. no_visible_node_has_a_hidden_prerequisite), roadmap_authority, generated_artifact_drift, roadmap_page, doc_reachability (both homes), roadmap_emit and roadmap_document. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
This was referenced Jul 29, 2026
briansrls
added a commit
that referenced
this pull request
Jul 29, 2026
…entity, bounded agent report, heal auto-remedy (#7440) * Bind the first roadmap node's execution contract; dispatch stops refusing everywhere Every roadmap node was ExecutionContractUnspecified, so dispatch_environment_admit matched that arm and refused before doing anything else. Clicking Dispatch on any of the ~46 tickets produced a typed refusal and nothing more, which meant no part of the workflow below Env — worktree, agent, and every receipt that depends on them — had ever executed end to end in production. The frontier reached zero when the 2026-07-27 authority refresh deleted the node that carried the prior binding; no claim noticed, because none existed. The binding is a COMBINATOR, not a constructor parameter. `sign` is the precedent sitting beside it and already threads `execution: rn.execution` through untouched; threading an execution argument instead would edit seven constructors and every one of their call sites to express a fact that today holds for exactly one node. A node without the wrapper keeps ExecutionContractUnspecified, which stays the fail-closed refusal the frontier note declares rather than a default anyone chose. shell-typed-invocation is bound to the transport-clean corpus witness (corpus_dag_compile_clean_transport_clean_holds), which is this ticket's own red control expressed as an operation: "a new unclassified production construction site reds the census". It reads the live tree, so it is a statement about the repository rather than a fixture. That validation is ALREADY GREEN, deliberately. A first binding has two jobs that pull apart — prove the Verify path runs at all, and prove it discriminates — and conflating them is how a green pipeline gets mistaken for a working one. This row takes the first job only: an Agent turn that changes nothing still produces a real admitted environment, a real derived command, and a real validation receipt, which is a statement about the PLUMBING and not about the provider. It proves nothing about discrimination and must not be read as evidence that Verify can fail; the discriminating red belongs on a second node whose validation is currently red and goes green when the work lands, and that is the next binding, not this one. The frontier note is corrected in the same motion. It read "Every node below is ExecutionContractUnspecified", which was true when written and false the moment one was bound. It now states the count is one, and that a count is not a completion. Three witnesses pin both halves of the frontier: the bound node yields a real contract rather than the fail-closed variant; the platform/task capability JOIN actually happens at admission (8 fixed dispatch mechanics + the one capability this validation derives = 9); and the frontier is exactly one node — which fails if a binding is silently lost AND if a second is added without updating the roster, so the documented count cannot drift from the authority. ROADMAP.md is unchanged: execution contracts are dispatch mechanics, not roadmap content, and the projection correctly does not render them. Verified: whole-tree compile 0 blocking errors; 46 witnesses green across roadmap_authority (incl. the three new ones and the pre-existing re-entry RED controls), generated_artifact_drift, roadmap_execution_contract, and roadmap_dispatch_environment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Bind the discriminating second node: give Verify a validation that can actually fail The first binding named an already-green validation, on purpose, to prove the Verify path executes at all — admission, argv derivation, receipt production. It cannot prove the path DISCRIMINATES, because nothing it runs is capable of returning anything but green. A frontier with only that binding is the coverage-by-illusion tier: machinery present, nothing gating on it. observation-raw-print-retirement now binds a validation that FAILS today. gunbc.observation_emit_census classifies every seed emit site as exactly one of MigratedToObservation or CountedFrontierSite, and observation_emit_frontier_count is the population of the second arm — 8 today (floor-drain, gate-warm-cost, receipt, file, resolve-split, assembly-split, witness-row-cost, witness-row-cost-drift). The witness asserts 0, which is that node's handback stated verbatim: "the classified zero-residue progress census". So the red is the ticket's own acceptance condition rather than a planted fixture, the count IS the completion condition rather than a proxy for it, and red -> green is the work being finished. The two bindings now pin both directions, and neither claim is obtainable from the other. Finding a genuine red took some looking, and the search is worth recording: the transport-script wall is clean corpus-wide (every enrolled path AND every unenrolled Exec.Run site I measured returns zero violations), so there was no red available there; the existing known-red quarantine rows are self-host behavioral receipts that red on cargo/shim closure refusal, which is infrastructure rather than anything a dispatched agent could turn green. The emit census is the one place where a small, agent-doable ticket has a countable completion condition that is currently false. The acceptance witness is enrolled QuarantineProbeExpectRed in gunbc.ci_layer_roots — the cadence std.witness_admission defines for exactly this, "known-red quarantined rows run on a probe cadence expecting red (greening is a counted un-quarantine event)" — so a deliberately-red acceptance test does not red the corpus. Its dissolution is forced rather than optional: when the frontier reaches zero the roster row becomes a still-red expectation against a green witness, so it must be deleted in the same change that lands the last migration, promoting the witness to ordinary DiscoverySelection as a standing regression ratchet. It lives in its own file rather than inside observation_emit_census_witness_test because that suite asserts the count the census HAS (== 8, which must red if the roster drifts) while this asserts the count the work OWES (== 0). Both are true and they disagree by construction; merging them would drag the roster's own hygiene check into quarantine and stop it reding when it should. The frontier note now reads TWO and says why the pair is one claim rather than two bindings. witness_execution_contract_frontier_is_exactly_one_node becomes _two_nodes, joined by witness_bound_pair_covers_both_verify_directions. Verified: whole-tree compile 0 blocking errors; the discriminating validation FAILS and the plumbing validation PASSES when each is run directly, which is the receipt that matters; 63 witnesses green with zero failures across roadmap_authority, generated_artifact_drift, observation_emit_census and witness_admission. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Carry the agent's report through the bounded projection, with truncation reported The dashboard could not show what an agent actually said. The raw JSONL is retained per attempt, but codex_provider_event_projection_filter — the jq stage that bounds each line before the substrate reads it — emitted only {"type":…,"item":{"type":…}} for item events, so an agent_message survived as an activity CLASSIFICATION and its text was gone before anything could render it. That was deliberate: the boundary exists because one item.completed carrying ~154KB of aggregated command output killed the old 64KB reader. So surfacing the report is a change AT that boundary, not a rendering change, and the reviewable decision is the bound. THE FIELD NAME IS CAPTURED, NOT GUESSED. Nothing in the repository recorded the agent_message item's text member, the shipped CLI is a stripped native binary, and the locator extdeps.llm.cli already cites documents the mode rather than the per-item event schema. Modeling it from memory is the guess DESIGN §3 forbids for an extdeps surface, and #7368's own design note sets the same bar for provider grammars: admitted only "after an actual provider receipt demonstrates its grammar". So a receipt was produced — codex exec --json against @openai/codex 0.145.0, landed under dag/test/fixture/codex_provider_events with its version and capture conditions. Its third line is the authority: {"type":"item.completed","item":{"id":"item_0","type":"agent_message","text":"ok"}} The report gets its OWN budget rather than reusing the detail budget beside it. They are the same type and genuinely different facts: a detail is a diagnostic, short by nature, read to find out what broke; a report is what the provider says it DID, and it is the text an operator reads to judge whether the work is any good. One shared number would mean tuning either bound silently retunes the other. 8192 is set against the incident this projection exists to prevent — an order of magnitude under the 65536 line budget so a report can never be the payload that reproduces it, and deliberately above the 4096 detail budget because a 4KB error is pathological while a 6KB work summary is ordinary. TRUNCATION IS REPORTED, NEVER SILENT. Everywhere else these bounds guard a machine-read classification, where a cut string simply fails to match and refuses. Here the bounded value is prose an operator reads and acts on, so a report severed mid-sentence and shown as if complete is a wrong answer delivered confidently — the operator would judge the work on a fragment with nothing on screen saying so. The envelope therefore carries a message object pairing the bounded text with a truncated flag computed from the ORIGINAL length before slicing, so the flag describes the provider's output rather than the slice. The anchored item.type walk is untouched by construction: the report is emitted as a member AFTER item.type, so item.type remains item's first member and the walk's landing position is unchanged — a report whose own text quotes an item-type fragment still cannot reclassify the pane. The note that described the envelope shape is corrected to say so rather than understating it. Verified by execution, which for a jq program means running the MODELED filter string rather than a hand copy: extracted from the substrate and run through real jq against the real captured receipt, it yields {"text":"ok","truncated":false}; against an 8193-character control it yields exactly 8192 retained characters and truncated:true. Four witnesses pin the contract the shell cannot prove downstream: the bound is the message budget (a copy-paste of the detail budget would still emit valid jq and still look fine until the day a long message arrived), the two budgets are distinct, truncation is computed against the original length, and both item envelope shapes keep item.type first. Verified: whole-tree compile 0 blocking errors; provider-events 18/18; 50 more green across roadmap_workflow_progress, generated_artifact_drift and roadmap_authority; zero failures. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Close the focus over prerequisites; give roadmap nodes a durable identity Two roadmap-authority changes, both about replanning not destroying work. FOCUS CLOSURE. Owner membership alone was the wrong visibility rule. The v1 deletion lane's critical path runs through pderive-source-slot-binding, whose prerequisite namespace-canonical-binding lives in a lane the focus hid — so the page rendered the fourth link of the spine as ready with its blocker invisible. Nothing was red; the page lied by omission. Worse for a dispatched agent than for a person: a worker reads a ready-looking node, starts, and stalls on work the roadmap never mentioned. Visibility is now lane membership UNION the transitive prerequisite closure, reusing std.graph.graph_reverse_reachable_members over the roadmap's own edge relation rather than minting a second walk — the edge is already a dependency edge, reverse reachability from a lane node is exactly its ancestors, and the graph is acyclic by witness so the walk terminates by construction. The closure found FIVE blockers, not the three visible by eye: namespace-canonical-binding has its own prerequisites, so the lane was blocked on a namespace sub-chain of three. Manual inspection would have discovered those one stall at a time. Pulled-in rows are named as prerequisites rather than counted as lane work, so the lane's real size stays honest at 17. The focus label is now "the v1 deletion lane" per the operator's naming. DURABLE IDENTITY. A roadmap node's id is its authored slug (RoadmapNodeId = NonEmptyStr), so a rename is structurally a delete plus a create: edges stop resolving, receipts name a node that no longer exists, and any attempt or session joined to it is orphaned. That is not hypothetical — the 2026-07-27 authority refresh deleted the node carrying the only execution contract and no claim noticed. std.occurrence_identity already states the right law: identity may not derive from filename, span, authored name, structural equality, or content hash. The LAW is reused; the TYPE deliberately is not. OccurrenceId is an ordinal from a per-graph allocator starting at zero, so inserting a node at the front renumbers every node after it and two branches mint the same integer — positional identity wearing an allocator's clothes. Correct for occurrences inside one parse, wrong for an entity that must survive edits, reordering, regeneration and independent branches. What occurrence identity does not claim is PERSISTENCE SCOPE, and that is the only thing missing. 51 collision-resistant identities are minted and persisted, one per declared node, at AUTHORING time — nothing mints during render, compile or regen, because a value re-derivable by evaluation would reintroduce the property the module exists to remove. The slug stays readable and stops being identity. Stage I0 is deliberately additive: no rendering, dependency, receipt or dispatch path changes. The v1-deletion recut can be authored against durable identities immediately without waiting on a consumer migration, and without its new nodes being rewritten a second time afterwards. TWO GATES DECLARED AS OWED, NOT ASSUMED. Permanence is a historical property and every witness here reads the current tree, so an identity silently REPLACED between base and head satisfies all of them — the snapshot corpus is structurally incapable of catching the exact edit this module forbids. The owed row lists the admitted and refused transitions for a base-versus-head registry comparison. Separately, decomposition needs an explicit parent completion policy: containment keeps pointers valid but cannot say whether a parent completes on direct acceptance, on all children, or both, and inferring it would silently close a parent whose children are unfinished. Verified: whole-tree compile 0 blocking errors; ROADMAP.md regenerated; 109 witnesses green with zero failures across roadmap_identity (7, incl. the discriminating identity_is_persisted_not_positional), roadmap_focus (8, incl. no_visible_node_has_a_hidden_prerequisite), roadmap_authority, generated_artifact_drift, roadmap_page, doc_reachability (both homes), roadmap_emit and roadmap_document. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * heal: apply the binary/source skew remedy automatically instead of printing it The skew guard printed "merge the base branch, then re-run" and exited 1. That is a correct refusal and a wasted round trip: the job holds contents:write, already pushes commits to this branch, and the fix is a merge it is fully authorized to perform. Making a human do a mechanical repair the machine can do is the toil this pipeline exists to remove (operator ruling 2026-07-29). This is a REPAIR, not a widen, which is what makes it admissible under DESIGN §5. The arm does not proceed despite the skew — it REMOVES the skew by merging the base into the branch, then re-runs the same check it just failed. Regeneration happens only if that second check passes, so artifacts are still projected by a compiler the tree agrees with. The invariant is re-established and re-verified, never assumed. The remedy is attempted exactly once; a base that moves again mid-run refuses rather than looping. A MERGE CONFLICT CONFINED TO GENERATED ARTIFACTS IS NOT A CONFLICT. Measured on the first real skew this guard caught (#7404): the only conflicting path was ROADMAP.md — a registered generated artifact this job rewrites from its authority in the very next step. Two branches that both regenerate a projection will always collide in the projection, so refusing there would fail the auto-remedy on its single most common case while the authorities underneath merged cleanly. A generated artifact has no independent content to reconcile: it is a pure function of the .dag authority, so whichever side is checked out is equally wrong until main_wet runs and equally right afterwards. Taking either side and regenerating is not a guess — the bytes are determined by the merged authority, and the drift gate proves the result is that authority's fixed point. Scope is exactly committed_generated_artifact_paths(), the same registry the staging step uses, so a file qualifies only by being declared generated. Both failure arms stay closed. If ANY conflicting path is outside that registry the merge aborts and refuses, listing every conflict and naming the authored ones — the classification is on the WHOLE set, so one authored conflict refuses the entire remedy rather than partially resolving into a half-merged tree. The merge commit is --no-verify: this is an unattended machine commit, and the repository's own rulings already establish that git hooks are per-clone developer feedback rather than enforcement (opt-in via core.hooksPath, bypassable, absent in container worktrees). Found by execution — the local pre-commit hook failed the remedy's commit with a cargo-not-on-PATH error, which CI would not have hit but which should not gate a machine commit in any case. Verified by execution against the real #7404 skew, not a fixture: skew detected, merge attempted, conflict correctly classified as generated-only, resolved, re-checked, EXIT=0 with the seed skew gone. The classification arm is unit-tested across five cases — generated-only and generated x2 auto-resolve; authored-only, seed-Rust, and MIXED all refuse, the mixed case being the one that must not partially resolve. Verified: whole-tree compile 0 blocking errors; ci.yml regenerated from the model; emitted guard bash -n clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Close the roadmap focus over prerequisites Owner membership alone was the wrong visibility rule. The v1 deletion lane's critical path runs through pderive-source-slot-binding, whose prerequisite namespace-canonical-binding lives in a lane the focus hid — so the page rendered the fourth link of the spine as READY with its blocker invisible. Nothing was red; the page lied by omission. That is worse for a dispatched agent than for a person: a worker reads a ready-looking node, starts, and stalls on work the roadmap never mentioned. Visibility is now lane membership UNION the transitive prerequisite closure, reusing std.graph.graph_reverse_reachable_members over the roadmap's own edge relation rather than minting a second walk — the edge is already a dependency edge, reverse reachability from a lane node is exactly its ancestors, and the graph is acyclic by witness so the walk terminates by construction. The closure found FIVE blockers where three were visible by eye: namespace-canonical-binding has its own prerequisites, so the lane was blocked on a namespace sub-chain of three. Manual inspection would have found those one stall at a time, each costing a wasted attempt. Pulled-in rows are named as prerequisites in the notice rather than counted as lane work, so the lane's real size stays honest. THE DISCRIMINATOR IS SYNTHETIC, deliberately (review). An earlier cut asserted count(pulled) > 0 against the live roadmap_focus_selection, which contradicts the carrier's own promise that changing or clearing the focus is a one-row view edit: it fails the moment the focus is NoFocus, or a lane that legitimately has no cross-owner prerequisite — reporting a defect where none exists and making the focus row un-editable in practice. The fixture owns its two lanes and its transitive cross-lane chain, so it holds under any live focus, and it exercises TRANSITIVITY (b3 pulls b2 pulls b1) which is the part manual inspection misses. A companion asserts the no-external-prerequisite case pulls nothing, so the empty result is a tested outcome rather than an untested branch. The notice claim is also strengthened per review: the test named "pulled in and named as such" now actually checks the notice contains every pulled id, which it previously did not. The live-tree invariant is stated conditionally beside the fixture: whatever the current focus is, no visible node may have a hidden prerequisite. That holds under every focus including NoFocus, so it constrains the projection without pinning the selection. Split out of #7417 on review: the identity half needs identity on RoadmapNode, an active/tombstoned lifecycle, and a base-versus-head permanence gate, and is being rebuilt separately. This half is independent of that work. Verified: whole-tree compile 0 blocking errors; ROADMAP.md regenerated from the authority; 104 witnesses green with zero failures across roadmap_focus (12), roadmap_authority, generated_artifact_drift, roadmap_page, doc_reachability (both homes), roadmap_emit and roadmap_document. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Give roadmap nodes a durable identity as a required field A roadmap node's id was its authored slug, so a rename was structurally a delete plus a create: edges stop resolving, acceptance receipts name a node that no longer exists, and any attempt or session joined to it is orphaned. Not hypothetical — the 2026-07-27 authority refresh deleted the node carrying the only execution contract and no claim noticed. std.occurrence_identity already states the right law: identity may not derive from filename, span, authored name, structural equality or content hash. The LAW is reused; the TYPE deliberately is not. OccurrenceId is an ordinal from a per-graph allocator starting at zero, so inserting a node at the front renumbers everything after it and two branches mint the same integer — positional identity wearing an allocator's clothes. Right for occurrences inside one parse, wrong for an entity that must survive edits, reordering, regeneration and independent branches. What occurrence identity does not claim is PERSISTENCE SCOPE, and that is the only thing missing. IDENTITY IS A REQUIRED FIELD, NOT A SIDE REGISTRY. The first cut carried a hand-written (identity, slug) list beside the authority. It compiled cleanly and was wrong twice over: two authorities that must be kept in step (DESIGN 3), and a node still authorable WITHOUT an identity, detectable only by a later witness — validation standing where construction was available (DESIGN 5). As a field it is unwritable instead, and the index is DERIVED from declared_roadmap_nodes() so the two cannot disagree. The change measured its own necessity: adding the field produced 50 hard failures — 28 struct literals the compiler rejected outright, then 22 constructor call sites failing with `undefined variable: identity`. Every one was a site where a node could previously come into existence without durable identity, and the side registry would have compiled past all fifty. 62 collision-resistant identities are minted and persisted at AUTHORING time. Nothing mints during render, compile or regen; a value re-derivable by evaluation would reintroduce the property this exists to remove. TOMBSTONES, because a base-versus-head diff alone cannot enforce non-reuse. Without persisted retirement: PR A removes node X, a diff observes the removal once, after merge X is simply absent from base, PR B reuses X's identity, and the next comparison sees present-in-head/absent-in-base — indistinguishable from a legitimately new identity. The reuse passes every check. So retirement is a STATE: TombstonedIdentity keeps the identity forever, carries the slug it last wore so historical links resolve, and names its successor. The roster is empty today and the witnesses are arithmetic (registry == active + tombstoned) rather than pinning zero, so the first real retirement will not red them. RESOLUTION IS EXACTLY-ONE. filter-map-first answers a duplicate registry with a plausible first row — the fabricated-plausible-output arm DESIGN 5 forbids, and it fails exactly when the registry is corrupt and a confident wrong answer is worst. IdentityResolved | IdentityUnknown | IdentityAmbiguous{matches} keeps the three remedies distinct. The discriminating witness is identity_survives_a_slug_rename. Every other claim here — uniqueness, totality, resolution — is equally true of a broken registry where identity IS the slug; only this one fails if the separation collapses. STILL OWED: the permanence gate. Every claim reads the current tree, so an identity silently REPLACED between base and head satisfies all of them. The carrier lists the admitted and refused transitions concretely; the tombstone above is what makes that gate implementable at all. ROADMAP.md is unchanged: identity is machinery, the slug remains display. Verified: whole-tree compile EXIT=0, 0 blocking errors; ROADMAP.md regenerated; 241 witnesses green across 15 roadmap suites. Seven wet witnesses fail identically on plain main (hermetic mode refusing to fabricate a host Check) and are unrelated — confirmed by running them on origin/main before attributing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Enroll the known-red acceptance witness on its executing probe roster CI caught orphaned enrollment, which is the Phase 0(b) admission invariant working exactly as intended: WITNESS ADMISSION REFUSAL cause=UnexecutedDeferredWitness count=1 — enrolled witness row(s) excluded from discovery name zero executing consumers The deliberately-red acceptance witness was classified QuarantineProbeExpectRed in witness_exclusion_frontier, which removes it from per-PR discovery. That is half the job. The exclusion says where it does NOT run; it does not say where it DOES. Without a matching row on known_red_probe_entries — the roster that actually executes known-red witnesses on their probe cadence expecting red — the witness ran nowhere, which is precisely the "enrolled, zero executions" coverage-by-illusion the invariant refuses. Adding the probe row completes the pair. The exclusion note now states the pairing rule and cites this failure, so the next author reads why both rows are required rather than discovering it from a floor red. Verified: whole-tree compile 0 blocking errors; ci.yml/ROADMAP.md regenerated. * Restore identity pass-through in with_execution The fixture-identity script matched the RoadmapNode literal inside with_execution and replaced its `identity: rn.identity` pass-through with a hardcoded literal. Since with_execution wraps both bound nodes, both collapsed onto one identity. identities_are_unique_across_the_registry failed on the first attempt to violate it — which is the claim's whole purpose: it is the one that notices when identity stops being per-node. Caught before the change could ship rather than after. Both combinators (sign, with_execution) now thread rn.identity. Verified: 4/4 identity witnesses green including identity_survives_a_slug_rename. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two roadmap-authority changes, both about replanning not destroying work. Split out of #7404 so the authority model reviews separately from the dispatch mechanism.
1. A focus must be closed under prerequisites
Owner membership alone was the wrong visibility rule. The v1 deletion lane's critical path runs through
pderive-source-slot-binding, whose prerequisitenamespace-canonical-bindinglives in a lane the focus hid — so the page rendered the fourth link of the spine as ready, with its blocker invisible. Nothing was red; the page lied by omission.That's worse for a dispatched agent than for a person: a worker reads a ready-looking node, starts, and stalls on work the roadmap never mentioned.
Visibility is now lane membership ∪ the transitive prerequisite closure, reusing
std.graph.graph_reverse_reachable_membersover the roadmap's own edge relation rather than minting a second walk — the edge is already a dependency edge, reverse reachability from a lane node is exactly its ancestors, and the graph is acyclic by witness so the walk terminates by construction.The closure found five blockers, not the three visible by eye.
namespace-canonical-bindinghas its own prerequisites, so the lane was blocked on a namespace sub-chain of three. Manual inspection would have discovered those one stall at a time, each costing a wasted attempt.Pulled-in rows are named as prerequisites rather than counted as lane work, so the lane's real size stays honest at 17 (page shows 22). Focus label renamed to "the v1 deletion lane".
2. Durable node identity (stage I0)
A roadmap node's id is its authored slug (
RoadmapNodeId = NonEmptyStr), so a rename is structurally a delete plus a create: edges stop resolving, receipts name a node that no longer exists, and any attempt or session joined to it is orphaned.Not hypothetical — the 2026-07-27 authority refresh deleted the node carrying the only execution contract and no claim noticed.
The law is reused; the type deliberately is not
std.occurrence_identityalready states the right law: identity may not derive from filename, span, authored name, structural equality, or content hash.But
OccurrenceIditself is unusable here:A per-graph counter from zero. Insert a node at the front and every id after it renumbers; two concurrent branches mint the same integer. That is positional identity wearing an allocator's clothes — correct for occurrences inside one parse, wrong for an entity that must survive edits, reordering, regeneration and independent branches. What occurrence identity deliberately does not claim is persistence scope, and that is the only thing missing.
51 collision-resistant identities are minted and persisted, one per declared node, at authoring time. Nothing mints during render, compile or regen — a value re-derivable by evaluation would reintroduce the exact property this module removes. The slug stays readable and stops being identity.
Deliberately additive
No rendering, dependency, receipt or dispatch path changes. The v1-deletion recut can be authored against durable identities immediately, without waiting on a consumer migration and without its new nodes being rewritten a second time afterwards.
Two gates declared as owed, not assumed
Permanence is unprovable from a snapshot. Every witness here reads the current tree, so an identity silently replaced between base and head satisfies all of them — the corpus is structurally incapable of catching the exact edit this module forbids. The owed row lists admitted transitions (identity retained, added, tombstoned; slug changed while identity unchanged) and refused ones (identity replaced, retired id reused, two nodes sharing an id, one slug resolving to two ids). It needs a base-vs-head registry comparison, which the floor's merge-base diff already has the shape for.
Decomposition needs an explicit completion policy. Containment keeps pointers valid but cannot say whether a parent completes on direct acceptance, on all children, or both — and inferring it would silently close a parent whose children are unfinished. Lands with containment, a later stage.
Validation
Verified on the pre-split branch (identical content, cherry-picked cleanly with zero references to binding-only symbols, so it stands alone rather than merely applying):
gunbc compile --target dag— 0 blocking errorsROADMAP.mdregenerated from the authorityroadmap_identity(7, incl. the discriminatingidentity_is_persisted_not_positional),roadmap_focus(8, incl.no_visible_node_has_a_hidden_prerequisite),roadmap_authority,generated_artifact_drift,roadmap_page,doc_reachability(both homes),roadmap_emit,roadmap_documentRe-verifying against fresh binaries on this branch (main moved under #7415, which changed
cli_run.rs); I'll post the re-run.Review note
The identity half deserves the harder look. The focus closure is mechanically checkable and its witness is structural. Identity has a declared hole —
identity_is_persisted_not_positionalpins one literal value as a tripwire, and the real base-vs-head gate is written down as owed rather than built. If that gate should exist before the recut rather than after, that's the call to make now, since the recut is what will exercise it.🤖 Generated with Claude Code