60%

Walking Skeleton — Rules <-> Registry <-> Lint spine

Goal

  • Prove the whole spine integrates before deepening any leg.
  • Thinnest end-to-end: registry module skeleton + minimal lint + one rule referencing it, all green together.

Implementation Sketch

  • Create ori_ir::canon::consumers with a pure-data ConsumerEntry and a CANON_CONSUMERS const listing the 5 real consumers (ori_eval, ori_arc, ori_llvm::evaluator, ori_compiler, oric).
  • Add a minimal cargo-metadata lint that loads CANON_CONSUMERS + cargo metadata, asserts every registered consumer has a dependency edge on ori_ir, and exits 0 on the current tree.
  • Add one stub rule (registration-required) to impl-hygiene.md citing the registry + lint as its mechanical backing.
  • Model on ori_registry (pure-data SSOT); no logic in the registry, data only.

Spec References

missions.md §ori_ir (registry home; avoids circular deps); canon.md §7.5; impl-hygiene.md PHASE-54.

Items

  • Create ori_ir::canon::consumers module with a pure-data ConsumerEntry struct carrying a crate_name: &'static str field (the real Cargo package name — the checkable cargo-metadata unit, distinct from any descriptive/module-qualified label) plus a CANON_CONSUMERS const listing the 5 named consumers (ori_eval, ori_arc, ori_llvm, ori_compiler, oric; a module-qualified detail like ori_llvm::evaluator stays a doc-comment/description field only, never the checked identity). Mirror ori_registry::burden::{TypeId, VariantId, FnSym}’s id+get() shape with a single entry_for(crate_name) -> Option<&ConsumerEntry> query (no parallel lookup functions), plus a minimal #[test] asserting CANON_CONSUMERS.len() == 5 and every entry resolves via entry_for — the full pool-subset-vs-real-deps unit test stays s-e731d59b’s w-e318d6df deliverable.
  • Extend the EXISTING compiler_repo/scripts/crate-dag-lint.py (already reads cargo metadata --no-deps into the {crate: {deps}} graph via _read_workspace_graph, already exposes --self-test/--json, currently wired into nothing) with a new check_registered_consumers(graph, registry) function reading CANON_CONSUMERS, asserting every registered consumer’s crate_name has a dependency edge on ori_ir; green on the current tree. Land in compiler_repo/scripts/ (the tooling-scope carve-out home per compiler-ops.md), never a new wrapper-root scripts/ script — do not write a second cargo-metadata-reading script. Reconcile the section’s sc-93a0660b1f probe_ref (currently python -m scripts.consumer_ssot_lint --check) to invoke this extended crate-dag-lint.py entry point at execution time.
  • Create ori_ir::canon::consumers module with ConsumerEntry pure-data struct + CANON_CONSUMERS const (5 consumers, name only for skeleton).
  • Add minimal cargo-metadata lint (wrapper scripts/) reading CANON_CONSUMERS + cargo metadata; green on current tree.
  • Add the registration-required rule STUB to impl-hygiene.md citing the registry + lint.

Fresh intel (regenerated)

The walking skeleton’s own plan-family research already undercounted its subject. Section s-e731d59b (consumer-ssot-registry, the very next sibling) explicitly corrected the topology once — “ori_repr and ori_fmt are NOT canon consumers … do not register them” — but the terrain (§2) shows the correction did not go far enough: ori_types and ori_patterns also import ori_ir::canon today (MonoInstanceId in ori_types; CanId + SharedCanonResult in ori_patterns::value::composite::function_value.rs), and neither is in the plan’s 5-name CANON_CONSUMERS list (ori_eval, ori_arc, ori_llvm::evaluator, ori_compiler, oric). Once the negative-fixture lint (s-e26ee405, failure mode (a): “a crate depends on ori_ir::canon with no CANON_CONSUMERS entry”) is built, it will fire immediately against the REAL tree, not just a synthetic fixture, unless the registry is widened or these two are ruled exempt with a stated reason — a decision the plan has not yet made.

Three decisions this package forces before any code is written:

  1. Widen the registration question from “does it depend on ori_ir::canon” to “does it consume MEANING or just carry an opaque ID.” ori_types::MonoInstanceId and ori_patterns::CanId/SharedCanonResult are handle-only usages (index/key passing for monomorphization + closure bodies), not tree-walking re-derivation — PHASE-54’s actual concern. The registry’s per-pool-subset design (s-e731d59b) is the right shape to capture this, but its work item enumerates only the 5 named crates; it needs to explicitly classify ori_types and ori_patterns (register with an ID-only pool subset, or state the exemption and why) rather than silently leaving them unregistered for the lint in s-e26ee405 to trip over later.
  2. cargo metadata resolves crates, not modules — “ori_llvm::evaluator” is not a checkable unit. The sketch names one of the 5 consumers at module granularity, but the dependency-edge check the lint performs is necessarily crate-level (ori_llvm as a whole already reaches ori_ir::canon through arc_emitter, function_compiler, and monomorphize, not only evaluator/). ConsumerEntry needs a crate_name field distinct from any descriptive/doc label, or the module-qualified name is cosmetic only.
  3. Do not write a second cargo-metadata-reading Python script. compiler_repo/scripts/crate-dag-lint.py already exists, already reads cargo metadata --no-deps into exactly the {crate: {deps}} graph shape the lint needs (_read_workspace_graph), and is currently wired into nothing (not test-all.sh, not full-check.sh, not lefthook) — it is dead, unexploited tooling sitting one function away from doing this section’s job. Extending it (a) avoids LEAK:algorithmic-duplication/SSOT-18 (two independent cargo metadata readers in the same crate), (b) gets an existing unwired lint into CI as a side effect, and (c) correctly places the artifact in compiler_repo/scripts/ (the exempt-tooling home per compiler-ops.md line 15/18) rather than the wrapper-root scripts/ the sibling section s-e26ee405 names — a location mismatch worth fixing now, before a second location is committed to.

DIG DEEPER

scripts/intel-query.sh plan-status canonical-consumer-boundary
scripts/intel-query.sh dag-ascii canonical-consumer-boundary
sed -n '1,50p' compiler_repo/scripts/crate-dag-lint.py

(full dossier: walking-skeleton—s-598f5119.intel.md)