Story: Generate shell command units per component
Table of Contents
This page documents a story in Sprint 25. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
1. Goal
The shell's per-entity command units are hand-written and hand-wired, and the shell has no part to receive generated ones. The ores.cpp.shell-command facet now emits one command unit from one entity model. Land that facet, give ores.shell a part per domain area, and generate the trading units into ores.shell.trading, so adding an entity's shell verbs becomes a codegen run that touches no shared file.
2. Status
| Field | Value |
|---|---|
| State | ABANDONED |
| Parent sprint | Sprint 25 |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-09-23 |
3. Acceptance
- The
ores.cpp.shell-commandfacet is in the template library, and its two archetypes write into the adapter part of the entity's own component. ores.shellis a composite whose parts includetrading, and the composite root and the part file lists regenerate with no difference.- The trading command units in
projects/ores.shell/trading/are codegen output, the hand-written units are gone from theapplicationpart, and the shell's trading surface answers as it did before. - The full preset build and the ctest suite are green. The units are verified against their models by a direct per-entity regeneration, because no component drift gate reaches an archetype that writes into another component's tree.
4. Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Land the shell-command facet and the ores.shell.trading part | DONE | 2026-09-16 | 2026-09-16 | Commit the five ores.cpp.shell-command templates with their output paths pointed at the new part, create projects/ores.shell/trading/ with its overview and build files, declare trading in the composite parts list, regenerate the composite root and the component file lists, and build the tree. |
| Generate the trading command units into ores.shell.trading | DONE | 2026-09-16 | 2026-09-16 | Opt the trading entities in, generate their command units into the new part, link the part into the application so the hand-written aggregator reaches them, delete the hand-written units the generated ones replace, and prove the surface with a build, the suites, and a live check through the shell. |
| Migrate the rates instruments to the generated protocol | ABANDONED | 2026-09-23 | Move the nine rates instruments off the shared hand-written instrument_protocol.hpp and onto the generated per-entity protocol, so their shell command units can be generated like the other 22. | |
| Give the trade model its field-group grammar | ABANDONED | 2026-09-23 | Teach the trade entity model to state which field group each column belongs to, and teach the shell-command generator to read it, so the trade command unit can be generated. | |
| Generate container add verbs in the shell command facet | ABANDONED | 2026-09-23 | The bond instrument add verb assembles a container across three services in one command: the issue, the instrument, and the engaged option or trs fact row. The facet renders one row from one entity model, so bond_instrument stays hand-written until the model and the facet can express the container. | |
| Retire the per-entity shell units the facet already generates | ABANDONED | 2026-09-23 | Task for: Generate shell command units per component | |
| Generate the compute and synthetic shell units | ABANDONED | 2026-09-23 | Task for: Generate shell command units per component | |
| Account for every remaining hand-written shell unit | ABANDONED | 2026-09-23 | Task for: Generate shell command units per component |
5. Decisions
- One part per domain area. The generated unit lands in the part named after the domain it projects, so only the adapter that owns the domain links the domain's protocol types. This is the fractal naming rule in Component architecture.
- The units replace, not join. The hand-written unit for an entity is deleted in the same change that generates its replacement, so the shell never carries two units for one submenu.
- A column states who supplies its value (2026-09-16). The add verb reads one
positional per user-supplied column, and the entity model did not say which
columns those were.
:supplied_by:fills that gap. The four values areuser,minted,session_partyandunset.useris the default and consumes the next positional. A uuid primary key defaults tominted, since the client owns the key of a row that does not exist yet; any other key defaults touser, since a text key such as a code is the caller's to choose.session_partyreads the acting party from the session and fails when the account has no default party.unsetleaves the member at its default. The generator turns the annotation into the{{user_supplied_count}}arity and the{{#is_*}}sections, so the template never branches on a supply source it does not expect. The tenant is not a column and takes no annotation; the template names it and fills it from the session. The arity counts the primary key's members once each, because the key dict mirrors its first member at the top level for the consumers that read a single-column key. Counting the mirror as well would count a single-column key twice, and reading only the mirror would skip a compound key's later members; either way the arity would disagree with the positionals the template reads. - The trading component is never regenerated whole (2026-09-16).
tradeandtrading-cppsit outsideKNOWN_DRIFT_FREEby the decision of 2026-09-06 in Entity classification and drift baseline: ores.trading, which measured 37 clobbered outputs at bind and left the registry entry to an estate rework story. The drift has grown since. A component-wide regeneration oftrading-cpptoday rewrites 132 tracked files and materialises 44 untracked ones, none of them this wave's. The hazard for this wave is bounded, because the shell-command facet writes only intoprojects/ores.shell/trading/and theshellentry is drift-free. The rule is therefore: generate trading units one entity at a time, and never runcheck_component_drift.py --component trading-cppon this branch. - The component aggregator stays hand-written (2026-09-16). One aggregator per
component wires the units to the REPL host. It lives in the application part
and carries one include and one
register_commandscall per entity. A generated aggregator needs a render-time list of the component's opted-in entities, which is the pattern the generator already uses for_COMPONENT_FILES_TEMPLATES, and the facet has no component-scoped archetype for it today. The wiring cost is one line per entity, so the archetype is not worth its complexity yet. The consequence for the part is that the application linksores.shell.<component>.lib; a static library'sPRIVATEdependency still reaches the executables. - A new facet regenerates the physical-space inventories (2026-09-16). The
facet and archetype tables in the parent pages are generated from the
children's frontmatter, and a facet that is absent from its parent's table is
invisible to anything that reads the catalogue. Landing
ores.cpp.shell-commandtherefore includesregenerate_physical_space_inventories.py. The same run cleared eighteen pages that earlier waves had left stale. - The drift gate for the units is a direct regeneration (2026-09-17). The
acceptance criterion first read "the codegen drift gate covers the new
part". It cannot hold as written.
check_component_drift.py --component shellregenerates the composite root alone, and the units are outputs ofores.tradingentity models, so thetradinggate is the one that would reach them, and this branch forbids it. The criterion now states the gate that exists: regenerate each opted-in entity atores.cpp.shell-commandand confirm the tree is unchanged. A general check for an archetype that writes into another component's tree needs its own task. - A container entity stays hand-written (2026-09-17). The facet renders one
row from one entity model, so it cannot express an add verb that builds the
bond_issue, the instrument, and the engaged fact row in one command.bond_instrumentis de-opted and its hand-written unit returns to the application part. Task CADAD363 owns the facet change that would readmit it.
6. Out of scope
- The other adapter components.
ores.qtand the remaining domains keep their hand-written units until their own tasks land. - Declaring the kind on the other adapter parts.
ores.shelldeclares#+component_kind: adapteron its parts, andores.qtdoes not. Theores.qtparts are not reachable from a catalogue slug, so the undeclared kind is inert there today; it stops being inert the dayores.qtjoins the catalogue. Follow-up task. - The adapter part build files. An adapter part carries hand-authored CMake by
design. The
#+component_kind: adapterdeclaration keeps codegen to the two file lists, so a regeneration never writes over the link libraries. See Component architecture.
7. Result
Abandoned at sprint 25 close. Sprint 26 replaces the codegen approaches tried so far with one method: for each module, sync codegen and clear its drift. Sprint 26 opens new, clean stories for each module. This story is not carried forward.