aw-spec-cli 0.1.16 → 0.1.18

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 (70) hide show
  1. package/assets/.appweaver/DESIGN-FORMAT.md +18 -18
  2. package/assets/.appweaver/ORCH-DIALECT.md +1 -3
  3. package/assets/.appweaver/RULE-PARTITION.md +29 -13
  4. package/assets/.appweaver/STACK-PROFILES.md +10 -9
  5. package/assets/.appweaver/orchestrator/.state/smoke.state.json +32 -0
  6. package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +4 -1
  7. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +251 -0
  8. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +104 -35
  9. package/assets/.appweaver/orchestrator/orchestrator.py +5 -4
  10. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +94 -36
  11. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +1 -1
  12. package/assets/.appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md +115 -0
  13. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +2 -9
  14. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +16 -40
  15. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +38 -17
  16. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +8 -8
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +10 -11
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +673 -0
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +535 -0
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +643 -0
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +84 -701
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +7 -7
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +16 -14
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +2 -2
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +243 -144
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +877 -578
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -4
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +32 -91
  30. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
  31. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +836 -514
  32. package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +2 -2
  33. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +132 -0
  34. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +38 -5
  35. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +3 -3
  36. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +271 -979
  37. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1085 -3385
  38. package/assets/.appweaver/scripts/bash/aw-xref.sh +1 -2
  39. package/assets/.appweaver/scripts/bash/common.sh +47 -0
  40. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +914 -3220
  41. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
  42. package/assets/.appweaver/scripts/python/aw-payload-parity.py +19 -3
  43. package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
  44. package/assets/.appweaver/scripts/python/aw-xref.py +86 -350
  45. package/assets/.appweaver/templates/constitution-ui-template.md +1 -1
  46. package/assets/.appweaver/templates/delta-design-template.md +120 -30
  47. package/assets/.appweaver/templates/delta-domain-model-template.md +81 -0
  48. package/assets/.appweaver/templates/delta-spec-template.md +43 -17
  49. package/assets/.appweaver/templates/design-template.md +68 -98
  50. package/assets/.appweaver/templates/domain-model-template.md +130 -0
  51. package/assets/.appweaver/templates/okf-concept-template.md +2 -2
  52. package/assets/.appweaver/templates/source-map-template.md +3 -3
  53. package/assets/.appweaver/templates/spec-template.md +100 -122
  54. package/assets/.appweaver/templates/ui-delta-design-template.md +159 -0
  55. package/assets/.appweaver/templates/ui-design-template.md +46 -75
  56. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +7 -18
  57. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +34 -39
  58. package/assets/guardrails/requirement-spec.txt +2 -2
  59. package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
  60. package/package.json +1 -1
  61. package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +0 -105
  62. package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +0 -87
  63. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
  64. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -813
  65. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
  66. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -663
  67. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -600
  68. package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -1147
  69. package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1121
  70. package/assets/.appweaver/templates/story-template.md +0 -178
@@ -1,813 +0,0 @@
1
- # =============================================================================
2
- # aw-spec-analyze-design (orchestrator) — the spec ↔ FEATURE PLAN conformance GATE
3
- #
4
- # The discrete conformance gate on the forward path:
5
- # specify / sync-spec → plan (+ plan-ui) → ANALYZE-DESIGN → sync-stories → story-plan
6
- # spec.md is the ANCHOR. This stage proves that the feature's PLAN DOCUMENTS —
7
- # specs/<Aggregate>/backend-plan.md and, where a frontend lane is declared,
8
- # specs/<Aggregate>/frontend-plan.md — strictly follow it, in BOTH directions: every
9
- # spec id has a design home, and every design element has a spec reason.
10
- #
11
- # LOAD-BEARING, not advisory. story-plan reads backend-plan.md as BINDING feature
12
- # architecture and implement generates from the story pass beneath it, so a plan that
13
- # drifted from the spec propagates into code through documents that never re-ask the
14
- # question. This is the only stage that asks it — and it writes a machine-readable
15
- # verdict (plan-analyze-report.json) beside the human report, so a pipeline can branch
16
- # on the answer instead of parsing prose (§7 of Workflow-To-Build.md).
17
- #
18
- # ONE COMMAND, BOTH LANES — deliberately, unlike plan / plan-ui. Those split because
19
- # each lane's AUTHORING rules are a different set (tech-rule-bindings gives each its
20
- # own, and one command would hand the phase both lanes' whole rule surface). This stage
21
- # loads no lane rule set at all: its checklist is the DOMAIN gate overlay, which is
22
- # lane-neutral. And the question it exists to answer is a UNION question that neither
23
- # lane can answer alone — a UX- has its only home in frontend-plan.md, a BR- only in
24
- # backend-plan.md, and an FR- with a user-facing surface needs BOTH. Gating one lane at
25
- # a time would either miss every UX- or report every UI-bearing FR- as uncovered in the
26
- # service lane. There is no /appweaver analyze-design-ui, and there will not be one.
27
- #
28
- # IT DOES NOT AUTHOR, AND IT DOES NOT SCAFFOLD. Each feature plan has exactly ONE
29
- # author (/appweaver plan, /appweaver plan-ui). This stage never creates the document
30
- # it gates: an empty template conjured here would be measured against the spec, found
31
- # wholly non-conformant, and then written by the gate — authoring under another name,
32
- # with none of the author's lane rules, code reading or interface publication. A
33
- # declared lane with no plan STOPS and routes to that lane's author.
34
- #
35
- # FRESHNESS FIRST. A plan is conformant to the spec version it DERIVES FROM; certifying
36
- # one the spec has moved past is a false green, because it conforms to a contract that
37
- # no longer holds. plan-check runs per lane and the run STOPS on `behind` /
38
- # `edited_behind`, routing to plan / plan-ui — whose update mode conforms exactly the
39
- # ids the ledger moved and appends the plan's own history row. `untracked` (a migrated
40
- # legacy master with no baseline) is ADMITTED: the whole document is analysed against
41
- # the whole spec, and the stamp establishes the baseline that was missing.
42
- #
43
- # REPAIR IS BOUNDED AND RECORDED. On approval the gate closes the gaps it found and
44
- # nothing else — no new design, no version, no id. plan-stamp then appends the plan's
45
- # own Change History row naming the ids and re-baselines the body hash, so the rewrite
46
- # is IN the ledger and story-plan does not later read it as an unexplained hand edit.
47
- # The verdict records the gaps AS FOUND alongside the final status, so a green earned
48
- # by repair stays visible as one.
49
- #
50
- # ROUTES OUT, by what the gap actually is:
51
- # • the plan lags what the ledger moved → /appweaver plan · /appweaver plan-ui
52
- # • the plan is wrong and the spec is right → repair here, or hand-edit the plan and
53
- # re-run plan with accept_manual: true
54
- # • the SPEC is wrong → /appweaver sync-spec (⌛ not yet built — until it is, fold the change back by hand under .appweaver/SPEC-EDIT-PROTOCOL.md) (never repaired here)
55
- # • a gate-overlay constraint was never lifted into the spec (LIFT-FAILURE:)
56
- # → /appweaver specify
57
- #
58
- # Launch: /appweaver analyze-design [strategy]
59
- # Prereq: /appweaver plan (and /appweaver plan-ui where a frontend lane is declared).
60
- # =============================================================================
61
-
62
- name: aw-spec-analyze-design
63
- description: The spec ↔ feature-plan conformance gate, both lanes in one pass — proves backend-plan.md and frontend-plan.md strictly follow spec.md in both directions (every spec id has a design home; every design element has a spec reason), with the domain gate overlays as a lift backstop. Checks each lane's derivation freshness first and refuses to certify a plan the spec has moved past, routing to plan / plan-ui. Never scaffolds the document it gates. On approval it repairs only the gaps it found and plan-stamp records the repair in the plan's own ledger. Writes a two-directional markdown report and a machine-readable verdict beside it for CI.
64
- version: 3.0.0
65
- agent_agnostic: true
66
- dialect: aw-spec.orchestrator/v1
67
-
68
- scripts:
69
- actions:
70
- sh: .appweaver/scripts/bash/aw-spec-actions.sh
71
- ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
72
-
73
- standards:
74
- - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
75
-
76
- vars:
77
- # --- lane scope ---------------------------------------------------------
78
- be_declared: "false"
79
- fe_declared: "false"
80
- be_in_scope: "false" # a backend lane is declared ⇒ backend-plan.md is gated
81
- fe_in_scope: "false" # a frontend lane is declared ⇒ frontend-plan.md is gated
82
- have_lane: "false" # at least one lane in scope — false ⇒ nothing to gate
83
- # --- per-lane derivation tracking (plan-check) --------------------------
84
- be_state: "" # fresh | untracked | clean | behind | edited | edited_behind
85
- be_derived: ""
86
- be_changed: ""
87
- fe_state: ""
88
- fe_derived: ""
89
- fe_changed: ""
90
- # The plan shapes this run certifies, re-read after any repair (step 8b). Empty means the
91
- # lane was not in scope, which every reader treats as "not certified", never as a mismatch.
92
- be_final_sha: ""
93
- fe_final_sha: ""
94
- current_version: ""
95
- # Freshness gates default FALSE so a probe that could not run fails CLOSED. The
96
- # message vars carry a generic default for the same reason — an empty stop message
97
- # would leave the user with a refusal and no route.
98
- be_gate_ok: "false"
99
- fe_gate_ok: "false"
100
- be_gate_msg: "The backend plan's derivation check did not complete, so this gate cannot tell whether backend-plan.md is current with the spec. Re-run /appweaver analyze-design; if it keeps failing, run /appweaver plan to re-derive and re-baseline the plan."
101
- fe_gate_msg: "The frontend plan's derivation check did not complete, so this gate cannot tell whether frontend-plan.md is current with the spec. Re-run /appweaver analyze-design; if it keeps failing, run /appweaver plan-ui to re-derive and re-baseline the plan."
102
- # --- the analysis -------------------------------------------------------
103
- analyze_ok: "false"
104
- analyze_gaps: ""
105
- gap_count: "0" # gaps still open at the end of the loop
106
- gap_refs: ""
107
- be_needs_repair: "false"
108
- fe_needs_repair: "false"
109
- fe_repair_go: "false" # fe_needs_repair AND the frontend lane is in scope
110
- found_recorded: "false" # the first pass's findings are the report's subject
111
- found_gaps: ""
112
- found_count: "0"
113
- found_refs: ""
114
- repaired: "false"
115
- # The ids a repair pass actually rewrote — the model's own declaration, and NOT the same
116
- # set as gap_refs (which holds every finding, including the ones left deliberately open).
117
- repaired_refs: ""
118
- be_stamp_ok: "true" # vacuously true until a repair actually stamps
119
- fe_stamp_ok: "true"
120
- be_stamp_error: ""
121
- fe_stamp_error: ""
122
- report_path: ""
123
- result_file: ""
124
- # --- legacy delta guard -------------------------------------------------
125
- active_change: ""
126
- has_change: "false"
127
- no_open_change: "true" # NOT has_change ⇒ the masters are authoritative ⇒ this gate applies
128
- version: ""
129
-
130
- workflow:
131
-
132
- # 1. Which lanes exist. The DECLARATION is the question — not the lane-gate module,
133
- # whose on-disk check would refuse a greenfield project that declared a root it
134
- # has not scaffolded. A plan is gated as a document, before any code exists.
135
- - id: lane_resolve
136
- action: execute_script
137
- on_error: continue # never crash raw on an undeclared lane — the gates below explain it
138
- script: { ref: actions }
139
- subcommand: lane-resolve
140
- outputs:
141
- - { name: lanes_declared, from: "$.declared" }
142
- - { name: be_declared, from: "$.backend_declared" }
143
- - { name: fe_declared, from: "$.frontend_declared" }
144
-
145
- - id: be_scope
146
- condition: "{{be_declared}} == true"
147
- action: set
148
- vars: { be_in_scope: "true", have_lane: "true" }
149
- - id: fe_scope
150
- condition: "{{fe_declared}} == true"
151
- action: set
152
- vars: { fe_in_scope: "true", have_lane: "true" }
153
- - id: require_lane
154
- action: verify_context
155
- require_true: have_lane
156
- on_false: stop
157
- on_false_message: "No lane is declared in .appweaver/stacks.json, so there is no plan document to gate. Run /appweaver select-stack to declare the stack(s) and their source paths, then /appweaver plan (and /appweaver plan-ui where there is a UI) before re-running analyze-design."
158
-
159
- # 2. Resolve the current feature and require its spec — the anchor everything is
160
- # measured against. feature-paths reports both plan paths whether or not they
161
- # exist; the freshness gates below decide what that absence means.
162
- - id: feature_paths
163
- action: execute_script
164
- on_error: continue # never crash raw on 'no feature set' — the gate below explains it
165
- script: { ref: actions }
166
- subcommand: feature-paths
167
- outputs:
168
- - { name: feature_dir, from: "$.feature_dir" }
169
- - { name: spec_file, from: "$.spec_file" }
170
- - { name: spec_exists, from: "$.spec_exists" }
171
- - { name: be_plan, from: "$.backend_plan_file" }
172
- - { name: fe_plan, from: "$.frontend_plan_file" }
173
-
174
- - id: require_feature
175
- action: verify_context
176
- require_non_empty: feature_dir
177
- on_empty: stop
178
- on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run analyze-design."
179
-
180
- - id: verify_spec
181
- action: verify_context
182
- require_true: spec_exists
183
- on_false: stop
184
- on_false_message: "No spec.md for the current feature. Run /appweaver specify first — the spec is this gate's anchor, and there is nothing to measure a plan against without it."
185
-
186
- - id: report_paths
187
- action: set
188
- vars: { report_path: "{{feature_dir}}/plan-analyze-report.md" }
189
-
190
- # 2a. INVALIDATE any previous verdict, before anything downstream can stop. A gate
191
- # that writes its result only on completion leaves the LAST run's verdict on disk
192
- # for every run that aborts — a freshness stop, a refused stamp — and a pipeline
193
- # reading that file would act on a certification this run never made. Every exit
194
- # from here on therefore leaves at least a red; the real verdict overwrites it.
195
- - id: gate_result_open
196
- action: execute_script
197
- # The lenient loader needs a flow sequence on ONE line.
198
- args: ["--gate", "analyze-design", "--feature-dir", "{{feature_dir}}", "--ok", "false", "--report", "{{report_path}}", "--note", "analyze-design started and has not reached a verdict; a run that stops before certifying leaves this red on purpose"]
199
- script: { ref: actions }
200
- subcommand: gate-result
201
- outputs:
202
- - { name: result_file, from: "$.result_file" }
203
-
204
- # 2b. The spec's own ledger tripwire, the same fence plan and plan-ui carry. This
205
- # gate compares each plan's derived version against the spec's ledger, so a spec
206
- # that moved OUTSIDE the ledger makes every one of those comparisons a lie: the
207
- # plans would be certified current against a version the spec no longer is.
208
- - id: spec_guard
209
- action: include
210
- module: .appweaver/orchestrator/modules/spec-state-gate.orch.yaml
211
- with: { command: "/appweaver analyze-design" }
212
-
213
- # 2c. LEGACY GUARD. Nothing in the current model opens a change — specify writes the
214
- # spec directly and the delta family is retired (deletion scheduled). has_change
215
- # true therefore means a repo left mid-flight by that retired chain: delta-spec /
216
- # delta-design are still the authoritative in-flight edit, and repairing the
217
- # master plans under them would drift the masters out from beneath the delta
218
- # before finalize-delta merges it. Stop BEFORE any analysis so the wrong-stage run
219
- # has zero side effects.
220
- - id: read_change
221
- action: execute_script
222
- script: { ref: actions }
223
- subcommand: version-read
224
- args: ["--feature-dir", "{{feature_dir}}"]
225
- outputs:
226
- - { name: active_change, from: "$.active_change" }
227
- - { name: has_change, from: "$.has_change" }
228
- - { name: version, from: "$.version" }
229
-
230
- - id: flag_change_open
231
- condition: "{{has_change}} == true"
232
- action: set
233
- vars: { no_open_change: "false" }
234
-
235
- - id: require_no_change
236
- action: verify_context
237
- require_true: no_open_change
238
- on_false: stop
239
- on_false_message: "Change {{active_change}} is still OPEN on this feature — a leftover from the retired delta chain. This gate must not repair the master plans while a delta is in flight beneath them. Finish that change (/appweaver analyze-delta-design → implement-delta → build → finalize-delta → analyze-slice → close-delta) or abandon it, then re-run analyze-design to re-anchor the masters against the current spec."
240
-
241
- # 3. Per-lane derivation freshness. plan-check reports `fresh` when the document does
242
- # not exist at all, which is how a declared-but-unplanned lane is caught here
243
- # rather than by a scaffold this stage deliberately does not perform.
244
- - id: be_track
245
- action: execute_script
246
- condition: "{{be_in_scope}} == true"
247
- on_error: continue # a probe that fails binds nothing; be_gate_ok stays false ⇒ fails CLOSED
248
- script: { ref: actions }
249
- subcommand: plan-check
250
- args: ["--doc", "{{be_plan}}", "--feature-dir", "{{feature_dir}}"]
251
- outputs:
252
- - { name: be_state, from: "$.state" }
253
- - { name: be_derived, from: "$.derived_version" }
254
- - { name: be_changed, from: "$.changed_csv" }
255
- - { name: current_version, from: "$.current_version" }
256
-
257
- - id: fe_track
258
- action: execute_script
259
- condition: "{{fe_in_scope}} == true"
260
- on_error: continue # same fail-closed contract as the backend probe
261
- script: { ref: actions }
262
- subcommand: plan-check
263
- args: ["--doc", "{{fe_plan}}", "--feature-dir", "{{feature_dir}}"]
264
- outputs:
265
- - { name: fe_state, from: "$.state" }
266
- - { name: fe_derived, from: "$.derived_version" }
267
- - { name: fe_changed, from: "$.changed_csv" }
268
- - { name: current_version, from: "$.current_version" }
269
-
270
- # 3a. BACKEND freshness. clean / edited / untracked proceed; the rest stop. `edited`
271
- # proceeds because a hand-edited plan is exactly what this gate exists to judge —
272
- # it measures the document as it stands. `untracked` proceeds because a migrated
273
- # legacy master has no baseline to be behind, and the whole-document analysis plus
274
- # the stamp is what establishes one.
275
- - id: be_gate_na
276
- condition: "{{be_in_scope}} == false"
277
- action: set
278
- vars: { be_gate_ok: "true" }
279
- - id: be_gate_clean
280
- condition: "{{be_state}} == clean"
281
- action: set
282
- vars: { be_gate_ok: "true" }
283
- - id: be_gate_edited
284
- condition: "{{be_state}} == edited"
285
- action: set
286
- vars: { be_gate_ok: "true" }
287
- - id: be_gate_untracked
288
- condition: "{{be_state}} == untracked"
289
- action: set
290
- vars: { be_gate_ok: "true" }
291
- - id: be_msg_fresh
292
- condition: "{{be_state}} == fresh"
293
- action: set
294
- vars: { be_gate_msg: "A backend lane is declared but there is no backend-plan.md for this feature, so there is nothing to gate on the service lane — and this stage does not create the document it gates. Run /appweaver plan to author it from the spec, then re-run analyze-design." }
295
- - id: be_msg_behind
296
- condition: "{{be_state}} == behind"
297
- action: set
298
- vars: { be_gate_msg: "backend-plan.md derives from spec {{be_derived}} and the spec is now at {{current_version}} — certifying it would green-light a plan that conforms to a contract no longer in force. Requirements the ledger moved since: {{be_changed}}. Run /appweaver plan (update mode conforms exactly those ids and appends the plan's own history row), then re-run analyze-design." }
299
- - id: be_msg_edited_behind
300
- condition: "{{be_state}} == edited_behind"
301
- action: set
302
- vars: { be_gate_msg: "backend-plan.md was hand-edited AND the spec has moved past it (derived from {{be_derived}}, spec now {{current_version}}; ids moved: {{be_changed}}). Run /appweaver plan with accept_manual: true — it folds the manual edits in, conforms the moved ids and re-baselines — then re-run analyze-design." }
303
- - id: require_be_current
304
- action: verify_context
305
- require_true: be_gate_ok
306
- on_false: stop
307
- on_false_message: "{{be_gate_msg}}"
308
-
309
- # 3b. FRONTEND freshness — the same contract, routed to the frontend lane's author.
310
- - id: fe_gate_na
311
- condition: "{{fe_in_scope}} == false"
312
- action: set
313
- vars: { fe_gate_ok: "true" }
314
- - id: fe_gate_clean
315
- condition: "{{fe_state}} == clean"
316
- action: set
317
- vars: { fe_gate_ok: "true" }
318
- - id: fe_gate_edited
319
- condition: "{{fe_state}} == edited"
320
- action: set
321
- vars: { fe_gate_ok: "true" }
322
- - id: fe_gate_untracked
323
- condition: "{{fe_state}} == untracked"
324
- action: set
325
- vars: { fe_gate_ok: "true" }
326
- - id: fe_msg_fresh
327
- condition: "{{fe_state}} == fresh"
328
- action: set
329
- vars: { fe_gate_msg: "A frontend lane is declared but there is no frontend-plan.md for this feature. The spec's Experience Standards (UX-) have their ONLY design home there, so a green from this gate would claim a coverage this feature does not have. Run /appweaver plan-ui to author it, then re-run analyze-design." }
330
- - id: fe_msg_behind
331
- condition: "{{fe_state}} == behind"
332
- action: set
333
- vars: { fe_gate_msg: "frontend-plan.md derives from spec {{fe_derived}} and the spec is now at {{current_version}} — ids the ledger moved since: {{fe_changed}}. Run /appweaver plan-ui, then re-run analyze-design." }
334
- - id: fe_msg_edited_behind
335
- condition: "{{fe_state}} == edited_behind"
336
- action: set
337
- vars: { fe_gate_msg: "frontend-plan.md was hand-edited AND the spec has moved past it (derived from {{fe_derived}}, spec now {{current_version}}; ids moved: {{fe_changed}}). Run /appweaver plan-ui with accept_manual: true, then re-run analyze-design." }
338
- - id: require_fe_current
339
- action: verify_context
340
- require_true: fe_gate_ok
341
- on_false: stop
342
- on_false_message: "{{fe_gate_msg}}"
343
-
344
- # 4. Load the domain GATE overlays (domain-rule-bindings `/aw-spec-gate`) as the
345
- # conformance CHECKLIST — validation input only, never authoring. The analyze step
346
- # sees them (include_standards: true); the repair steps do NOT, because a plan
347
- # conforms to the SPEC and an overlay constraint absent from the spec is a lift
348
- # failure to report, not a rule to design against. Empty for a non-domain project.
349
- - id: resolve_gate_overlays
350
- action: execute_script
351
- script: { ref: actions }
352
- subcommand: resolve-domain
353
- args: ["--command", "aw-spec-gate"]
354
- outputs:
355
- - { name: gate_overlays, from: "$.candidates" }
356
- - id: load_gate_overlays
357
- action: load_standards
358
- files_from_variable: gate_overlays
359
- optional: true
360
-
361
- # 5. Pre-execution hooks. The phase keys stay `before_analyze` / `after_analyze` —
362
- # they are configuration contract and outlive the rename.
363
- - id: resolve_pre_hooks
364
- action: execute_script
365
- script: { ref: actions }
366
- subcommand: resolve-hooks
367
- args: ["--phase", "before_analyze"]
368
- outputs:
369
- - { name: before_hooks, from: "$.hooks" }
370
- - id: dispatch_pre_hooks
371
- action: dispatch_hooks
372
- hooks_from_variable: before_hooks
373
-
374
- # 6. The context both lanes are judged in. One envelope, because the coverage
375
- # question spans them: a UX- lives only in the frontend plan, a BR- only in the
376
- # backend plan, and an FR- with a user-facing surface needs a home in each.
377
- # Both plans are `optional:` ONLY because a lane may be undeclared — for a
378
- # declared lane the freshness gate above already proved the file exists, so an
379
- # in-scope plan is never silently skipped here.
380
- - id: ctx_analyze
381
- action: build_context
382
- context:
383
- - { path: "{{spec_file}}" }
384
- - { path: "{{be_plan}}", optional: true }
385
- - { path: "{{fe_plan}}", optional: true }
386
- - { path: "{{feature_dir}}/interfaces.md", optional: true } # the published surface both plans cite by IF- id
387
-
388
- # 7. Conformance loop: analyze → REPORT gaps + ASK → repair the found gaps → re-check.
389
- # Bounded (3x). "Report and Skip" leaves both plans untouched and stops at the
390
- # report, which is the right answer whenever the fix belongs to another stage.
391
- - id: analyze_loop
392
- action: loop
393
- while: "{{analyze_ok}} != true"
394
- max_iterations: 3
395
- steps:
396
- # A stale `true` from a previous iteration must never arm a write, so the
397
- # frontend repair permission is rebuilt from scratch on every pass.
398
- - id: fe_repair_reset
399
- action: set
400
- vars: { fe_repair_go: "false" }
401
-
402
- - id: analyze
403
- action: agent_query
404
- include_standards: true # the domain gate overlays (the checklist) ride in the standards
405
- schema: { ok: true, gap_count: 0, gap_refs: "<comma-separated spec ids the gaps touch>", repaired_refs: "<comma-separated spec ids THIS repair actually rewrote — empty on the first pass>", backend_repair: false, frontend_repair: false, gaps: [ { severity: "high", lane: "backend|frontend|cross", spec_ref: "<FR/BR/CN/DC/IC/UX/SC id, or the overlay name, or —>", where: "<document + section>", gap: "<how it drifts>", route: "<the command that fixes it>" } ] }
406
- mock: { ok: true, gap_count: 0, gap_refs: "", repaired_refs: "", backend_repair: false, frontend_repair: false, gaps: [] }
407
- task: |
408
- spec.md is the ANCHOR. Judge the feature's PLAN DOCUMENTS against it and list
409
- EVERY drift as a gap. In scope this run: backend-plan.md = {{be_in_scope}},
410
- frontend-plan.md = {{fe_in_scope}}. Judge ONLY the plans in scope; never
411
- report a gap against a lane that is not declared.
412
-
413
- SECTION NUMBERS ARE NOT STABLE, so identify every section by its ROLE and by
414
- the heading actually present in the document in front of you. A brownfield
415
- project's plans come from the code-derived template /appweaver onboard
416
- installs, whose layout differs from the greenfield one (its traceability,
417
- assumptions and deviations sections carry different numbers, and its
418
- component sections are named after the project's own structural axes). Any
419
- "§n" below is the GREENFIELD number, given only to say WHICH section is
420
- meant. Put the heading you actually found in `where`, and never report a
421
- missing section on the strength of a number.
422
-
423
- (1) FORWARD — COVERAGE. Every spec item has a design home, in the RIGHT lane:
424
- • FR- (requirement) — a home in backend-plan.md; and ALSO a screen/
425
- component/state in frontend-plan.md when it has a user-facing surface.
426
- • BR- (business rule) — backend-plan.md only, as a domain invariant.
427
- • CN- (constraint) — wherever it binds. A CN- that binds both lanes needs
428
- a home in BOTH; one that binds a single lane needs only that one.
429
- • DC- (data classification) — backend enforcement, plus what the frontend
430
- withholds from the client wherever a client sees the data.
431
- • IC- (integration contract) — the backend adapter/port that realises it;
432
- the frontend binds to it through interfaces.md, never directly.
433
- • UX- (experience standard) — frontend-plan.md ONLY. backend-plan.md does
434
- not carry them, so a UX- with no frontend row is a DROPPED requirement
435
- and is always a HIGH gap.
436
- • SC- (success criterion) — wherever it is met and how it is verified.
437
- A lifted CN-/DC-/IC- with no row in the relevant Traceability section is a
438
- HIGH gap: those sections are the two-way guarantee and must be complete.
439
- Edge Cases carry no ids by design — do not invent one for them.
440
-
441
- (2) THE DOMAIN MODEL MATCHES THE SPEC EXACTLY (backend). Entities and value
442
- objects are the spec's Key Entities under the same names, invariants are
443
- the spec's Business Rules, and the boundary is the spec's Aggregate
444
- Boundary. A renamed or invented entity is a HIGH gap.
445
-
446
- (3) BACKWARD — JUSTIFICATION. No design element exceeds or contradicts the
447
- spec. Anything without a spec basis is a drift UNLESS it is already logged
448
- as a DA-n assumption or a WA-n workaround in that plan's own ledgers — a
449
- logged item with a stated basis (including "per Const. §<n>" for standing
450
- technical machinery the constitution mandates) is accounted for, not a gap.
451
-
452
- (4) CROSS-LANE — the checks neither lane can make alone. Report these with
453
- lane "cross":
454
- • an FR- with a user-facing surface designed in one lane only;
455
- • a CN-/DC-/IC- that binds both lanes and is honoured in only one;
456
- • the frontend's contract-binding section (greenfield §5) citing an IF-
457
- id that interfaces.md does not publish, or an operation it needs that
458
- has no IF- entry and is not already raised as a contract gap;
459
- • either plan RESTATING the wording of a published interface instead of
460
- citing its IF- id — the spec owns requirement wording and interfaces.md
461
- owns contract wording; a second copy is drift by construction;
462
- • the two plans contradicting each other on the same behaviour.
463
-
464
- (5) COHERENCE. The backend layers hold (Application→Domain, API→Application,
465
- Infrastructure implements the declared ports); the frontend's state,
466
- components and flows are consistent with its own navigation. Both stay
467
- high-level and technology-agnostic.
468
-
469
- (6) OVERLAY BACKSTOP. The domain gate checklist is loaded in the standards.
470
- For each constraint it states (retention, immutability, append-only audit,
471
- data exposure, interop obligation …), confirm a plan realises it VIA a
472
- spec CN-/DC-/IC-. If a checklist constraint is in NEITHER the spec nor a
473
- plan, it is a HIGH gap: set spec_ref to the overlay name and PREFIX the
474
- gap text with "LIFT-FAILURE:". It was never lifted into the spec, so the
475
- fix is /appweaver specify — never fabricate it into a plan.
476
-
477
- For each gap set `route` to the command that actually fixes it: repair here
478
- when the plan is wrong and the spec is right; /appweaver sync-spec when the
479
- SPEC is what is wrong; /appweaver specify for a LIFT-FAILURE; /appweaver plan
480
- or /appweaver plan-ui when the plan simply lags the spec.
481
-
482
- Set backend_repair true only if at least one gap can be closed by rewriting
483
- backend-plan.md, and frontend_repair likewise for frontend-plan.md — false for
484
- a lane that is not in scope, and false when every gap on that lane routes to
485
- another command. Set gap_count to the number of gaps and gap_refs to their
486
- spec ids, comma-separated. Set ok=true ONLY when every plan in scope fully
487
- conforms to the spec AND no checklist constraint is unaccounted for.
488
-
489
- `repaired_refs` IS A DIFFERENT SET FROM `gap_refs`, and it goes into the plans'
490
- own Change History. Set it to the spec ids THIS PASS ACTUALLY REWROTE a plan for
491
- — empty on a pass that repaired nothing, which includes the first pass of every
492
- run. Never copy gap_refs into it: that set holds every finding, including the
493
- gaps you were told to leave OPEN (route sync-spec or specify), a LIFT-FAILURE
494
- whose spec_ref is an overlay name rather than an id, and any "—". Recording one
495
- of those claims the plan was conformed for a requirement nobody touched, in the
496
- ledger story-plan and the next /appweaver plan read.
497
- outputs:
498
- - { name: analyze_ok, from: "$.ok" }
499
- - { name: analyze_gaps, from: "$.gaps" }
500
- - { name: gap_count, from: "$.gap_count" }
501
- - { name: gap_refs, from: "$.gap_refs" }
502
- - { name: be_needs_repair, from: "$.backend_repair" }
503
- - { name: fe_needs_repair, from: "$.frontend_repair" }
504
- - { name: repaired_refs, from: "$.repaired_refs" }
505
-
506
- # The report's subject is the state the gate FOUND, not what survived repair —
507
- # a verdict that only ever describes the repaired document cannot show that a
508
- # repair happened, and a green earned by repair must stay visible as one.
509
- - id: record_found
510
- condition: "{{found_recorded}} == false"
511
- action: set
512
- vars:
513
- found_gaps: "{{analyze_gaps}}"
514
- found_count: "{{gap_count}}"
515
- found_refs: "{{gap_refs}}"
516
- found_recorded: "true"
517
-
518
- - id: conform_break
519
- action: break
520
- condition: "{{analyze_ok}} == true"
521
-
522
- - id: ask_repair
523
- action: ask
524
- prompt: |
525
- Spec ↔ plan gaps found (spec.md is the anchor; {{gap_count}} gap(s) on {{gap_refs}}):
526
- {{analyze_gaps}}
527
- Repair the plan document(s) to conform now, or write the report and stop?
528
- Repair closes only these gaps and records the rewrite in the plan's own
529
- Change History. Choose Report and Skip whenever the fix belongs elsewhere —
530
- a wrong SPEC goes through /appweaver sync-spec, a LIFT-FAILURE through
531
- /appweaver specify.
532
- # REPORT FIRST, BECAUSE THE FIRST OPTION IS THE UNATTENDED ANSWER. An `ask` with
533
- # nobody at the terminal resolves to option one, so an unattended run of this gate
534
- # takes whichever branch is written here. Repairing means REWRITING both feature
535
- # plans and re-stamping them, and a CI job that rewrites the documents it was asked
536
- # to check reports on a state it created. Reporting is the safe default: it writes
537
- # the report, leaves the plans untouched, and the person who reads it chooses.
538
- options: ["Report and Skip", "Repair"]
539
- answer_var: repair_choice
540
-
541
- - id: skip_break
542
- action: break
543
- condition: "{{repair_choice}} == Report and Skip"
544
-
545
- - id: mark_repaired
546
- action: set
547
- vars: { repaired: "true" }
548
-
549
- # The frontend lane's write permission is the AND of "the model found something
550
- # to fix there" and "that lane is in scope" — the evaluator takes single
551
- # comparisons only, so the second half arrives as the assigned value.
552
- - id: fe_repair_arm
553
- condition: "{{fe_needs_repair}} == true"
554
- action: set
555
- vars: { fe_repair_go: "{{fe_in_scope}}" }
556
-
557
- - id: reconcile_be
558
- condition: "{{be_needs_repair}} == true"
559
- action: agent_author
560
- include_standards: false # rewrite-in-place: the SPEC anchors, not the overlays
561
- output_target: { type: file, path: "{{be_plan}}" }
562
- task: |
563
- spec.md is the ANCHOR — update backend-plan.md (in context) to CONFORM to it,
564
- closing the gaps below whose lane is "backend" or "cross" AND whose route is
565
- this repair. The spec WINS every time.
566
- {{analyze_gaps}}
567
- - SECTION NUMBERS ARE NOT STABLE: this document may be the greenfield
568
- four-layer template or the code-derived one /appweaver onboard installs,
569
- whose layout differs. Work from the headings actually in the document; the
570
- "§n" below name the GREENFIELD section so you know which one is meant.
571
- NEVER renumber or reorder what is there.
572
- - Give each uncovered spec FR-/BR-/CN-/DC-/IC-/SC- a design home: a lifted
573
- CN-/DC-/IC- lands in a domain invariant (§1), the persistence component
574
- (§4.1), an external-integrations adapter row (§4.2), or a cross-cutting
575
- row (§5).
576
- - Realign the domain model to the spec: entity and value-object names = the
577
- spec's Key Entities, invariants = the spec's Business Rules, boundary = the
578
- spec's Aggregate Boundary.
579
- - REMOVE, or move to a DA-n assumption, any design element with no spec basis.
580
- EXCEPTION: do NOT remove a cross-cutting element the technical constitution
581
- mandates — standing technical or infrastructure machinery rather than
582
- invented domain logic. MOVE it to a DA-n citing the mandating principle
583
- ("per Const. §<n>"); removing it could violate the constitution. Reserve
584
- REMOVE for genuine over-reach — invented domain entities, rules or behaviour
585
- the spec omits.
586
- - The published-surface section (§3) is an INDEX into interfaces.md: cite IF-
587
- ids, never restate a published contract's wording. If a gap says an operation
588
- has no IF- entry, note it there as a contract gap to publish — do not invent
589
- an IF- id, and never edit interfaces.md from here; /appweaver plan is its
590
- only publisher.
591
- - A "LIFT-FAILURE:" gap CANNOT be closed here: the constraint is not in the
592
- spec and you must not invent a domain rule the spec omits. Leave it open and
593
- add a DA-n reading "lift-failure: <constraint> — re-run /appweaver specify to
594
- lift it into CN-/DC-/IC-".
595
- - Leave every gap routed to /appweaver sync-spec or /appweaver specify OPEN.
596
- Fixing the plan to match a wrong spec buries the defect in the document that
597
- is supposed to reveal it.
598
- - Keep the Traceability section (§6) in sync with what you changed. Preserve
599
- every already-conformant section VERBATIM, and preserve the Assumptions (§7),
600
- Workarounds/Deviations (§8) and Change History (§9) sections as LEDGERS —
601
- append to them, never rewrite, renumber or drop them. Dropping the Change
602
- History section is refused downstream and stops the run. Write NO version and
603
- NO history row: plan-stamp does both.
604
- - Do NOT change spec.md, frontend-plan.md, interfaces.md or adr.md.
605
- Return the COMPLETE updated backend-plan.md content only.
606
-
607
- # plan-stamp is the plan's only version authority. Repairing without stamping
608
- # would leave the document reading as an unexplained hand edit to story-plan and
609
- # to the next /appweaver plan, and the rewrite would be absent from its ledger.
610
- - id: stamp_be
611
- condition: "{{be_needs_repair}} == true"
612
- action: execute_script
613
- script: { ref: actions }
614
- subcommand: plan-stamp
615
- # --ids is WHAT THIS REPAIR WROTE, not every gap the pass found. gap_refs is the
616
- # whole finding set — including gaps the repair prompt is told to leave OPEN (route
617
- # sync-spec / specify), a LIFT-FAILURE whose spec_ref is an overlay name rather than
618
- # an id, and the literal "—" the schema permits. Stamping those recorded, in the
619
- # plan's own Change History, that the repair conformed ids it never touched — and
620
- # that ledger is what story-plan and the next /appweaver plan read.
621
- # --note carries the plan's tracking STATE, because a repair over an `edited`
622
- # document re-baselines a hand edit that /appweaver plan would have refused without
623
- # accept_manual: true. Admitting the edit is right; adopting it silently is not.
624
- args: ["--doc", "{{be_plan}}", "--feature-dir", "{{feature_dir}}", "--ids", "{{repaired_refs}}", "--note", "Conformance repair (analyze-design) over a plan in state '{{be_state}}'"]
625
- outputs:
626
- - { name: be_stamp_ok, from: "$.ok" }
627
- - { name: be_stamp_error, from: "$.error" }
628
-
629
- - id: reconcile_fe
630
- condition: "{{fe_repair_go}} == true"
631
- action: agent_author
632
- include_standards: false
633
- output_target: { type: file, path: "{{fe_plan}}" }
634
- task: |
635
- spec.md is the ANCHOR — update frontend-plan.md (in context) to CONFORM to it,
636
- closing the gaps below whose lane is "frontend" or "cross" AND whose route is
637
- this repair. The spec WINS every time.
638
- {{analyze_gaps}}
639
- - Give every uncovered UX- a home — this document is their ONLY home — and
640
- every FR- with a user-facing surface a screen, component or state.
641
- - Honour each CN-/DC- that binds this lane: a DC- says what the client is not
642
- shown, a CN- a bound the experience must meet.
643
- - SECTION NUMBERS ARE NOT STABLE — work from the headings actually in the
644
- document; the "§n" here name the greenfield sections so you know which is
645
- meant, and nothing is renumbered or reordered.
646
- - The Contract Binding section (§5) cites interfaces.md by IF- id and never
647
- restates a contract's wording. An operation with no published entry keeps the
648
- "(unpublished)" marker and is listed under contract gaps to raise — this
649
- lane never writes interfaces.md; /appweaver plan publishes it.
650
- - REMOVE, or move to a DA-n, any element with no spec basis, except what
651
- constitution.ui.md mandates — that moves to a DA-n citing the basis.
652
- - Leave every gap routed to /appweaver sync-spec or /appweaver specify OPEN.
653
- - Keep the Traceability section (§7) in sync. Preserve every already-conformant
654
- section VERBATIM, and preserve the Assumptions (§8), Workarounds (§9) and
655
- Change History sections as LEDGERS — append, never rewrite, renumber or drop.
656
- Dropping the Change History section is refused downstream and stops the run.
657
- Write NO version and NO history row: plan-stamp does both.
658
- - Do NOT change spec.md, backend-plan.md, interfaces.md or adr.md.
659
- Return the COMPLETE updated frontend-plan.md content only.
660
-
661
- - id: stamp_fe
662
- condition: "{{fe_repair_go}} == true"
663
- action: execute_script
664
- script: { ref: actions }
665
- subcommand: plan-stamp
666
- # Same two rules as the service lane: the ids this repair actually wrote, and the
667
- # tracking state the repair ran over. See stamp_be.
668
- args: ["--doc", "{{fe_plan}}", "--feature-dir", "{{feature_dir}}", "--ids", "{{repaired_refs}}", "--note", "Conformance repair (analyze-design) over a plan in state '{{fe_state}}'"]
669
- outputs:
670
- - { name: fe_stamp_ok, from: "$.ok" }
671
- - { name: fe_stamp_error, from: "$.error" }
672
-
673
- # 7b. The ledger-survival fence. plan-stamp refuses to baseline a document whose
674
- # Change History section went missing, so ok=false here means a repair landed on
675
- # disk having dropped the plan's ledger. That is the one loss this model never
676
- # absorbs silently — stop on it, before any verdict is written.
677
- - id: be_stamp_fence
678
- action: verify_context
679
- require_true: be_stamp_ok
680
- on_false: stop
681
- on_false_message: "The repair to backend-plan.md was written but NOT baselined: {{be_stamp_error}}. The rewrite dropped a section plan-stamp refuses to stamp over (its Change History ledger). Restore that section in backend-plan.md — git diff shows what the repair removed — then re-run analyze-design."
682
- - id: fe_stamp_fence
683
- action: verify_context
684
- require_true: fe_stamp_ok
685
- on_false: stop
686
- on_false_message: "The repair to frontend-plan.md was written but NOT baselined: {{fe_stamp_error}}. The rewrite dropped a section plan-stamp refuses to stamp over (its Change History ledger). Restore that section in frontend-plan.md — git diff shows what the repair removed — then re-run analyze-design."
687
-
688
- # 8. The human report — written on EVERY run, green or red. It is the two-directional
689
- # coverage record, and a gate that leaves nothing behind when it passes gives a
690
- # reviewer no evidence that it ran at all.
691
- - id: write_report
692
- action: agent_author
693
- include_standards: false
694
- output_target: { type: file, path: "{{report_path}}" }
695
- task: |
696
- Write the Plan Analyze Report for this feature to the output.
697
- Verdict: conformant={{analyze_ok}}. Gaps as FOUND: {{found_count}} on
698
- {{found_refs}}. Gaps still open: {{gap_count}}. Repair was run: {{repaired}}.
699
- Lanes gated: backend={{be_in_scope}} (tracking {{be_state}}, derived from spec
700
- {{be_derived}}), frontend={{fe_in_scope}} (tracking {{fe_state}}, derived from
701
- spec {{fe_derived}}). Spec version: {{current_version}}.
702
- The gaps as found:
703
- {{found_gaps}}
704
- Markdown format:
705
- - "# Plan Analyze Report — <aggregate>", then a line naming the spec version and
706
- each gated plan with its tracking state.
707
- - A verdict line: "verdict: CONFORMS ✓" or "verdict: GAPS ✗ · <n> open".
708
- WHERE A REPAIR RAN OVER A HAND-EDITED PLAN, say so on that line — backend-plan.md
709
- tracking state was "{{be_state}}" and frontend-plan.md "{{fe_state}}", and an
710
- `edited` or `edited_behind` there means the repair re-baselined a hand edit that
711
- /appweaver plan would have refused without accept_manual: true. Name the document,
712
- the state, and that the edit is now adopted into the plan's baseline — whoever made
713
- it should confirm the repair kept its intent.
714
- When repair ran, say so and give both numbers — found and still open.
715
- - "## Coverage (spec → plan)": a table | spec id | type | lane(s) it needs |
716
- design home | status |, one row per spec id you can see in the spec, marking
717
- each covered or UNCOVERED. This is the forward half of the two-way guarantee
718
- and it is the reason this report exists — write it even on a green run.
719
- - "## Gaps": a table | # | severity | lane | spec ref | where | gap | route |.
720
- Omit the section when there were none.
721
- - "## How to resolve": group by route and give the exact command for each — the
722
- spec is the anchor, so a wrong plan is repaired here or via /appweaver plan
723
- with accept_manual, a wrong SPEC goes through /appweaver sync-spec, and a
724
- "LIFT-FAILURE:" goes through /appweaver specify. Omit on a green run.
725
- Do not invent ids, versions or dates beyond the values given above.
726
- Return the complete report markdown only.
727
-
728
- # 8b. THE SHAPE BEING CERTIFIED, read at the last possible moment. Taken here rather than
729
- # from the freshness probes at the top, because the repair loop may have rewritten and
730
- # re-stamped both plans in between — recording the pre-repair hash would certify a
731
- # document that no longer exists and fail every downstream comparison on a green run.
732
- # Per lane and conditional: a lane out of scope has no hash and gets an empty field,
733
- # which the readers treat as "this lane was not certified" rather than as a mismatch.
734
- - id: be_final_hash
735
- condition: "{{be_in_scope}} == true"
736
- action: execute_script
737
- on_error: continue
738
- script: { ref: actions }
739
- subcommand: plan-check
740
- args: ["--doc", "{{be_plan}}", "--feature-dir", "{{feature_dir}}"]
741
- outputs:
742
- - { name: be_final_sha, from: "$.body_sha256" }
743
- - id: fe_final_hash
744
- condition: "{{fe_in_scope}} == true"
745
- action: execute_script
746
- on_error: continue
747
- script: { ref: actions }
748
- subcommand: plan-check
749
- args: ["--doc", "{{fe_plan}}", "--feature-dir", "{{feature_dir}}"]
750
- outputs:
751
- - { name: fe_final_sha, from: "$.body_sha256" }
752
-
753
- # 9. The machine-readable verdict, written beside the report by the SCRIPT — CI
754
- # cannot branch on prose, and a verdict is exactly the kind of dated, derived
755
- # record the model never writes. `ok` from this step means the file was written;
756
- # the pass/fail verdict is `status` inside it.
757
- - id: gate_result
758
- action: execute_script
759
- script: { ref: actions }
760
- subcommand: gate-result
761
- # The lenient loader needs a flow sequence on ONE line.
762
- # backend_sha256 / frontend_sha256 are WHAT THIS RUN CERTIFIED — the shape each plan was
763
- # in when it was judged. Nothing else in the verdict can say it: backend_state and
764
- # backend_derived do not move when a plan is hand-edited, so a plan approved here and
765
- # edited afterwards was indistinguishable from one still in the approved shape. story-plan
766
- # and both implements compare against these, which is what makes law 40 (approved,
767
- # changed, never re-approved must not reach code) enforceable rather than stated.
768
- # RE-READ AFTER ANY REPAIR: the loop above may have rewritten and re-stamped the plans, so
769
- # the hash recorded has to be the one on disk NOW, not the one the freshness probe saw.
770
- args: ["--gate", "analyze-design", "--feature-dir", "{{feature_dir}}", "--ok", "{{analyze_ok}}", "--spec-version", "{{current_version}}", "--report", "{{report_path}}", "--gap-count", "{{found_count}}", "--gap-refs", "{{found_refs}}", "--field", "gaps_open={{gap_count}}", "--field", "repaired={{repaired}}", "--field", "backend_plan={{be_plan}}", "--field", "backend_in_scope={{be_in_scope}}", "--field", "backend_state={{be_state}}", "--field", "backend_derived={{be_derived}}", "--field", "backend_sha256={{be_final_sha}}", "--field", "frontend_plan={{fe_plan}}", "--field", "frontend_in_scope={{fe_in_scope}}", "--field", "frontend_state={{fe_state}}", "--field", "frontend_derived={{fe_derived}}", "--field", "frontend_sha256={{fe_final_sha}}"]
771
- outputs:
772
- - { name: result_file, from: "$.result_file" }
773
-
774
- - id: analyze_gate
775
- action: verify_context
776
- require_true: analyze_ok
777
- on_false: stop
778
- on_false_message: "The feature plans do not conform to spec {{current_version}} — {{gap_count}} gap(s) open on {{gap_refs}}. The full two-directional report is at {{report_path}} and the machine-readable verdict at {{result_file}}. Repair was skipped, or the gaps could not be closed in 3 passes. Route by gap: a wrong PLAN is repaired here or by hand-editing it and re-running /appweaver plan (or plan-ui) with accept_manual: true; a wrong SPEC goes through /appweaver sync-spec; a 'LIFT-FAILURE:' gap means a domain constraint from the gate checklist was never lifted into the spec, so re-run /appweaver specify to add it as a CN-/DC-/IC-."
779
-
780
- # (No version is written here by design. This gate proves conformance; close-story is
781
- # the version authority downstream, and plan-stamp the plans' own.)
782
-
783
- # 10. Post-execution hooks.
784
- - id: resolve_post_hooks
785
- action: execute_script
786
- script: { ref: actions }
787
- subcommand: resolve-hooks
788
- args: ["--phase", "after_analyze"]
789
- outputs:
790
- - { name: after_hooks, from: "$.hooks" }
791
- - id: dispatch_post_hooks
792
- action: dispatch_hooks
793
- hooks_from_variable: after_hooks
794
-
795
- outputs:
796
- feature_dir: "{{feature_dir}}"
797
- spec_version: "{{current_version}}"
798
- backend_plan: "{{be_plan}}"
799
- backend_in_scope: "{{be_in_scope}}"
800
- backend_state: "{{be_state}}"
801
- backend_derived: "{{be_derived}}"
802
- frontend_plan: "{{fe_plan}}"
803
- frontend_in_scope: "{{fe_in_scope}}"
804
- frontend_state: "{{fe_state}}"
805
- frontend_derived: "{{fe_derived}}"
806
- conformant: "{{analyze_ok}}"
807
- gaps_found: "{{found_count}}"
808
- gaps_open: "{{gap_count}}"
809
- repaired: "{{repaired}}"
810
- report: "{{report_path}}"
811
- result_json: "{{result_file}}"
812
- gaps: "{{analyze_gaps}}"
813
- next: "Both lanes' plans conform to spec {{current_version}} — the verdict is at {{result_file}} for CI and the coverage report at {{report_path}}. Next: /appweaver sync-stories to plan the backlog against this spec version, then /appweaver story-plan <story-id> for the slice, which reads backend-plan.md as binding feature architecture. Re-run this gate after EVERY /appweaver plan and /appweaver plan-ui — it is what keeps that binding honest. NOTE repaired=true means this gate rewrote a plan to close its own findings and plan-stamp recorded the rewrite in that plan's Change History; gaps_found is what it found before repairing, so review the diff rather than reading the green alone."