aw-spec-cli 0.1.32 → 0.1.34

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 (75) hide show
  1. package/README.md +76 -81
  2. package/assets/.appweaver/DESIGN-FORMAT.md +56 -17
  3. package/assets/.appweaver/STACK-PROFILES.md +95 -60
  4. package/assets/.appweaver/orchestrator/LAUNCHER.md +1 -1
  5. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +173 -32
  6. package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +29 -1
  7. package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +7 -4
  8. package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +49 -60
  9. package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +58 -0
  10. package/assets/.appweaver/orchestrator/orchestrator.py +26 -8
  11. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +20 -16
  12. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +30 -6
  13. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +9 -7
  14. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +5 -5
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +341 -36
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +254 -43
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +248 -68
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +251 -20
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +316 -10
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +6 -47
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +62 -16
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +132 -9
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-fix-test-coverage.orch.yaml +965 -0
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +222 -169
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +28 -12
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +453 -116
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +7 -4
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +83 -6
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +45 -2
  30. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +324 -74
  31. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +275 -44
  32. package/assets/.appweaver/scripts/python/aw-foundation.py +16 -13
  33. package/assets/.appweaver/scripts/python/aw-gaps.py +11 -3
  34. package/assets/.appweaver/scripts/python/aw-model-guard.py +295 -0
  35. package/assets/.appweaver/scripts/python/aw-plan-coverage.py +412 -3
  36. package/assets/.appweaver/scripts/python/aw-plangate.py +23 -16
  37. package/assets/.appweaver/scripts/python/aw-stack.py +1794 -265
  38. package/assets/.appweaver/stack-profiles/dotnet-webapi.json +0 -2
  39. package/assets/.appweaver/stack-profiles/java-spring.json +0 -2
  40. package/assets/.appweaver/templates/api-plan-template.md +107 -0
  41. package/assets/.appweaver/templates/delta-api-plan-template.md +87 -0
  42. package/assets/.appweaver/templates/delta-design-template.md +25 -23
  43. package/assets/.appweaver/templates/delta-spec-template.md +3 -3
  44. package/assets/.appweaver/templates/design-template.md +20 -42
  45. package/assets/.appweaver/templates/domain-model-template.md +3 -0
  46. package/assets/.appweaver/templates/ui-delta-design-template.md +11 -8
  47. package/assets/.appweaver/templates/ui-design-template.md +4 -4
  48. package/assets/foundations/foundation.spec.template.txt +25 -18
  49. package/assets/guardrails/README.md +61 -25
  50. package/assets/guardrails/methodology/ux/accessibility-standards.md +38 -0
  51. package/assets/guardrails/methodology/ux/content-design.md +34 -0
  52. package/assets/guardrails/methodology/ux/interaction-states.md +45 -0
  53. package/assets/guardrails/methodology/ux/journey-mapping.md +41 -0
  54. package/assets/guardrails/project-specific/domain/domain-requirements.md +25 -38
  55. package/assets/guardrails/project-specific/domain-rule-bindings.md +33 -69
  56. package/assets/guardrails/project-specific/frontend/ui-requirements.md +2 -2
  57. package/assets/guardrails/project-specific/service/technical-requirements.md +2 -2
  58. package/assets/guardrails/project-specific/tech-rule-bindings.md +16 -13
  59. package/assets/guardrails/sample-healthcare/domain/domain-requirements.md +58 -0
  60. package/assets/guardrails/sample-healthcare/domain-rule-bindings.md +138 -0
  61. package/assets/guardrails/sample-healthcare/frontend/ui-requirements.md +43 -0
  62. package/assets/guardrails/sample-healthcare/service/technical-requirements.md +40 -0
  63. package/assets/guardrails/sample-healthcare/tech-rule-bindings.md +303 -0
  64. package/dist/commands/init.d.ts.map +1 -1
  65. package/dist/commands/init.js +216 -7
  66. package/dist/commands/init.js.map +1 -1
  67. package/dist/utils/config.js +1 -1
  68. package/dist/utils/config.js.map +1 -1
  69. package/dist/utils/foundation.d.ts +36 -0
  70. package/dist/utils/foundation.d.ts.map +1 -0
  71. package/dist/utils/foundation.js +109 -0
  72. package/dist/utils/foundation.js.map +1 -0
  73. package/dist/utils/paths.js +1 -1
  74. package/dist/utils/paths.js.map +1 -1
  75. package/package.json +1 -1
@@ -16,7 +16,10 @@
16
16
  # still READ the other lane where they need it: a frontend plan binds to the backend
17
17
  # plan's published surface, which the backend developer owns. specify, finalize-delta and
18
18
  # close-delta do NOT follow it: the spec is written whole, from its rules and standards,
19
- # and a change merges and closes with whatever its lanes have delivered.
19
+ # and a change merges and closes with whatever its lanes have delivered. Brownfield adopt
20
+ # does not follow it either: it reverses a feature once, every half its code has, because
21
+ # it cannot run a second time for the other lane, so the other lane's developer finds
22
+ # their plan already written.
20
23
  #
21
24
  # THE OPTIONS COME FROM stacks.json. Both lanes declared → backend, frontend and both.
22
25
  # One lane declared → that lane only, picked without a question (there is nothing to
@@ -32,8 +35,8 @@
32
35
  # =============================================================================
33
36
 
34
37
  name: aw-spec-select-lane
35
- description: Choose the lane this developer works on — backend, frontend or both — and store it in .appweaver/lane.json, which is not committed, so each developer keeps their own. The options come from the stacks declared in .appweaver/stacks.json, all three when both lanes are declared and only the declared one otherwise. plan, analyze-plan, implement, build and analyze-slice then write only that lane while still reading the other; specify, finalize-delta and close-delta do not follow it. select-stack writes the default (every declared lane); run this to narrow it.
36
- version: 1.0.0
38
+ description: Choose the lane this developer works on — backend, frontend or both — and store it in .appweaver/lane.json, which is not committed, so each developer keeps their own. The options come from the stacks declared in .appweaver/stacks.json, all three when both lanes are declared and only the declared one otherwise. plan, analyze-plan, implement, build and analyze-slice then write only that lane while still reading the other; specify, finalize-delta, close-delta and brownfield adopt do not follow it. select-stack writes the default (every declared lane); run this to narrow it.
39
+ version: 1.0.2
37
40
  agent_agnostic: true
38
41
  dialect: aw-spec.orchestrator/v1
39
42
 
@@ -100,5 +103,5 @@ outputs:
100
103
  working_lane: "{{working_lane}}"
101
104
  working_kinds: "{{working_kinds}}"
102
105
  lane_file: "{{lane_file}}"
103
- note: "Working lane `{{working_lane}}` stored in {{lane_file}}. The file is not committed — it is this developer's choice, and another developer on the same repository keeps their own. From now on plan, analyze-plan, implement, build and analyze-slice write only the working lane: they plan it, gate it with its own receipt, generate it, compile it and test it. They still READ the other lane where they need it — a frontend plan binds to the operation ids the backend plan publishes. specify writes the whole spec whatever lane is chosen, and finalize-delta and close-delta merge and close a change with whatever its lanes have delivered. Re-running /appweaver select-stack resets the working lane to every declared lane."
106
+ note: "Working lane `{{working_lane}}` stored in {{lane_file}}. The file is not committed — it is this developer's choice, and another developer on the same repository keeps their own. From now on plan, analyze-plan, implement, build and analyze-slice write only the working lane: they plan it, gate it with its own receipt, generate it, compile it and test it. They still READ the other lane where they need it — a frontend plan binds to the operation ids the feature's API plan (api-plan.md) publishes, and never reads the backend plan. specify writes the whole spec whatever lane is chosen, and finalize-delta and close-delta merge and close a change with whatever its lanes have delivered. Brownfield adopt reverses a feature once, every half its code has, whatever lane is chosen, so the other lane's developer finds their plan already written. Re-running /appweaver select-stack resets the working lane to every declared lane."
104
107
  next: "Run the lane's flow on the active feature: /appweaver plan → analyze-plan → implement → build → analyze-slice. Switch lanes any time with /appweaver select-lane backend | frontend | both."
@@ -52,8 +52,15 @@
52
52
  # for the projects it matters most to. `lane-verify` runs the on-disk check, in the
53
53
  # phases that actually read the subtree — implement, build, run, onboard.
54
54
  #
55
- # ZERO LLM: scripts, two gates, two selects and two questions. No generation, no
56
- # rule resolution.
55
+ # WHICH STACKS HAVE A RULE SET is said where the stack is chosen. Each menu's prompt
56
+ # carries one line from `stack-rules` — the stacks the active project's technical map has
57
+ # rule files for, complete or partial, and the ones it has none for — and a lane declared
58
+ # on a stack whose rule set is missing or incomplete raises a warning. Every stack stays
59
+ # selectable: one without a rule set runs on ai-rules.md and the technical constitution
60
+ # (STACK-PROFILES.md §7), and the point is that the operator knows it before choosing.
61
+ #
62
+ # ZERO LLM: scripts, two gates, two warnings, two selects and two questions. No
63
+ # generation; the rule report reads the map and the disk and loads nothing.
57
64
  #
58
65
  # Run (interactive via launcher):
59
66
  # /appweaver select-stack # ask everything
@@ -66,7 +73,7 @@
66
73
 
67
74
  name: aw-spec-select-stack
68
75
  description: Declare the project's lanes — each lane's STACK and its LANE ROOT FOLDER — and persist them to .appweaver/stacks.json, the single channel every later workflow resolves and gates on. The first command of both the greenfield and the brownfield sequence, and the sole writer of the lane model. It also writes this developer's working lane, .appweaver/lane.json, to its default — both lanes when both are declared, otherwise the one that is — which /appweaver select-lane narrows to one lane.
69
- version: 2.4.0
76
+ version: 2.5.0
70
77
  agent_agnostic: true
71
78
  dialect: aw-spec.orchestrator/v1
72
79
 
@@ -85,6 +92,17 @@ scripts:
85
92
  sh: .appweaver/scripts/bash/aw-spec-actions.sh
86
93
  ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
87
94
 
95
+ vars:
96
+ api_rules_note: "" # the menu's rule-set line for a backend stack (stack-rules)
97
+ api_rules_ok: "false" # the declared service lane's stack has a complete rule set, or the
98
+ # map names none for the lane — only stack-rules' own `ok` raises it
99
+ api_rules_warning: ""
100
+ api_rule_set: "" # complete | partial | none | not-mapped; empty with no service lane
101
+ ui_rules_note: ""
102
+ ui_rules_ok: "false"
103
+ ui_rules_warning: ""
104
+ ui_rule_set: ""
105
+
88
106
  workflow:
89
107
 
90
108
  # ═══════════════ SERVICE LANE ═══════════════════════════════════════════════
@@ -118,11 +136,22 @@ workflow:
118
136
  stacks there), then re-run.
119
137
  on_empty: stop
120
138
 
139
+ # 2b. Which backend stacks the active project's technical map has rule files for, as
140
+ # one line for the prompt. The option labels stay as stack-list wrote them, because
141
+ # lane-declare matches a label back to its profile.
142
+ - id: api_rules
143
+ action: execute_script
144
+ script: { ref: actions }
145
+ subcommand: stack-rules
146
+ args: ["--kind", "backend"]
147
+ outputs:
148
+ - { name: api_rules_note, from: "$.note" }
149
+
121
150
  # 3. Choose the service stack. A matching id in USER_INPUT auto-selects, and so
122
151
  # does a detected stack; a project with exactly one candidate auto-confirms.
123
152
  - id: select_api_stack
124
153
  action: user_select
125
- prompt: "Select the API (backend) stack — it owns the service lane. This repo's own files point at: {{api_detected}}. A detected stack leads the list. (none) declares a project with no service lane at all, and stops the technical constitution half and the service rule files being authored:"
154
+ prompt: "Select the API (backend) stack — it owns the service lane. This repo's own files point at: {{api_detected}}. A detected stack leads the list. (none) declares a project with no service lane at all, and stops the technical constitution half and the service rule files being authored. {{api_rules_note}}"
126
155
  options_from_variable: api_options
127
156
  preselect_from: api_preselect
128
157
  single_option_confirm: true
@@ -180,6 +209,25 @@ workflow:
180
209
  - { name: api_test_path, from: "$.test_path" }
181
210
  - { name: api_root_exists, from: "$.root_exists" }
182
211
 
212
+ # 6b. A service lane on a stack whose rule set is missing or incomplete is a supported
213
+ # setup, said here once with what it means and how to change it. The run continues.
214
+ - id: api_rules_check
215
+ condition: "{{api_declared}}"
216
+ action: execute_script
217
+ script: { ref: actions }
218
+ subcommand: stack-rules
219
+ args: ["--kind", "backend", "--declared"]
220
+ outputs:
221
+ - { name: api_rules_ok, from: "$.ok" }
222
+ - { name: api_rules_warning, from: "$.warning" }
223
+ - { name: api_rule_set, from: "$.verdict" }
224
+ - id: warn_api_rules
225
+ condition: "{{api_declared}}"
226
+ action: verify_context
227
+ require_true: api_rules_ok
228
+ on_false: continue
229
+ on_false_message: "{{api_rules_warning}}"
230
+
183
231
  # ═══════════════ UI LANE ════════════════════════════════════════════════════
184
232
 
185
233
  # 7. Candidates for the UI lane, same shape. The service lane is already declared
@@ -198,10 +246,20 @@ workflow:
198
246
  - { name: ui_detected, from: "$.detected_summary" }
199
247
  - { name: ui_none_sentinel, from: "$.none_sentinel" }
200
248
 
249
+ # 7b. The UI stacks' rule sets, measured with the service lane just declared: a
250
+ # frontend rule set can name a file per backend stack (its API integration).
251
+ - id: ui_rules
252
+ action: execute_script
253
+ script: { ref: actions }
254
+ subcommand: stack-rules
255
+ args: ["--kind", "frontend"]
256
+ outputs:
257
+ - { name: ui_rules_note, from: "$.note" }
258
+
201
259
  # 8. Choose the UI stack.
202
260
  - id: select_ui_stack
203
261
  action: user_select
204
- prompt: "Select the UI (frontend) stack — it owns the web lane, and its root must not overlap the service lane's. This repo's own files point at: {{ui_detected}}. A detected stack leads the list. (none) declares a backend-only project, and stops the UI constitution half and the frontend rule files being authored:"
262
+ prompt: "Select the UI (frontend) stack — it owns the web lane, and its root must not overlap the service lane's. This repo's own files point at: {{ui_detected}}. A detected stack leads the list. (none) declares a backend-only project, and stops the UI constitution half and the frontend rule files being authored. {{ui_rules_note}}"
205
263
  options_from_variable: ui_options
206
264
  preselect_from: ui_preselect
207
265
  single_option_confirm: true
@@ -246,6 +304,23 @@ workflow:
246
304
  - { name: ui_test_path, from: "$.test_path" }
247
305
  - { name: ui_root_exists, from: "$.root_exists" }
248
306
 
307
+ - id: ui_rules_check
308
+ condition: "{{ui_declared}}"
309
+ action: execute_script
310
+ script: { ref: actions }
311
+ subcommand: stack-rules
312
+ args: ["--kind", "frontend", "--declared"]
313
+ outputs:
314
+ - { name: ui_rules_ok, from: "$.ok" }
315
+ - { name: ui_rules_warning, from: "$.warning" }
316
+ - { name: ui_rule_set, from: "$.verdict" }
317
+ - id: warn_ui_rules
318
+ condition: "{{ui_declared}}"
319
+ action: verify_context
320
+ require_true: ui_rules_ok
321
+ on_false: continue
322
+ on_false_message: "{{ui_rules_warning}}"
323
+
249
324
  # ═══════════════ CHECK + REPORT ═════════════════════════════════════════════
250
325
 
251
326
  # 10. Is what was just written sound? --allow-missing-root because a lane root folder
@@ -333,5 +408,7 @@ outputs:
333
408
  ui_detected: "{{ui_detected}}"
334
409
  prototype: "{{prototype}}"
335
410
  working_lane: "{{working_lane}}" # this developer's working lane, written to .appweaver/lane.json (not committed)
336
- note: "Lanes persisted to .appweaver/stacks.json. Each lane carries a STACK and a LANE ROOT FOLDER, and both are load-bearing. The LANE ROOT FOLDER is ownership: implement's service lane writes its code only under the service lane's and its frontend lane only under the UI lane's, build/test/run act on it, and onboard reverse-engineers out of it. Each lane also owns a TEST PATH, taken from its stack profile — tests/ beside a .NET src/, the lane root folder itself for a stack that keeps its tests inside — and implement writes the lane's tests there. Overlapping paths of either kind are refused here rather than discovered later as two generators writing one file. The build/test/run commands are project-supplied strings the engine shells out to, which is how it stays stdlib-only and stack-ignorant. api_detected / ui_detected report what this repo's own files pointed at, whether or not it was chosen, as '<stack> at <path>' — or 'nothing detected' on a greenfield repo with nothing to detect. A lane root folder that does not exist yet is NORMAL here and reported rather than refused: greenfield declares src/ before implement creates it. The on-disk check runs in the phases that read the subtree — implement, build, run and onboard all call lane-verify and STOP with the fix named. Declining a lane with (none) is a real declaration and removes any lane of that kind a previous run left behind; it is also what stops that lane's constitution half and rule files being authored. `prototype` is not a lane — it has a root and a run command so an operator can open it, and nothing else. Re-run this workflow any time to re-stack a lane, move a lane root folder, or drop a lane with (none); it is the only workflow that may write this file. It also wrote .appweaver/lane.json — this developer's WORKING lane, set to {{working_lane}} — which is not committed: each developer keeps their own. A developer who works on one lane runs /appweaver select-lane, and from then on plan, analyze-plan, implement, build and analyze-slice write only that lane while still reading the other one."
411
+ api_rule_set: "{{api_rule_set}}" # complete | partial | none | not-mapped — the service stack's rule set in the active project's map
412
+ ui_rule_set: "{{ui_rule_set}}" # the same for the UI stack
413
+ note: "Lanes persisted to .appweaver/stacks.json. Each lane carries a STACK and a LANE ROOT FOLDER, and both are load-bearing. The LANE ROOT FOLDER is ownership: implement's service lane writes its code only under the service lane's and its frontend lane only under the UI lane's, build/test/run act on it, and onboard reverse-engineers out of it. Each lane also owns a TEST PATH, taken from its stack profile — tests/ beside a .NET src/, the lane root folder itself for a stack that keeps its tests inside — and implement writes the lane's tests there. Overlapping paths of either kind are refused here rather than discovered later as two generators writing one file. The build/test/run commands are project-supplied strings the engine shells out to, which is how it stays stdlib-only and stack-ignorant. api_detected / ui_detected report what this repo's own files pointed at, whether or not it was chosen, as '<stack> at <path>' — or 'nothing detected' on a greenfield repo with nothing to detect. A lane root folder that does not exist yet is NORMAL here and reported rather than refused: greenfield declares src/ before implement creates it. The on-disk check runs in the phases that read the subtree — implement, build, run and onboard all call lane-verify and STOP with the fix named. Declining a lane with (none) is a real declaration and removes any lane of that kind a previous run left behind; it is also what stops that lane's constitution half and rule files being authored. `prototype` is not a lane — it has a root and a run command so an operator can open it, and nothing else. Re-run this workflow any time to re-stack a lane, move a lane root folder, or drop a lane with (none); it is the only workflow that may write this file. It also wrote .appweaver/lane.json — this developer's WORKING lane, set to {{working_lane}} — which is not committed: each developer keeps their own. A developer who works on one lane runs /appweaver select-lane, and from then on plan, analyze-plan, implement, build and analyze-slice write only that lane while still reading the other one. api_rule_set / ui_rule_set say how much of each chosen stack's technical rule set the active project's map finds on disk — complete, partial, none, or not-mapped where the map names no stack folder for that lane. A stack with none runs on ai-rules.md and the technical constitution; a warning above says what to write to change that."
337
414
  next: "GREENFIELD: /appweaver foundation → constitution → specify. The project rules live in guardrails/project-specific/, which .appweaver/domain.json names by default — fill in its rule files and its two maps; /appweaver select-domain switches to another project folder under guardrails/ when you keep more than one. On a project with no specs yet, constitution creates the default bounded context MainContext and its feature CoreFeature and makes both active; to use your own names, run /appweaver select-bounded-context <Name> and then /appweaver select-feature <Name> before specify. Once any bounded context exists, select-feature refuses to run until one is selected. foundation scaffolds every lane declared here in one run, backend then frontend — from a verified pack, from a spec the agent builds the project from, or skipped for a lane whose code already exists — and a spec build follows the technical rules the active project maps under `## /aw-spec-foundation-backend` and `## /aw-spec-foundation-frontend`. BROWNFIELD: /appweaver inventory — it scans the lane root folders declared here and proposes the bounded contexts and features as specs/proposed-inventory.md; edit that map, run /appweaver inventory apply, then /appweaver onboard <Context> once per bounded context, which reverse-engineers the governance out of the same lane root folders and activates it: onboard creates guardrails/brf-<domain>/ from the code and names it in .appweaver/domain.json. Run this workflow FIRST in either sequence: foundation scaffolds into the lane root folders declared here, constitution reads the declaration to decide which halves to author, and inventory and onboard read it to decide what to scan."
@@ -81,7 +81,7 @@
81
81
 
82
82
  name: aw-spec-specify
83
83
  description: Author the ACTIVE feature's specification AND its bounded context's domain model, together. The aggregate root, its entities, events and invariants (BR-n) live in specs/<Context>/domain-model.md — the one canonical definition every spec in the context cites by id and never restates. First call writes the master spec (US-1) plus the master model; a later call opens its OWN change (many may be open at once) and writes that change's delta-spec and delta-domain-model, leaving both masters untouched for finalize-delta to merge. Opening a change asks which lanes it touches — both by default — and records the answer on the delta-spec's **Lanes** line; the spec is written whole either way. Each change carries a WAVE — deltas sharing one are independent and developable in parallel, a higher wave is implemented after the lower ones land — and its model delta sits beside the master so every other open delta can see it while being authored. Spec phase only — plan/design is separate.
84
- version: 3.6.0
84
+ version: 3.7.1
85
85
  agent_agnostic: true
86
86
  dialect: aw-spec.orchestrator/v1
87
87
 
@@ -131,6 +131,10 @@ vars:
131
131
  change_lanes_csv: "" # DELTA path only — what the **Lanes** line now records
132
132
  open_count: "0" # how many changes are open on this feature after this call
133
133
  domain_model: "" # the CONTEXT's canonical model — bound by context-paths
134
+ model_guard_ok: "false" # model-guard-check's verdict on the bootstrap rewrite. DEFAULTS
135
+ # TO REFUSE: only the check's own `ok` lets the run past the gate
136
+ model_guard_losses: "" # what the rejected rewrite lost, one clause per item
137
+ model_guard_rejected: "" # where the rejected rewrite was kept
134
138
  pending_model_files: [] # every OTHER open delta's model change, for build_context
135
139
  pending_count: "0"
136
140
  next_wave: "1"
@@ -443,6 +447,19 @@ workflow:
443
447
  - .appweaver/memory/constitution.domain.md
444
448
  - { path: .appweaver/memory/constitution.ui.md, optional: true }
445
449
 
450
+ # THE REWRITE IS CHECKED. The engine writes the agent's reply over the WHOLE model, and
451
+ # from a context's second feature on that file holds the aggregates, the BR-/XBR- rules
452
+ # and the ledger rows other features rely on. The snapshot copies it aside; the check
453
+ # after the step compares the two (python/aw-model-guard.py) and, on a loss, puts the
454
+ # model back and keeps the rewrite as domain-model.rejected.md. Template placeholders are
455
+ # not recorded, so a context's first feature is held to nothing but its Context version.
456
+ - id: boot_model_snapshot
457
+ condition: "{{is_bootstrap}} == true"
458
+ action: execute_script
459
+ script: { ref: actions }
460
+ subcommand: model-guard-snapshot
461
+ args: ["--model", "{{domain_model}}"]
462
+
446
463
  - id: boot_model_generate
447
464
  condition: "{{is_bootstrap}} == true"
448
465
  action: agent_author
@@ -465,6 +482,12 @@ workflow:
465
482
  yet gets a new section. EXTEND the ubiquitous language. Do NOT rewrite, renumber
466
483
  or "tidy" what is already there — other specs cite those ids, and a renumbered BR
467
484
  breaks every citation silently.
485
+ - A SCRIPT COMPARES YOUR FILE WITH THE MODEL AS IT WAS, and puts the old one back if
486
+ any of these is gone: an `## Aggregate:` section under its name; a BR-n or XBR-n,
487
+ in the same section, with the same rule text, defined once; a name on an
488
+ aggregate's `Serving specs` line; a Ubiquitous Language term; a context the
489
+ Context Map names; a Change History row; the Context version. Add to any of them
490
+ freely; never remove, rename, renumber, move or reword one.
468
491
  - DDD IS THE RATIFIED MODELLING STANDARD (NON-NEGOTIABLE → the modelling-methodology
469
492
  standard in the sticky standards, wired per-project via tech-rule-bindings
470
493
  "## /aw-spec-specify", plus the domain's own DDD overlay): a consistent ubiquitous
@@ -481,6 +504,26 @@ workflow:
481
504
  - Do NOT write the Context version or the Change History version column — scripts
482
505
  stamp those. Fill the initial row's date and aggregate only.
483
506
 
507
+ - id: boot_model_check
508
+ condition: "{{is_bootstrap}} == true"
509
+ action: execute_script
510
+ script: { ref: actions }
511
+ subcommand: model-guard-check
512
+ args: ["--model", "{{domain_model}}"]
513
+ outputs:
514
+ - { name: model_guard_ok, from: "$.ok" }
515
+ - { name: model_guard_losses, from: "$.losses_text" }
516
+ - { name: model_guard_rejected, from: "$.rejected" }
517
+
518
+ # On a stop the model is already restored, and the unfinished-first-draft marker is still
519
+ # up, so the re-run asks to re-author the draft.
520
+ - id: boot_model_guard
521
+ condition: "{{is_bootstrap}} == true"
522
+ action: verify_context
523
+ require_true: model_guard_ok
524
+ on_false: stop
525
+ on_false_message: "The domain model lost content other features rely on, so {{domain_model}} was put back as it was before this run. Lost: {{model_guard_losses}}. The rejected rewrite is kept at {{model_guard_rejected}} for inspection. Run /appweaver specify again and choose \"Re-author the first draft\"."
526
+
484
527
  # ═════════════════════════ DELTA — spec.md exists ═════════════════════════
485
528
  # A later requirement = a new user story on the SAME aggregate. Delegate to the
486
529
  # delta-spec module: it opens the change (cr-setup) and authors BOTH of the change's
@@ -797,4 +840,4 @@ outputs:
797
840
  # Both paths spell out their WHOLE remaining chain. The bootstrap path named only
798
841
  # `plan` and stopped there, which left a first greenfield run with no successor at
799
842
  # the exact point the delta path was fully documented.
800
- next: "/appweaver plan — BOOTSTRAP authors backend-plan.md from the spec + the domain model (plus frontend-plan.md where a UI lane is declared); a DELTA (this run opened change {{cr_id}}, wave {{wave}}) authors the delta plans for it. BOOTSTRAP then flows: plan → analyze-plan → implement → build → analyze-slice → run → deploy (analyze-plan is the spec↔plan gate and it repairs and stops only while NO change is open). A change instead flows: analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta (finalize-delta merges the delta into spec.md + domain-model.md — not the lane plans, which are derived from those two — but leaves the change open; analyze-slice verifies the slice against the merged master; close-delta archives the change and bumps the version). ⚠️ With more than one change open, plan, analyze-plan-delta and implement ASK which one they are for (pass the id as the argument to skip the prompt) and activate the answer; finalize-delta and close-delta still act on whichever change is active — so finish or close the others deliberately."
843
+ next: "/appweaver plan — BOOTSTRAP authors api-plan.md and backend-plan.md from the spec + the domain model (plus frontend-plan.md where a UI lane is declared); a DELTA (this run opened change {{cr_id}}, wave {{wave}}) authors the delta plans for it. BOOTSTRAP then flows: plan → analyze-plan → implement → build → analyze-slice → run → deploy (analyze-plan is the spec↔plan gate and it repairs and stops only while NO change is open). A change instead flows: analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta (finalize-delta merges the delta into spec.md + domain-model.md, and the change's delta API plan into api-plan.md — not the lane plans, which are derived from those three — but leaves the change open; analyze-slice verifies the slice against the merged master; close-delta archives the change and bumps the version). ⚠️ With more than one change open, plan, analyze-plan-delta and implement ASK which one they are for (pass the id as the argument to skip the prompt) and activate the answer; finalize-delta and close-delta still act on whichever change is active — so finish or close the others deliberately."