aw-spec-cli 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/assets/.appweaver/DESIGN-FORMAT.md +63 -31
  2. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +62 -14
  3. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +10 -6
  4. package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +9 -1
  5. package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +21 -0
  6. package/assets/.appweaver/orchestrator/orchestrator.py +23 -1
  7. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +1 -1
  8. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +22 -13
  9. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +1 -1
  10. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +6 -4
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +399 -131
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +1 -1
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +199 -38
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +1 -1
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +159 -25
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +20 -9
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +1 -1
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +183 -40
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +1 -1
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +311 -36
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +221 -40
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +217 -174
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +494 -109
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +1 -1
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +83 -18
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +319 -0
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +1 -1
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +29 -23
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +143 -34
  30. package/assets/.appweaver/scripts/bash/aw-inventory.sh +15 -0
  31. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +878 -51
  32. package/assets/.appweaver/scripts/bash/aw-xref.sh +3 -0
  33. package/assets/.appweaver/scripts/bash/common.sh +28 -1
  34. package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +15 -0
  35. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +868 -51
  36. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +3 -0
  37. package/assets/.appweaver/scripts/python/aw-inventory.py +1832 -65
  38. package/assets/.appweaver/scripts/python/aw-xref.py +500 -81
  39. package/assets/.appweaver/templates/delta-design-template.md +1 -1
  40. package/assets/.appweaver/templates/delta-domain-model-template.md +1 -1
  41. package/assets/.appweaver/templates/delta-spec-template.md +4 -3
  42. package/assets/.appweaver/templates/design-template.md +56 -40
  43. package/assets/.appweaver/templates/okf-concept-template.md +11 -1
  44. package/assets/.appweaver/templates/proposed-inventory-template.md +127 -0
  45. package/assets/.appweaver/templates/source-map-template.md +5 -3
  46. package/assets/.appweaver/templates/spec-template.md +24 -21
  47. package/assets/.appweaver/templates/ui-delta-design-template.md +1 -1
  48. package/assets/.appweaver/templates/ui-design-template.md +11 -7
  49. package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +4489 -361
  50. package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +2274 -125
  51. package/assets/guardrails/domain/healthcare/auditing.domain.md +3 -3
  52. package/assets/guardrails/domain/pharma/auditing.domain.md +3 -3
  53. package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +1 -1
  54. package/assets/guardrails/technology/dotnet-webapi/auditing.md +1 -1
  55. package/assets/guardrails/technology/dotnet-webapi/authorization.md +1 -1
  56. package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +1 -1
  57. package/assets/guardrails/technology/dotnet-webapi/cqrs.md +1 -1
  58. package/assets/guardrails/technology/dotnet-webapi/data-classification.md +1 -1
  59. package/assets/guardrails/technology/dotnet-webapi/eventing.md +1 -1
  60. package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
  61. package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +1 -1
  62. package/assets/guardrails/technology/dotnet-webapi/persistence.md +1 -1
  63. package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +1 -1
  64. package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +1 -1
  65. package/assets/guardrails/technology/nextjs/accessibility-implementation.md +1 -1
  66. package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +1 -1
  67. package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +1 -1
  68. package/assets/guardrails/technology/nextjs/auth-session.md +1 -1
  69. package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
  70. package/assets/guardrails/technology/nextjs/design-system.md +1 -1
  71. package/assets/guardrails/technology/nextjs/error-handling.md +1 -1
  72. package/assets/guardrails/technology/nextjs/forms-and-validation.md +1 -1
  73. package/assets/guardrails/technology/nextjs/i18n.md +1 -1
  74. package/assets/guardrails/technology/nextjs/performance-budgets.md +1 -1
  75. package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
  76. package/assets/guardrails/technology/nextjs/state-management.md +1 -1
  77. package/package.json +1 -1
@@ -8,34 +8,49 @@ design document — not the engine. The authoring template is
8
8
 
9
9
  ## The contract
10
10
 
11
- A feature's design is **one document**, living beside its spec:
11
+ A feature's design is **one document**, living beside its spec — and the domain it
12
+ elaborates lives one level up, at the bounded context:
12
13
 
13
14
  ```
14
- specs/<Aggregate>/
15
- spec.md # behaviour (tech-agnostic) — WHAT / WHY — the source of truth
16
- backend-plan.md # structure & decisions (tech-agnostic HLD) — the MEDIATOR
17
- changes/ # open + archived change records (delta-spec + delta-design)
15
+ specs/<Context>/
16
+ domain-model.md # STRUCTURE — aggregates, entities, invariants (BR-nnn) — shared
17
+ <Aggregate>/
18
+ spec.md # BEHAVIOUR (tech-agnostic) — WHAT / WHY
19
+ backend-plan.md # structure & decisions (tech-agnostic HLD) — the MEDIATOR
20
+ changes/ # open + archived change records (delta-spec + delta plans)
18
21
  ```
19
22
 
20
- `backend-plan.md` is the **mediator** between the spec and the generated code:
23
+ **TWO AUTHORITIES, NOT ONE.** `domain-model.md` is the canonical definition of what the
24
+ domain *is*; `spec.md` is the canonical definition of what the system must *do*. They are
25
+ separate files because several specs in a context act on one aggregate, so a per-spec copy
26
+ of an invariant drifts. The spec cites invariants by id (`BR-nnn`) and never restates them.
27
+
28
+ `backend-plan.md` is the **mediator** between those two and the generated code:
21
29
 
22
30
  ```
23
- spec.md ──▶ backend-plan.md ──▶ code (+ tests)
24
- behaviour structure & generated by `implement`
25
- FR/BR/US/SC decisions (HLD) (stack bound at generation time)
31
+ domain-model.md ─┐
32
+ structure ├─▶ backend-plan.md ──▶ code (+ tests)
33
+ BR-nnn │ structure & generated by `implement`
34
+ spec.md ──┘ decisions (HLD) (stack bound at generation time)
35
+ behaviour
36
+ FR/US/SC/CN/DC/IC
26
37
  ```
27
38
 
28
- - **One design per aggregate.** The four clean-architecture layers (Domain,
39
+ - **One design per feature.** The four clean-architecture layers (Domain,
29
40
  Application, API, Infrastructure) are **sections** of this one document, each at
30
- design level. Feature = aggregate; commands are user stories inside it. *(This is
41
+ design level. A feature is a capability scope the developer chooses — it may act on
42
+ several aggregates, and several features may act on one; its operations are user
43
+ stories inside it. *(This is
31
44
  the **greenfield** layout, authored from `design-template.md`. See the Brownfield
32
45
  variant below for reverse-engineered / existing-codebase designs.)*
33
46
  - **High-level, not code.** It carries entities/value-objects/events, behaviour as
34
47
  **signatures + pre/post/invariant/raises**, use-cases, endpoint contracts, ports,
35
48
  and the significant technical decisions with rationale — but **no complete method
36
49
  bodies**. See `design-template.md` for the section structure (§0–§9). The domain
37
- layer (§1) follows spec.md exactly — its entities/value-objects are the spec's Key
38
- Entities and its invariants are the spec's Business Rules.
50
+ layer (§1) follows the context's `domain-model.md` exactly — its entities and
51
+ value-objects carry the model's names, its aggregate boundary is the model's Boundary
52
+ block, and its invariants **cite** the model's `BR-nnn` rather than restating them.
53
+ Behaviour comes from the spec, structure from the model.
39
54
  - **Technology-agnostic.** Behaviour and contracts use conceptual types, never a
40
55
  language. The SAME backend-plan.md must be generatable on a different stack. The target
41
56
  language/framework/persistence is bound only at `implement` time from the
@@ -145,31 +160,48 @@ The design document and the code are **not** a byte-for-byte copy of each other.
145
160
  ## The traceability spine
146
161
 
147
162
  ```
148
- FR/BR/US/SC ↔ design section (element / decision) ↔ code (+ tests) ↔ commit
163
+ FR/US/SC/CN/DC/IC (spec) ↔ design section (element / decision) ↔ code (+ tests) ↔ commit
164
+ BR-nnn (domain model) ↗
149
165
  ```
150
166
 
151
- Every design element traces **up** to a spec id, and the whole spec traces **down**
152
- into a design section (§6 Traceability). This is what makes the analyze gate and the
153
- review able to check coverage, and what keeps CR impact analysis exact.
167
+ Every design element traces **up** to an id, and the whole spec traces **down** into a
168
+ design section (§6 Traceability). The ids come from the two authorities: behaviour ids
169
+ from `spec.md`, invariant ids (`BR-nnn`) from the context's `domain-model.md`. An element
170
+ that traces to neither is an invention and the analyze gate raises it. This is what makes
171
+ the gate and the review able to check coverage, and what keeps CR impact analysis exact.
154
172
 
155
173
  ## Feature granularity (why a command is a delta, not a new feature)
156
174
 
157
- A **feature is the largest slice whose code no other feature owns.** Because
158
- `implement` regenerates the whole feature's code coherently, a shared file
159
- (`src/Domain/Order.cs`) must have exactly **one** owning design. So for an aggregate
160
- with a shared entity, **feature = the aggregate** (`Order`), and individual commands
161
- (`CreateOrder`, `UpdateOrder`, …) are **user stories / deltas** on the same backend-plan.md:
175
+ A **feature is a capability scope the developer chooses** — `Order`, or
176
+ `PatientRegistration` beside `PatientMerge`. It may act on several aggregates, and
177
+ several features may act on one. The aggregates live above every feature: in the bounded
178
+ context's `domain-model.md` (one `## Aggregate:` section each) and in the OKF bundle (one
179
+ concept each, listing the features that act on it). An operation inside a feature's scope
180
+ (`CreateOrder`, `UpdateOrder`, …) is a **user story / delta** on that feature's
181
+ backend-plan.md:
162
182
 
163
183
  - `CreateOrder` = US-1; `UpdateOrder` = US-2.
164
- - Adding `UpdateOrder` is an additive CR: a Delta Spec (`+US-2`) and a Delta Design
165
- (`ADDED` new use-case/endpoint sections, `MODIFIED` the shared Order/root and
166
- repository sections — see `delta-design-template.md`). The design phase amends
167
- backend-plan.md doc-first, bumps the feature version (`v1.0 → v1.1`), and archives the CR;
168
- `implement` regenerates the touched slice; regression re-tests the shared-file
169
- dependents.
170
-
171
- Two command-level features would both claim `src/Domain/Order.cs` and collide —
172
- which is exactly why the aggregate is the boundary.
184
+ - Adding `UpdateOrder` is an additive CR: a **delta-spec** (`+US-2`), the change's own
185
+ **model delta** where the domain moves, and **delta lane plans** (`ADDED` new
186
+ use-case/endpoint sections, `MODIFIED` the shared Order/root and repository sections —
187
+ see `delta-design-template.md`). All three live inside `changes/<CR-id>/`, and the
188
+ MASTERS are not touched while the change is open: `plan` writes
189
+ `delta-backend-plan.md` / `delta-frontend-plan.md` beside the delta-spec, never
190
+ `backend-plan.md`. `implement` generates only what the delta marks ADDED or MODIFIED;
191
+ regression re-tests the shared-file dependents.
192
+ - **Nothing in the plan phase bumps a version or archives a change.** `finalize-delta`
193
+ MERGES the delta into `spec.md` and the context's `domain-model.md` (not the lane
194
+ plans — they are derived from those two and are re-derived by `/appweaver plan main`),
195
+ and `close-delta` is the sole stage that archives `changes/<CR-id>/` and moves the
196
+ version (`v1.0 → v1.1`). The two are deliberately separate so a red `analyze-slice`
197
+ between them is still "fix and re-run" rather than a change that already shipped.
198
+
199
+ **Shared aggregate code is extended, never regenerated.** When two features act on one
200
+ aggregate, its root, entities and repository (`src/Domain/Order.cs`) are realised by
201
+ both plans: each names only the members its feature needs, and `implement` adds those
202
+ members to the existing file instead of writing it afresh. What a feature owns outright
203
+ is its own use-cases, endpoints and handlers. The invariants are the model's, so two
204
+ features never disagree about them.
173
205
 
174
206
  ## Migration status
175
207
 
@@ -28,6 +28,12 @@
28
28
  # wave means "after those land". So a lower-wave delta is committed to landing first
29
29
  # and may be built on; a same-wave one is visible but is never assumed, because
30
30
  # nothing orders the two against each other.
31
+ # • the SIBLING deltas — every open model delta at the SAME or a higher wave. VISIBLE
32
+ # is the operative word and the reason they are in context at all: this module tells
33
+ # the planner to name any sibling touching the same aggregate or component, and a
34
+ # planner that was never shown them can only stay silent — which the gate one stage
35
+ # later reads as a missed overlap. Their model deltas only, since the aggregate a
36
+ # sibling touches is what makes it an overlap.
31
37
  #
32
38
  # IT READS THE CODE. The feature's real source files arrive from its OKF concept
33
39
  # (concept-paths, resolved by the caller) as a PATH LIST, and the authoring steps declare
@@ -39,7 +45,8 @@
39
45
  # EXPECTS caller vars: feature_dir, spec_file, backend_plan, frontend_plan, domain_model,
40
46
  # be_existing, fe_existing, fe_lane_id, source_files, source_count, USER_INPUT,
41
47
  # and — resolved by the caller from the change it chose and activated — cr_id, cr_dir,
42
- # delta_spec, delta_model, wave, predecessor_files, predecessor_count, sibling_count.
48
+ # delta_spec, delta_model, wave, predecessor_files, predecessor_count, predecessor_ids,
49
+ # sibling_count, sibling_ids, related_docs.
43
50
  # PRODUCES: delta_backend_plan, delta_frontend_plan, downstream_count, impact_report.
44
51
  # =============================================================================
45
52
 
@@ -66,7 +73,13 @@ vars:
66
73
  wave: "1"
67
74
  predecessor_files: [] # every open model delta in a LOWER wave — this delta's predecessors
68
75
  predecessor_count: "0"
76
+ predecessor_ids: ""
69
77
  sibling_count: "0"
78
+ sibling_ids: ""
79
+ # What actually goes into the authoring context: the predecessors' two halves AND the
80
+ # siblings' model deltas. Both kinds are SHOWN; the prompts say which ids carry which
81
+ # licence, because a planner asked to name an overlap has to be able to see one.
82
+ related_docs: []
70
83
  delta_backend_plan: ""
71
84
  delta_frontend_plan: ""
72
85
 
@@ -97,9 +110,13 @@ workflow:
97
110
  - id: ctx_backend_delta
98
111
  condition: "{{be_existing}} == true"
99
112
  action: build_context
100
- context_from: predecessor_files # the open model deltas in LOWER waves — committed
101
- # to landing first, so this plan may build on them.
102
- # Empty for a wave-1 delta, which is the common case.
113
+ context_from: related_docs # every other open change's model delta, plus each
114
+ # PREDECESSOR's delta-spec. Two kinds, opposite
115
+ # licence: a LOWER-wave predecessor is committed to
116
+ # landing first and may be built on; a SAME-wave
117
+ # sibling may only be designed around. The prompt
118
+ # names which ids are which. Empty when this is the
119
+ # only open change, the common case.
103
120
  context:
104
121
  - "{{delta_backend_plan}}" # the delta plan template to fill
105
122
  - "{{delta_spec}}" # the spec-side change (from specify)
@@ -144,11 +161,17 @@ workflow:
144
161
  invariant is ENFORCED. Cite ids; never restate what an invariant says. An item in
145
162
  the model delta with no row here is a structural change nothing implements.
146
163
  - THE EFFECTIVE MODEL IS master + this change's delta + the PREDECESSOR deltas in
147
- context (open changes in a LOWER wave, committed to landing first). You MAY build
148
- on a predecessor — record it under "## Depends on", which is what orders the
149
- finalizes. You may NOT assume a same-wave delta: nothing orders it against this
150
- one. Where one touches the same aggregate or element, say so under "Other open
151
- deltas" and plan around it. Until merged, every one of them is a proposal.
164
+ context. {{predecessor_count}} predecessor(s) — open changes in a LOWER wave,
165
+ committed to landing first: {{predecessor_ids}}. You MAY build on one; record it
166
+ under "## Depends on", which is what orders the finalizes.
167
+ - {{sibling_count}} SIBLING change(s) are open at the same or a higher wave:
168
+ {{sibling_ids}}. Their model deltas are in context too, and the licence over them
169
+ is the opposite one — nothing orders them against this change, so you may NOT
170
+ assume any of them lands. Read each one and, where it touches an aggregate or an
171
+ element this plan also touches, say so by id under "Other open deltas" and plan so
172
+ that either order of merging works. If consuming one is genuinely unavoidable,
173
+ that is a dependency, not an overlap: record it under "## Depends on" and say the
174
+ wave must be raised. Until merged, every one of them is a proposal.
152
175
  - PUBLISH WHAT THE FRONTEND LANE NEEDS. Fill "## Contract deltas for the frontend
153
176
  lane" with every addition or change to this feature's published surface, as field
154
177
  shapes. That table is the only part of this document the frontend delta plan may
@@ -168,12 +191,33 @@ workflow:
168
191
  stamp those.
169
192
  Guidance: {{USER_INPUT}}
170
193
 
194
+ # ══════════ THE LANE SWAP — the frontend's rules replace the backend's ═══════
195
+ # The delta twin of the swap `plan` performs before its own master frontend sections.
196
+ # Sticky standards only accumulate, and the caller loaded BOTH lanes' rule sets before
197
+ # including this module, so without this the service delta plan is authored with the UI
198
+ # rules in its envelope and the experience delta plan with the service rules. `replace`
199
+ # resets to the workflow's declared seed and loads `## /aw-spec-plan-ui`, so the backend
200
+ # delta above kept the service set and everything below this line is the frontend's.
201
+ #
202
+ # Conditioned on the same flag as the lane it precedes: a change with no frontend half
203
+ # authors nothing here and must not have its envelope swapped out from under the
204
+ # xref step that follows.
205
+ - id: tech_select_ui_delta
206
+ condition: "{{fe_existing}} == true"
207
+ action: include
208
+ module: .appweaver/orchestrator/modules/tech-select.orch.yaml
209
+ with: { command: aw-spec-plan-ui, replace: "true" }
210
+
171
211
  # ═════════════════════ FRONTEND LANE — the experience delta plan ═════════════
172
212
  - id: ctx_frontend_delta
173
213
  condition: "{{fe_existing}} == true"
174
214
  action: build_context
175
- context_from: predecessor_files # the same lower-wave model deltas — the ubiquitous
176
- # language and the aggregates this change assumes
215
+ context_from: related_docs # the same two kinds as the service lane: lower-wave
216
+ # predecessors (the ubiquitous language and the
217
+ # aggregates this change may assume) and same-wave
218
+ # siblings (visible, never assumed — this lane shares
219
+ # screens and components, so an unseen sibling is an
220
+ # unseen collision). The prompt names which is which.
177
221
  context:
178
222
  - "{{delta_frontend_plan}}" # the delta plan template to fill
179
223
  - "{{delta_spec}}" # the spec-side change (UX-/US-/FR- items)
@@ -225,9 +269,13 @@ workflow:
225
269
  labels, states and messages — the same terms, no synonyms — and cite BR-nnn rather
226
270
  than restating a rule.
227
271
  - THE EFFECTIVE MODEL IS master + this change's delta + the PREDECESSOR deltas in
228
- context (open changes in a LOWER wave). You MAY build on a predecessor — record it
229
- under "## Depends on". You may NOT assume a same-wave delta; where one touches the
230
- same screens or shared components, say so under "Other open deltas".
272
+ context. {{predecessor_count}} predecessor(s) — open changes in a LOWER wave:
273
+ {{predecessor_ids}}. You MAY build on one; record it under "## Depends on".
274
+ - {{sibling_count}} SIBLING change(s) are open at the same or a higher wave:
275
+ {{sibling_ids}}. Their model deltas are in context and nothing orders them against
276
+ this change, so you may NOT assume any of them lands. Where one touches the same
277
+ screens, shared components or contract surface, say so by id under "Other open
278
+ deltas" and plan so that either order of merging works.
231
279
  - TECHNOLOGY-AGNOSTIC: no framework, router, state library, component library or
232
280
  styling approach — responsibilities, field shapes, events and states only. The
233
281
  header's Lane field is `{{fe_lane_id}}`.
@@ -48,10 +48,12 @@ scripts:
48
48
 
49
49
  workflow:
50
50
 
51
- # Open the change + scaffold its THREE documents: delta-spec and delta-design under
52
- # changes/<cr-id>/, and the model delta BESIDE the context's master model. Many
53
- # changes may be open at once — every specify call on an established feature opens
54
- # its own, and the wave is what orders them.
51
+ # Open the change + scaffold its TWO documents: the delta-spec under changes/<cr-id>/,
52
+ # and the model delta BESIDE the context's master model. The delta PLANS are not
53
+ # scaffolded here — `plan` owns them and creates them with delta-plan-scaffold when it
54
+ # is run for this change, because a change that is never planned should not leave empty
55
+ # plan files behind. Many changes may be open at once — every specify call on an
56
+ # established feature opens its own, and the wave is what orders them.
55
57
  - id: cr_setup
56
58
  action: execute_script
57
59
  script: { ref: actions }
@@ -119,8 +121,10 @@ workflow:
119
121
  to the standard governing every such behaviour is a UX- delta. A bound the SERVICE
120
122
  must meet stays a CN-, because the service-side gates trace CN- and never read the
121
123
  Experience Standards.
122
- - The new operation is a NEW USER STORY — assign the next id after the highest US-n
123
- in the master — never a new feature/aggregate. Record the regression risk (shared
124
+ - The new operation is a NEW USER STORY of THIS feature — assign the next id after
125
+ the highest US-n in the master. Whether a capability is its own feature is the
126
+ developer's decision, made when they chose this feature; never open, name or
127
+ imply another feature or a new aggregate here. Record the regression risk (shared
124
128
  behaviour that must still hold).
125
129
  - STRUCTURE IS NOT YOURS TO WRITE HERE. A new entity or value object, a changed
126
130
  aggregate boundary, a new domain event, or a new or amended invariant (BR-n) is a
@@ -22,7 +22,15 @@
22
22
  # never asked about a UI lane it declined.
23
23
  #
24
24
  # Inputs (via include `with`):
25
- # require — the lane kind this phase cannot run without: backend | frontend
25
+ # require — the lane kind this phase cannot run without: backend | frontend, or an
26
+ # EMPTY string for "any lane will do". Empty is what a phase passes when it
27
+ # needs a declaration to work from but no particular kind of one — `build`
28
+ # compiles every declared lane, so a frontend-only project is as buildable
29
+ # as a backend-only one. It narrows nothing else: no lane declared at all, a
30
+ # lane with no source path, a source path that is not on disk and two lanes
31
+ # claiming overlapping subtrees all still stop the run. The required kind's
32
+ # fields below then bind from the backend block, which an empty-require
33
+ # caller reads none of.
26
34
  # command — the phase being gated, quoted back in the failure message
27
35
  #
28
36
  # Produces, for the required kind: WHERE it lives (lane_root / lane_source_path),
@@ -110,6 +110,27 @@ workflow:
110
110
  multi: true
111
111
  answer_var: tg_subset
112
112
 
113
+ # 2b. SWAP, don't stack — `replace` (passed by the caller through `with:`).
114
+ #
115
+ # A phase that generates for two lanes includes this module twice, and standards only
116
+ # ever ACCUMULATE: the second lane inherited the first's rule set, so a full-stack run
117
+ # showed the frontend generator the service rules and the backend generator the UI
118
+ # rules, and each prompt had to argue against its own envelope. The caller's FIRST
119
+ # include leaves `replace` unset — that lane's rules simply join the declared seed —
120
+ # and the second passes replace: "true" immediately before its own lane's steps.
121
+ #
122
+ # IT IS ITS OWN STEP, not a flag on the loads below, because the loads are conditional
123
+ # and the reset must not be. A lane whose map resolves nothing fires NO load step at all
124
+ # (no map ⇒ no candidates ⇒ the selection box never runs), and on that path a flag
125
+ # attached to the loads would leave the OTHER lane's rules standing — "no rules
126
+ # configured for this lane" silently meaning "keep the other lane's", which is the exact
127
+ # confusion this exists to remove. load_standards with no files is a pure reset.
128
+ - id: swap_lane_rules
129
+ action: load_standards
130
+ condition: "{{replace}} == true"
131
+ files: []
132
+ replace: "true"
133
+
113
134
  # 3. Load the selection into the sticky standards set (system block, D3).
114
135
  - id: load_all
115
136
  action: load_standards
@@ -183,6 +183,14 @@ class Orchestrator:
183
183
  # system prefix stays byte-stable across steps (prompt-cache lever)
184
184
  # while build_context keeps declaring only the per-request delta.
185
185
  self.standards = list(wf.get("standards", []) or [])
186
+ # The workflow's DECLARED seed, kept apart from what steps load later. A phase
187
+ # that generates for two lanes needs each lane's rule set and NOT the other's,
188
+ # but standards only ever accumulate — so the second lane inherited the first's
189
+ # rules and both prompts had to argue their way out of an envelope that
190
+ # contradicted them. `load_standards: replace` resets to this seed and then
191
+ # appends, which swaps the lane rules while leaving the always-on set (ai-rules
192
+ # and anything else the workflow declared up front) exactly where it was.
193
+ self.standards_seed = list(self.standards)
186
194
  # F1: top-level scripts ref map {ref: {sh: path, ps1: path}} (decl shape)
187
195
  self.scripts = dict(wf.get("scripts", {}) or {})
188
196
  # every file that has entered any payload (verify_context required_files)
@@ -960,8 +968,22 @@ class Orchestrator:
960
968
  def act_load_standards(self, step):
961
969
  """F4 — append files to the sticky standards set (dedup; `optional:`
962
970
  tolerated). _assemble_system prepends the set to EVERY payload, so a
963
- skill loads its standards once (decl load_context → system per D3)."""
971
+ skill loads its standards once (decl load_context → system per D3).
972
+
973
+ `replace: true` first resets the set to the workflow's DECLARED seed,
974
+ dropping everything steps have loaded since. That is how a phase serving
975
+ two lanes gives each one its own rule set instead of the union: the
976
+ second lane replaces the first's rules rather than stacking on them.
977
+ The seed survives, so the always-on standards are never dropped."""
964
978
  p = step.get("parameters") or step
979
+ # Through _cond, so a rendered "{{flag}}" that resolves to "false" is FALSE.
980
+ # Plain truthiness would read that non-empty string as true and silently drop
981
+ # every loaded standard on the path meant to keep them.
982
+ if self._cond(str(p.get("replace", ""))):
983
+ dropped = len(self.standards) - len(self.standards_seed)
984
+ self.standards = list(self.standards_seed)
985
+ self.emit(f"[load_standards] replace — reset to the declared seed "
986
+ f"({len(self.standards_seed)} file(s), {max(dropped, 0)} dropped)")
965
987
  files = p.get("files") or []
966
988
  if p.get("files_from_variable"):
967
989
  files = [{"path": x} for x in (self.vars.get(p["files_from_variable"]) or [])]
@@ -40,7 +40,7 @@
40
40
  -->
41
41
  # Delta Backend Plan — [CR-ID]: [TITLE]
42
42
 
43
- **Feature (aggregate slice)**: [Feature]
43
+ **Feature**: [Feature]
44
44
 
45
45
  **Lane**: backend
46
46
 
@@ -53,7 +53,7 @@
53
53
  -->
54
54
  # Design: [AGGREGATE]
55
55
 
56
- **Aggregate (feature)**: `[Aggregate]`
56
+ **Feature**: `[Feature]`
57
57
 
58
58
  **Plan home**: `specs/[Context]/[Feature]/backend-plan.md`
59
59
 
@@ -71,13 +71,19 @@
71
71
  contracts, behaviour and decisions; it is not code.
72
72
 
73
73
  ANCHORING RULES
74
- - Every design element traces UP to a spec id (FR-/BR-/CN-/DC-/IC-/US-/SC-)
75
- and the whole spec is covered DOWN here (§4). No element without a spec
76
- reason; no requirement without a design home.
77
- - THE SPEC IS THE ONLY DOMAIN INPUT. Design reads the spec + the technical
78
- standards, never the domain rule files. A domain rule the spec omits is
79
- recorded as a §5 assumption, never smuggled in.
80
- - Feature = aggregate. A later requirement is a DELTA on this document.
74
+ - Every design element traces UP to an id (FR-/BR-/CN-/DC-/IC-/US-/SC-) and the
75
+ whole spec is covered DOWN here (§4). No element without a reason in an
76
+ authority; no requirement without a design home.
77
+ - THE AUTHORITIES ARE THE SPEC AND, WHERE THE CONTEXT HAS ONE, ITS
78
+ domain-model.md — never the domain rule files. The spec is always the
79
+ behaviour authority. Where specs/[Context]/domain-model.md exists it is the
80
+ STRUCTURE authority and outranks the spec on entities, value objects, the
81
+ aggregate boundary and the BR-nnn invariants; where it does not, the spec
82
+ carries that structure alone. A domain rule neither states is recorded as a
83
+ §5 assumption, never smuggled in.
84
+ - One feature = one plan. A feature is the capability scope the developer chose
85
+ and may act on several aggregates, as several features may act on one. A later
86
+ requirement on this feature is a DELTA on this document.
81
87
 
82
88
  STRUCTURE FOLLOWS THIS PROJECT'S CODE
83
89
  - [GENERATED_STRUCTURE_NOTE] — form: one or two sentences naming the
@@ -134,11 +140,14 @@
134
140
 
135
141
  ### 1.2 Domain / Data Model
136
142
 
137
- <!-- PINNED ANCHOR — follow spec.md exactly. Entities & value objects are the
138
- spec's Key Entities (SAME names); invariants are its Business Rules
139
- (BR-nnn, cite each); the boundary is its Aggregate Boundary; states are its
140
- lifecycle. Cross-slice references are BY ID and live here — the
141
- cross-reference tool reads this section for those edges. -->
143
+ <!-- PINNED ANCHOR — follow the STRUCTURE authority exactly. Where the context has
144
+ a specs/[Context]/domain-model.md, that is it: entities & value objects carry
145
+ the model's names, invariants cite its BR-nnn (never restate them), the
146
+ boundary is its Boundary block, states are the lifecycle it describes. Where
147
+ the context has no model yet, the same four come from spec.md's Key Entities,
148
+ Business Rules, Aggregate Boundary and lifecycle. Cross-slice references are
149
+ BY ID and live here either way — the cross-reference tool reads this section
150
+ for those edges. -->
142
151
 
143
152
  | Element | Kind | Fields / shape | Invariants (BR-) |
144
153
  |---|---|---|---|
@@ -37,7 +37,7 @@
37
37
  -->
38
38
  # Delta Frontend Plan — [CR-ID]: [TITLE]
39
39
 
40
- **Feature (aggregate slice)**: [Feature]
40
+ **Feature**: [Feature]
41
41
 
42
42
  **Base version**: [vX.Y]
43
43
 
@@ -59,7 +59,7 @@
59
59
  -->
60
60
  # UI Design: [AGGREGATE]
61
61
 
62
- **Aggregate (feature)**: `[Aggregate]`
62
+ **Feature**: `[Feature]`
63
63
 
64
64
  **Plan home**: `specs/[Context]/[Feature]/frontend-plan.md`
65
65
 
@@ -83,9 +83,11 @@
83
83
  - Every element traces UP to a spec id (FR-/BR-/CN-/DC-/IC-/US-/SC-) and the
84
84
  experience obligations of the spec are covered DOWN here (§7). No screen
85
85
  without a spec reason; no user story without a screen home.
86
- - THE SPEC IS THE ONLY DOMAIN INPUT — the same rule the service design keeps.
87
- This document reads the spec + the lane's own standards, never the domain
88
- rule files.
86
+ - THE SPEC IS THE ONLY DOMAIN INPUT for this lane. This document reads the spec
87
+ + the lane's own standards, never the domain rule files. (The service lane
88
+ reads a second authority, the context's domain-model.md, because it realises
89
+ the aggregate's structure. This lane consumes the published surface at §5, not
90
+ the domain, so the model is not in its context.)
89
91
  - THE CONTRACT IS THE ONLY SERVICE INPUT (§5). Read the design contract, never
90
92
  the service design.
91
93