Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ Parked context (non-dispatchable): [compiler algorithm survey](docs/plans/compil

The graph has ten lanes: SCM compatibility · namespace · P-derive · observation · placement · v1 exit (four finish lines: compiler fixed point, interpreter deleted, products v1-free, zero hand-maintained Rust) · shell · roadmap runtime · fleet/hygiene · hermetic toolchain. The three independent SCM R0 models and P−1 evidence carrier are landed prerequisites; R1 compatibility-shape extraction is the active SCM slice, while semantic-kernel and later product lanes remain parked. The toolchain pin model is an independent root. The P-derive receipt feeds the emitter fixed point, so the v1 chain nests under it. Node fields define boundary, first slice, RED control, exclusions, owner, and handback.

**Focused view — the v1 → v2 emitter exit lane.** 29 active deliverable(s) in other lanes are declared in the authority and hidden here; nothing is deleted or parked by focusing. Clear `roadmap_focus_selection` in `dag/gunbc/roadmap_authority.dag` to restore the full page. 5 of the rows below are NOT lane deliverables — they are prerequisites pulled in from other lanes because lane work is blocked on them: namespace-canonical-binding, v1-seed-honesty-decision, namespace-legacy-choice-retirement, namespace-v2-import-completion, namespace-bound-only-consumers. Hidden lanes remain readable through their carriers: [docs/plans/dag-scm-design.md](docs/plans/dag-scm-design.md) · [dag/gunbc/plans/cli_run_hollowing_plan.dag](dag/gunbc/plans/cli_run_hollowing_plan.dag) · [docs/plans/progress-observation-design.md](docs/plans/progress-observation-design.md) · [docs/plans/ci-two-tier-placement-redesign.md](docs/plans/ci-two-tier-placement-redesign.md) · [docs/plans/shell-to-dag-residual-census-and-arc-completion.md](docs/plans/shell-to-dag-residual-census-and-arc-completion.md) · [docs/plans/roadmap-workspace-ux-plan.md](docs/plans/roadmap-workspace-ux-plan.md) · [docs/plans/host-converge-inventory.md](docs/plans/host-converge-inventory.md) · [docs/runbooks/bmc-redfish-operator-access.md](docs/runbooks/bmc-redfish-operator-access.md) · [docs/plans/host-effect-orchestration.md](docs/plans/host-effect-orchestration.md) · [docs/plans/gunbc-served-dashboard-design.md](docs/plans/gunbc-served-dashboard-design.md) · [docs/plans/fleet-acceptance-criteria.md](docs/plans/fleet-acceptance-criteria.md) · [docs/plans/hermetic-tool-provisioning-design.md](docs/plans/hermetic-tool-provisioning-design.md).
**Focused view — the v1 → v2 emitter exit lane.** 29 active deliverable(s) in other lanes are declared in the authority and hidden here; nothing is deleted or parked by focusing. Clear `roadmap_focus_selection` in `dag/gunbc/roadmap_authority.dag` to restore the full page. 6 of the rows below are NOT lane deliverables — they are prerequisites pulled in from other lanes because lane work is blocked on them: namespace-canonical-binding, v1-seed-honesty-decision, namespace-legacy-choice-retirement, namespace-v2-import-completion, namespace-bound-only-consumers, namespace-import-deletion. Hidden lanes remain readable through their carriers: [docs/plans/dag-scm-design.md](docs/plans/dag-scm-design.md) · [dag/gunbc/plans/cli_run_hollowing_plan.dag](dag/gunbc/plans/cli_run_hollowing_plan.dag) · [docs/plans/progress-observation-design.md](docs/plans/progress-observation-design.md) · [docs/plans/ci-two-tier-placement-redesign.md](docs/plans/ci-two-tier-placement-redesign.md) · [docs/plans/shell-to-dag-residual-census-and-arc-completion.md](docs/plans/shell-to-dag-residual-census-and-arc-completion.md) · [docs/plans/roadmap-workspace-ux-plan.md](docs/plans/roadmap-workspace-ux-plan.md) · [docs/plans/host-converge-inventory.md](docs/plans/host-converge-inventory.md) · [docs/runbooks/bmc-redfish-operator-access.md](docs/runbooks/bmc-redfish-operator-access.md) · [docs/plans/host-effect-orchestration.md](docs/plans/host-effect-orchestration.md) · [docs/plans/gunbc-served-dashboard-design.md](docs/plans/gunbc-served-dashboard-design.md) · [docs/plans/fleet-acceptance-criteria.md](docs/plans/fleet-acceptance-criteria.md) · [docs/plans/hermetic-tool-provisioning-design.md](docs/plans/hermetic-tool-provisioning-design.md).

- [ ] **Have exactly one rule decide what every name refers to** — Work out the candidates by looking outward from where the name was written, then call one shared decision function once. Not several mechanisms racing to answer. Why: Several lookup mechanisms exist today and they can disagree about the same name, which is how a program silently ends up meaning something other than what was written. [authority](docs/plans/namespace-unique-on-chain-operational-plan.md)
- [ ] **Make the rest of the compiler use only that one answer** — Type checking, call lookup, and argument-count checks all read the single decided answer. When the answer is 'unresolved' or 'ambiguous', they stop rather than carrying on with a guess. Why: While parts of the compiler still do their own lookup, the one shared rule is advisory rather than authoritative. [authority](docs/plans/namespace-unique-on-chain-operational-plan.md)
- [ ] **Do the same for imports** — Every place the newer compiler resolves an import or a dotted name reads the single decided answer, before the old mechanisms are deleted. Why: If imports are left behind, they become a second source of truth about what a name means — exactly the problem this whole chain exists to remove. [authority](docs/plans/namespace-unique-on-chain-operational-plan.md)
- [ ] **Delete every other way of deciding what a name means** — Once all ordinary consumers use the one rule, remove the older mechanisms: name-string matching, walking up the parent chain, the global pool of bare names, and the import retry. Why: Leaving them in place means one name can still get different answers from different code paths. [authority](docs/plans/namespace-unique-on-chain-operational-plan.md)
- [ ] **Delete the import lines, and the grammar that allows them** — Every import line goes, and import stops being something the language can express at all: writing one becomes a parse error. What a file depends on is worked out from the names it actually mentions. Why: While import still parses, a file can declare one set of dependencies while its references need another, and the two can disagree. That is the second source of truth this whole chain exists to remove, and it survives every earlier step. [authority](docs/plans/namespace-unique-on-chain-operational-plan.md)
- [ ] **Attach those requirements to the right type, not just the right position** — Connect each recorded slot to the exact declaration it belongs to, using the name resolution work above. Why: Matching by position or by short name attaches a requirement to the wrong type whenever two types share a name or the order shifts. [authority](docs/plans/v2-self-hosting.md)
- [ ] **Generate those requirements from one place in the new generator** — The new Rust generator reads the record and the mapping through its existing path, rather than needing a special case per shape. Teaching the old generator the same thing is allowed only where the build-itself path is genuinely stuck without it, and even then it reads the same answer rather than working it out again. Why: Every shape currently needing its own hand-written patch is a shape someone has to fix again next time — and doing it in the generator we are deleting means doing it twice. [authority](src/v2/compiler/05_emit.dag) — requires all: pderive-source-slot-binding, v2-emitter-direct-rust-door
- [ ] **Count the compiler errors before and after, to prove it helped** — Re-run the same set of modules and compare the error counts and kinds. Why: A fix that makes one example compile can leave the real number untouched; without the count we would not know. [authority](docs/plans/v2-self-hosting.md)
Expand All @@ -26,13 +27,12 @@ The graph has ten lanes: SCM compatibility · namespace · P-derive · observati
- [ ] **Build the products with the old runner left out entirely** — A rehearsal: build without the old runner included at all, and run the products and the build both cold and warm. Why: Proves nothing still reaches for it, which is the thing that actually makes deletion safe. [authority](dag/gunbc/v1_deletion_plan.dag)
- [ ] **Delete the old runner** — Delete it and everything now unreachable, in one change, once the rehearsal above holds. This is about the runner only — not the rest of the old code, and not hand-written Rust generally. Why: Removes the second, slower way of running everything, and with it the possibility of the two disagreeing. [authority](dag/gunbc/v1_deletion_plan.dag) — requires all: v1-interpreter-quarantine, v1-emitter-delete
- [ ] **Build and run every product without the old code present** — With the old runner gone and the hand-written queue worked through, the products build and run with the old directory absent. Anything that must stay gets an explicit reason. Why: Separates 'the products no longer need the old code' from 'the old runner is gone', so neither claim waits on the other's leftovers. [authority](dag/gunbc/v1_deletion_plan.dag) — requires all: v1-interpreter-delete, v1-hand-queue-drain
- [ ] **Get hand-written Rust in this repository down to zero** — Every Rust file we track is either generated from a description or deleted. Hand-maintaining Rust is a stage we are passing through, not where we stop. Why: Every hand-written file is a permanent tax: the behaviour and its description have to be kept in step by hand, forever. [authority](dag/gunbc/v1_deletion_plan.dag) — requires all: v1-products-v1-free, caret-parse-smoke-seed-growth-justification, v1-test-migration, v1-seed-honesty-decision, namespace-legacy-choice-retirement
- [ ] **Move one hand-written test check into the substrate and delete the original** — Re-express the check, prove it behaves identically, then delete the hand-written version rather than keeping both. Why: Keeping both means two things to fix whenever the rule changes, and they will drift. [authority](docs/plans/v2-self-hosting.md)
- [ ] **Keep an honest list of what still depends on the old compiler** — Every module is either generated by the new compiler or explicitly kept on the old one with a reason and a plan to move. Why: Turns a vague sense of 'mostly done' into a finite, countable list someone can work through. [authority](dag/gunbc/v1_deletion_plan.dag)
- [ ] **Get hand-written Rust in this repository down to zero** — Every Rust file we track is either generated from a description or deleted. Hand-maintaining Rust is a stage we are passing through, not where we stop. Why: Every hand-written file is a permanent tax: the behaviour and its description have to be kept in step by hand, forever. [authority](dag/gunbc/v1_deletion_plan.dag) — requires all: v1-products-v1-free, caret-parse-smoke-seed-growth-justification, v1-test-migration, v1-seed-honesty-decision, namespace-legacy-choice-retirement, namespace-import-deletion
- [ ] **Record which generator actually produced each result** — Every result says which generator made it and how far it got, as two separate facts. Claiming the new generator made something, without the evidence that it did, has to be impossible to write down. Why: Right now everything is made by the old generator, and the count we track reads as if the new one made it. We are measuring the wrong thing, so we cannot tell how far along we are. [authority](src/v2/compiler/self_host/frontier.dag)
- [ ] **Have the new compiler write out Rust source itself** — Read the source, work out the types, translate, and write real Rust to disk without the old generator deciding any of it. The old runner may still be what executes this; it just stops making the decisions. Why: Until the new compiler can write Rust on its own, every improvement has to go through the generator we are trying to delete. [authority](src/v2/compiler/self_host/candidate_generation.dag)
- [ ] **Regenerate one already-tested module with the new generator** — Take a module that already has working tests, change only which generator produces it, and confirm it still passes the same tests including the deliberately broken one. Why: Proves the whole approach works on something real before anyone builds more on the assumption that it does. [authority](src/v2/compiler/self_host/frontier.dag)
- [ ] **Build the new generator with itself, then do it again** — The generator, and only the generator: it writes and builds a native version of itself, that native version writes and builds another, and the second one needs nothing from the old compiler. The other parts of the compiler are still produced the old way at this point. Why: This is the moment the new generator stops being something we produce and becomes something that produces itself. [authority](src/v2/compiler/self_host/frontier.dag) — requires all: v2-emitter-first-behavioral-module, pderive-error-movement-receipt
- [ ] **Keep an honest list of what still depends on the old compiler** — Every module is either generated by the new compiler or explicitly kept on the old one with a reason and a plan to move. Why: Turns a vague sense of 'mostly done' into a finite, countable list someone can work through. [authority](dag/gunbc/v1_deletion_plan.dag)
- [ ] **Compute things once and reuse the result** — Tests and the compiler share one store keyed by what went into the result, with a size limit and a clear answer when something is missing. Why: Everything is currently recomputed from scratch on every run, and the one-off caches that exist do not help each other. [authority](docs/plans/witness-realization-plan.md)
- [ ] **Prove old and new agree, by running them, not by waiting** — Run the whole test suite cold on both, compare compile results, check the self-rebuild, and record each family's outcome. Why: Confidence that comes from 'it has been fine for a while' is not evidence; this replaces it with runs someone chose to do. [authority](dag/gunbc/v1_deletion_plan.dag) — requires all: v1-materialization-kernel, v1-emitter-fixed-point
- [ ] **Work through the remaining hand-written files the products need** — Move the hand-written files the shipping tools still depend on. Anything that genuinely has to stay gets an explicit reason. Why: The main command-line tool is itself one of these files, so 'the products still build' is not something we can honestly claim until this is done. [authority](dag/gunbc/v1_deletion_plan.dag)
Expand Down
35 changes: 35 additions & 0 deletions dag/gunbc/roadmap_authority.dag
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,21 @@ fn declared_roadmap_nodes() -> List<RoadmapNode> {
handback: "The zero-remaining inventory plus proof the compiler still behaves the same."
)
),
active_large(
identity: "rn_SG8EVJAQXVGP7V5G8BG8G8YVJQ",
id: "namespace-import-deletion",
owner: "namespace",
path: "docs/plans/namespace-unique-on-chain-operational-plan.md",
t: fields(
headline: "Delete the import lines, and the grammar that allows them",
boundary: "Every import line goes, and import stops being something the language can express at all: writing one becomes a parse error. What a file depends on is worked out from the names it actually mentions.",
displaced_cost: "While import still parses, a file can declare one set of dependencies while its references need another, and the two can disagree. That is the second source of truth this whole chain exists to remove, and it survives every earlier step.",
first_slice: "Delete the import lines from one subtree whose references already reach everything it needs, and show that subtree still compiles from its references alone.",
red_control: "A file that still needs an import to reach something is named and refused, never quietly left holding it. Once the grammar goes, a file containing an import line fails to parse rather than being ignored.",
out_of_scope: "No new way of writing dependencies is introduced; this step only removes the old one. Deleting imports ahead of a proven reference-only closure is the failure this step exists to prevent, not a shortcut through it.",
handback: "A corpus with no import lines, a grammar with no import production, and a run showing that an authored import is now a parse error."
)
),

active_small(
identity: "rn_E414RDCS728VRFA9QZ0NX76SYN",
Expand Down Expand Up @@ -1460,6 +1475,8 @@ fn declared_roadmap_edges() -> List<RoadmapEdge> {
edge(child: "v1-zero-hand-maintained-rust", parent: "v1-test-migration"),
edge(child: "v1-zero-hand-maintained-rust", parent: "v1-seed-honesty-decision"),
edge(child: "v1-zero-hand-maintained-rust", parent: "namespace-legacy-choice-retirement"),
edge(child: "namespace-import-deletion", parent: "namespace-legacy-choice-retirement"),
edge(child: "v1-zero-hand-maintained-rust", parent: "namespace-import-deletion"),

edge(child: "shell-effectplan-to-bash", parent: "shell-typed-invocation"),
edge(child: "shell-gate-migration", parent: "shell-effectplan-to-bash"),
Expand Down Expand Up @@ -1619,6 +1636,24 @@ fn roadmap_acceptance_receipts() -> List<RoadmapAcceptanceReceipt> {
accepted_by: "operator",
accepted_on: "2026-07-29",
},
RoadmapAcceptanceReceipt {
node: nid(s: "v1-test-hygiene-producer-retirement"),
criteria_digest: chash(s: "8f77a4b2e0ee3387"),
red_control: RedControlExecuted {
witness_module: "test.claim.test_module_hygiene_hand_rust_equivalence_witness",
witness_fn: "test_module_hygiene_equivalence_unparsable_refuse_discriminator_holds",
executed_on: "2026-07-29",
},
handback: HandbackDelivered {
first_artifact: "dag/gunbc/test_module_hygiene.dag",
further_artifacts: [
"dag/test/claim/test_module_hygiene_hand_rust_equivalence_witness_test.dag",
"src/v1/stage0/src/cli_run/test_module_hygiene_bridge.rs",
],
},
accepted_by: "operator",
accepted_on: "2026-07-29",
},
]
}

Expand Down
10 changes: 9 additions & 1 deletion dag/test/claim/roadmap_authority_test.dag
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,11 @@ test fn witness_every_declared_row_is_complete_unsigned_ticket() -> Bool {

test fn witness_active_set_is_declared_minus_accepted() -> Bool {
let accepted = accepted_roadmap_node_ids()
count(accepted) == 7
count(accepted) == 8
&& (accepted |> any(a => a == "namespace-occurrence-transport"))
&& (accepted |> any(a => a == "pderive-typesafe-nullary-reflection"))
&& (accepted |> any(a => a == "pderive-static-supplemental-contract"))
&& (accepted |> any(a => a == "v1-test-hygiene-producer-retirement"))
&& (accepted |> any(a => a == "2-scm-git-upstream-model"))
&& (accepted |> any(a => a == "2-scm-mercurial-upstream-model"))
&& (accepted |> any(a => a == "2-scm-pijul-upstream-model"))
Expand Down Expand Up @@ -506,6 +507,13 @@ test fn witness_v1_exit_fan_in_matches_finish_line_split() -> Bool {
&& edge_exists(child: "v1-zero-hand-maintained-rust", parent: "namespace-legacy-choice-retirement")
}

test fn witness_import_deletion_gates_zero_hand_maintained_rust() -> Bool {
id_exists(id: "namespace-import-deletion")
&& active_id_exists(id: "namespace-import-deletion")
&& edge_exists(child: "namespace-import-deletion", parent: "namespace-legacy-choice-retirement")
&& edge_exists(child: "v1-zero-hand-maintained-rust", parent: "namespace-import-deletion")
}

test fn witness_shell_roadmap_and_fleet_chains_are_explicit() -> Bool {
edge_exists(child: "shell-effectplan-to-bash", parent: "shell-typed-invocation")
&& edge_exists(child: "shell-gate-migration", parent: "shell-effectplan-to-bash")
Expand Down
Loading
Loading