aw-spec-cli 0.1.18 → 0.1.19
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.
- package/assets/.appweaver/DESIGN-FORMAT.md +45 -21
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +62 -14
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +6 -4
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +9 -1
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +21 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +23 -1
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +18 -11
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +5 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +298 -109
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +161 -34
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +125 -24
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +18 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +182 -39
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +300 -35
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +190 -31
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +93 -8
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +34 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +319 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +125 -24
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +846 -35
- package/assets/.appweaver/scripts/bash/common.sh +28 -1
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +843 -46
- package/assets/.appweaver/scripts/python/aw-inventory.py +3 -2
- package/assets/.appweaver/templates/design-template.md +41 -30
- package/assets/.appweaver/templates/spec-template.md +7 -4
- package/assets/.appweaver/templates/ui-design-template.md +7 -3
- package/assets/guardrails/domain/healthcare/auditing.domain.md +3 -3
- package/assets/guardrails/domain/pharma/auditing.domain.md +3 -3
- package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/authorization.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/data-classification.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/eventing.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/persistence.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +1 -1
- package/assets/guardrails/technology/nextjs/accessibility-implementation.md +1 -1
- package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +1 -1
- package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +1 -1
- package/assets/guardrails/technology/nextjs/auth-session.md +1 -1
- package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
- package/assets/guardrails/technology/nextjs/design-system.md +1 -1
- package/assets/guardrails/technology/nextjs/error-handling.md +1 -1
- package/assets/guardrails/technology/nextjs/forms-and-validation.md +1 -1
- package/assets/guardrails/technology/nextjs/i18n.md +1 -1
- package/assets/guardrails/technology/nextjs/performance-budgets.md +1 -1
- package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
- package/assets/guardrails/technology/nextjs/state-management.md +1 -1
- package/package.json +1 -1
|
@@ -8,21 +8,32 @@ 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/<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
`
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
39
|
- **One design per aggregate.** The four clean-architecture layers (Domain,
|
|
@@ -34,8 +45,10 @@ FR/BR/US/SC decisions (HLD) (stack bound at generation time)
|
|
|
34
45
|
**signatures + pre/post/invariant/raises**, use-cases, endpoint contracts, ports,
|
|
35
46
|
and the significant technical decisions with rationale — but **no complete method
|
|
36
47
|
bodies**. See `design-template.md` for the section structure (§0–§9). The domain
|
|
37
|
-
layer (§1) follows
|
|
38
|
-
|
|
48
|
+
layer (§1) follows the context's `domain-model.md` exactly — its entities and
|
|
49
|
+
value-objects carry the model's names, its aggregate boundary is the model's Boundary
|
|
50
|
+
block, and its invariants **cite** the model's `BR-nnn` rather than restating them.
|
|
51
|
+
Behaviour comes from the spec, structure from the model.
|
|
39
52
|
- **Technology-agnostic.** Behaviour and contracts use conceptual types, never a
|
|
40
53
|
language. The SAME backend-plan.md must be generatable on a different stack. The target
|
|
41
54
|
language/framework/persistence is bound only at `implement` time from the
|
|
@@ -145,12 +158,15 @@ The design document and the code are **not** a byte-for-byte copy of each other.
|
|
|
145
158
|
## The traceability spine
|
|
146
159
|
|
|
147
160
|
```
|
|
148
|
-
FR/
|
|
161
|
+
FR/US/SC/CN/DC/IC (spec) ↔ design section (element / decision) ↔ code (+ tests) ↔ commit
|
|
162
|
+
BR-nnn (domain model) ↗
|
|
149
163
|
```
|
|
150
164
|
|
|
151
|
-
Every design element traces **up** to
|
|
152
|
-
|
|
153
|
-
|
|
165
|
+
Every design element traces **up** to an id, and the whole spec traces **down** into a
|
|
166
|
+
design section (§6 Traceability). The ids come from the two authorities: behaviour ids
|
|
167
|
+
from `spec.md`, invariant ids (`BR-nnn`) from the context's `domain-model.md`. An element
|
|
168
|
+
that traces to neither is an invention and the analyze gate raises it. This is what makes
|
|
169
|
+
the gate and the review able to check coverage, and what keeps CR impact analysis exact.
|
|
154
170
|
|
|
155
171
|
## Feature granularity (why a command is a delta, not a new feature)
|
|
156
172
|
|
|
@@ -161,12 +177,20 @@ with a shared entity, **feature = the aggregate** (`Order`), and individual comm
|
|
|
161
177
|
(`CreateOrder`, `UpdateOrder`, …) are **user stories / deltas** on the same backend-plan.md:
|
|
162
178
|
|
|
163
179
|
- `CreateOrder` = US-1; `UpdateOrder` = US-2.
|
|
164
|
-
- Adding `UpdateOrder` is an additive CR: a
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
180
|
+
- Adding `UpdateOrder` is an additive CR: a **delta-spec** (`+US-2`), the change's own
|
|
181
|
+
**model delta** where the domain moves, and **delta lane plans** (`ADDED` new
|
|
182
|
+
use-case/endpoint sections, `MODIFIED` the shared Order/root and repository sections —
|
|
183
|
+
see `delta-design-template.md`). All three live inside `changes/<CR-id>/`, and the
|
|
184
|
+
MASTERS are not touched while the change is open: `plan` writes
|
|
185
|
+
`delta-backend-plan.md` / `delta-frontend-plan.md` beside the delta-spec, never
|
|
186
|
+
`backend-plan.md`. `implement` generates only what the delta marks ADDED or MODIFIED;
|
|
187
|
+
regression re-tests the shared-file dependents.
|
|
188
|
+
- **Nothing in the plan phase bumps a version or archives a change.** `finalize-delta`
|
|
189
|
+
MERGES the delta into `spec.md` and the context's `domain-model.md` (not the lane
|
|
190
|
+
plans — they are derived from those two and are re-derived by `/appweaver plan main`),
|
|
191
|
+
and `close-delta` is the sole stage that archives `changes/<CR-id>/` and moves the
|
|
192
|
+
version (`v1.0 → v1.1`). The two are deliberately separate so a red `analyze-slice`
|
|
193
|
+
between them is still "fix and re-run" rather than a change that already shipped.
|
|
170
194
|
|
|
171
195
|
Two command-level features would both claim `src/Domain/Order.cs` and collide —
|
|
172
196
|
which is exactly why the aggregate is the boundary.
|
|
@@ -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,
|
|
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:
|
|
101
|
-
#
|
|
102
|
-
#
|
|
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,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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:
|
|
176
|
-
#
|
|
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
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
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 }
|
|
@@ -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 [])]
|
|
@@ -71,12 +71,16 @@
|
|
|
71
71
|
contracts, behaviour and decisions; it is not code.
|
|
72
72
|
|
|
73
73
|
ANCHORING RULES
|
|
74
|
-
- Every design element traces UP to
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
- THE
|
|
78
|
-
|
|
79
|
-
|
|
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.
|
|
80
84
|
- Feature = aggregate. A later requirement is a DELTA on this document.
|
|
81
85
|
|
|
82
86
|
STRUCTURE FOLLOWS THIS PROJECT'S CODE
|
|
@@ -134,11 +138,14 @@
|
|
|
134
138
|
|
|
135
139
|
### 1.2 Domain / Data Model
|
|
136
140
|
|
|
137
|
-
<!-- PINNED ANCHOR — follow
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
<!-- PINNED ANCHOR — follow the STRUCTURE authority exactly. Where the context has
|
|
142
|
+
a specs/[Context]/domain-model.md, that is it: entities & value objects carry
|
|
143
|
+
the model's names, invariants cite its BR-nnn (never restate them), the
|
|
144
|
+
boundary is its Boundary block, states are the lifecycle it describes. Where
|
|
145
|
+
the context has no model yet, the same four come from spec.md's Key Entities,
|
|
146
|
+
Business Rules, Aggregate Boundary and lifecycle. Cross-slice references are
|
|
147
|
+
BY ID and live here either way — the cross-reference tool reads this section
|
|
148
|
+
for those edges. -->
|
|
142
149
|
|
|
143
150
|
| Element | Kind | Fields / shape | Invariants (BR-) |
|
|
144
151
|
|---|---|---|---|
|
|
@@ -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
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|