The drift gate covers only the registry, and misses output written into another component's tree
Table of Contents
This page is a capture in the inbox bucket of the product backlog — a pre-sprint idea, not yet pulled into a sprint as a story.
1. What
Widen the drift gate so it covers generated output that no run of a component's own models can reach. Two holes are open, and they share one cause: the gate's coverage is the registry, and the registry lists model-bearing components, not generated files.
1.1. Hole A. An archetype that writes into another component's tree**
Two routes are open. The generator can record, per archetype, the component that
owns each output path, so a run of the shell component regenerates every file
under projects/ores.shell/ no matter which model produced it. Or the registry
can name the model sets a component's gate must cover, so --component shell
regenerates the 23 trading entities that route into it, under a rule that writes
only inside the shell tree.
1.2. Hole B. Generated output under a component the registry does not list**
Add a dry-run sweep over every catalogue component, and fail only when a file the branch touched would change. Dry run, because the in-place hazard is recorded elsewhere and a run on a branch behind main overwrites files. Touched files only, because a full comparison would fail on the pre-existing drift that belongs to each component's own clean-up.
That sweep would catch a cross-component rename, but only once someone ran it, so add the cheaper half as well: a check that every symbol a model's pasted code block references resolves in the tree. A rename then fails at review, without a regeneration.
2. Why
The gate is the only mechanism that catches a model edited without a regeneration, and it works by regenerating a component and diffing the tree. An archetype whose output path points into a different component's tree breaks that assumption in both directions. The owning component's gate never touches the file, and the receiving component's gate has no model that produces it.
The measured case for hole A is the trading command units.
check_component_drift.py --component shell reports "Regenerating 1 models" and
writes one file, the ores.shell composite root. The 23 units come from
ores.trading entity models, so the run cannot reach them. Regenerating the
trading component would reach them and 176 other files as well, none of them
that wave's, which is why the wave forbids it. The units passed only because each
of the 23 was regenerated from its own model by hand and the tree came back
clean.
The measured case for hole B is the ores.service clean-up. It moved three
workflow header names from ores.service into ores.workflow.api, and
ores.refdata keeps a workflow override as a source block pasted into a
generated handler. The generated file was edited by hand and the model was left
naming the deleted symbols, so the next refdata regeneration would have reverted
the edit and broken the build. check_component_drift.py --all was green, and CI
was green on every head, because --all is the registry and refdata is not in
it. An independent verifier found it by running the per-component check.
The exposure is measured. projects/ holds 5,829 files carrying the generated
marker, and the gate covers the 937 under the 8 registry components, so 4,892
have no gate. 68 model files paste hand-written C++ into generated output through
:implements blocks, and 40 of them are refdata's. A cross-component rename can
break one of those blocks with no compile error, because the block is compiled
only when its component is regenerated.
This is a general hole, not a shell or a service quirk. Any facet that writes into another component's tree has hole A, any rename that crosses a component boundary can hit hole B, and the number of such facets grows with the adapter parts.
3. References
projects/ores.codegen/scripts/check_component_drift.py— the gate, itsKNOWN_DRIFT_FREEregistry, and its untracked-file snapshot.projects/ores.codegen/scripts/regenerate_cmake_component_files.py— the sibling gap, a writer with no compass subcommand.projects/ores.refdata/modeling/ores.refdata.party.organd the generatedores.refdata.core/messaging/party_handler.hpp— the measured case for hole B, fixed at source by the clean-service task.doc/agile/versions/v0/sprint_26/clean-service/task_clean_service.org— items P03 and P05, where the defect and its fix are recorded.- Generate the trading command units into ores.shell.trading — the task that measured hole A and covered its own units by hand.
4. See also
- check_component_drift.py corrupts a branch that is behind main — the other hazard in the same script, which bounds what a wider gate may do.
- Component Clean Standard — items P03 and G02, the cross-component rename and the hand-edited generated file this hole lets through.