aw-spec-cli 0.1.15 → 0.1.17

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 (69) 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 +78 -626
  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 +882 -464
  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 +832 -477
  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 -892
  37. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1096 -2911
  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 +936 -2766
  41. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
  42. package/assets/.appweaver/scripts/python/aw-payload-parity.py +174 -0
  43. package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
  44. package/assets/.appweaver/scripts/python/aw-xref.py +185 -292
  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/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
  63. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -749
  64. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
  65. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -540
  66. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -597
  67. package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -945
  68. package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1040
  69. package/assets/.appweaver/templates/story-template.md +0 -147
@@ -1,749 +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 (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
- current_version: ""
91
- # Freshness gates default FALSE so a probe that could not run fails CLOSED. The
92
- # message vars carry a generic default for the same reason — an empty stop message
93
- # would leave the user with a refusal and no route.
94
- be_gate_ok: "false"
95
- fe_gate_ok: "false"
96
- 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."
97
- 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."
98
- # --- the analysis -------------------------------------------------------
99
- analyze_ok: "false"
100
- analyze_gaps: ""
101
- gap_count: "0" # gaps still open at the end of the loop
102
- gap_refs: ""
103
- be_needs_repair: "false"
104
- fe_needs_repair: "false"
105
- fe_repair_go: "false" # fe_needs_repair AND the frontend lane is in scope
106
- found_recorded: "false" # the first pass's findings are the report's subject
107
- found_gaps: ""
108
- found_count: "0"
109
- found_refs: ""
110
- repaired: "false"
111
- be_stamp_ok: "true" # vacuously true until a repair actually stamps
112
- fe_stamp_ok: "true"
113
- be_stamp_error: ""
114
- fe_stamp_error: ""
115
- report_path: ""
116
- result_file: ""
117
- # --- legacy delta guard -------------------------------------------------
118
- active_change: ""
119
- has_change: "false"
120
- no_open_change: "true" # NOT has_change ⇒ the masters are authoritative ⇒ this gate applies
121
- version: ""
122
-
123
- workflow:
124
-
125
- # 1. Which lanes exist. The DECLARATION is the question — not the lane-gate module,
126
- # whose on-disk check would refuse a greenfield project that declared a root it
127
- # has not scaffolded. A plan is gated as a document, before any code exists.
128
- - id: lane_resolve
129
- action: execute_script
130
- on_error: continue # never crash raw on an undeclared lane — the gates below explain it
131
- script: { ref: actions }
132
- subcommand: lane-resolve
133
- outputs:
134
- - { name: lanes_declared, from: "$.declared" }
135
- - { name: be_declared, from: "$.backend_declared" }
136
- - { name: fe_declared, from: "$.frontend_declared" }
137
-
138
- - id: be_scope
139
- condition: "{{be_declared}} == true"
140
- action: set
141
- vars: { be_in_scope: "true", have_lane: "true" }
142
- - id: fe_scope
143
- condition: "{{fe_declared}} == true"
144
- action: set
145
- vars: { fe_in_scope: "true", have_lane: "true" }
146
- - id: require_lane
147
- action: verify_context
148
- require_true: have_lane
149
- on_false: stop
150
- 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."
151
-
152
- # 2. Resolve the current feature and require its spec — the anchor everything is
153
- # measured against. feature-paths reports both plan paths whether or not they
154
- # exist; the freshness gates below decide what that absence means.
155
- - id: feature_paths
156
- action: execute_script
157
- on_error: continue # never crash raw on 'no feature set' — the gate below explains it
158
- script: { ref: actions }
159
- subcommand: feature-paths
160
- outputs:
161
- - { name: feature_dir, from: "$.feature_dir" }
162
- - { name: spec_file, from: "$.spec_file" }
163
- - { name: spec_exists, from: "$.spec_exists" }
164
- - { name: be_plan, from: "$.backend_plan_file" }
165
- - { name: fe_plan, from: "$.frontend_plan_file" }
166
-
167
- - id: require_feature
168
- action: verify_context
169
- require_non_empty: feature_dir
170
- on_empty: stop
171
- on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run analyze-design."
172
-
173
- - id: verify_spec
174
- action: verify_context
175
- require_true: spec_exists
176
- on_false: stop
177
- 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."
178
-
179
- - id: report_paths
180
- action: set
181
- vars: { report_path: "{{feature_dir}}/plan-analyze-report.md" }
182
-
183
- # 2a. INVALIDATE any previous verdict, before anything downstream can stop. A gate
184
- # that writes its result only on completion leaves the LAST run's verdict on disk
185
- # for every run that aborts — a freshness stop, a refused stamp — and a pipeline
186
- # reading that file would act on a certification this run never made. Every exit
187
- # from here on therefore leaves at least a red; the real verdict overwrites it.
188
- - id: gate_result_open
189
- action: execute_script
190
- # The lenient loader needs a flow sequence on ONE line.
191
- 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"]
192
- script: { ref: actions }
193
- subcommand: gate-result
194
- outputs:
195
- - { name: result_file, from: "$.result_file" }
196
-
197
- # 2b. The spec's own ledger tripwire, the same fence plan and plan-ui carry. This
198
- # gate compares each plan's derived version against the spec's ledger, so a spec
199
- # that moved OUTSIDE the ledger makes every one of those comparisons a lie: the
200
- # plans would be certified current against a version the spec no longer is.
201
- - id: spec_guard_read
202
- action: execute_script
203
- script: { ref: actions }
204
- subcommand: spec-check
205
- args: ["--feature-dir", "{{feature_dir}}"]
206
- outputs:
207
- - { name: spec_ok, from: "$.ok" }
208
- - { name: spec_state, from: "$.state" }
209
- - id: spec_guard
210
- action: verify_context
211
- require_true: spec_ok
212
- on_false: stop
213
- on_false_message: "spec.md has moved outside the ledger (state: {{spec_state}}), so its version no longer names its contents and no plan can be certified against it. Record what changed in a Change History row (or fold it back through /appweaver sync-spec), re-run spec-stamp to re-baseline, then re-run analyze-design."
214
-
215
- # 2c. LEGACY GUARD. Nothing in the current model opens a change — specify writes the
216
- # spec directly and the delta family is retired (deletion scheduled). has_change
217
- # true therefore means a repo left mid-flight by that retired chain: delta-spec /
218
- # delta-design are still the authoritative in-flight edit, and repairing the
219
- # master plans under them would drift the masters out from beneath the delta
220
- # before finalize-delta merges it. Stop BEFORE any analysis so the wrong-stage run
221
- # has zero side effects.
222
- - id: read_change
223
- action: execute_script
224
- script: { ref: actions }
225
- subcommand: version-read
226
- args: ["--feature-dir", "{{feature_dir}}"]
227
- outputs:
228
- - { name: active_change, from: "$.active_change" }
229
- - { name: has_change, from: "$.has_change" }
230
- - { name: version, from: "$.version" }
231
-
232
- - id: flag_change_open
233
- condition: "{{has_change}} == true"
234
- action: set
235
- vars: { no_open_change: "false" }
236
-
237
- - id: require_no_change
238
- action: verify_context
239
- require_true: no_open_change
240
- on_false: stop
241
- 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."
242
-
243
- # 3. Per-lane derivation freshness. plan-check reports `fresh` when the document does
244
- # not exist at all, which is how a declared-but-unplanned lane is caught here
245
- # rather than by a scaffold this stage deliberately does not perform.
246
- - id: be_track
247
- action: execute_script
248
- condition: "{{be_in_scope}} == true"
249
- on_error: continue # a probe that fails binds nothing; be_gate_ok stays false ⇒ fails CLOSED
250
- script: { ref: actions }
251
- subcommand: plan-check
252
- args: ["--doc", "{{be_plan}}", "--feature-dir", "{{feature_dir}}"]
253
- outputs:
254
- - { name: be_state, from: "$.state" }
255
- - { name: be_derived, from: "$.derived_version" }
256
- - { name: be_changed, from: "$.changed_csv" }
257
- - { name: current_version, from: "$.current_version" }
258
-
259
- - id: fe_track
260
- action: execute_script
261
- condition: "{{fe_in_scope}} == true"
262
- on_error: continue # same fail-closed contract as the backend probe
263
- script: { ref: actions }
264
- subcommand: plan-check
265
- args: ["--doc", "{{fe_plan}}", "--feature-dir", "{{feature_dir}}"]
266
- outputs:
267
- - { name: fe_state, from: "$.state" }
268
- - { name: fe_derived, from: "$.derived_version" }
269
- - { name: fe_changed, from: "$.changed_csv" }
270
- - { name: current_version, from: "$.current_version" }
271
-
272
- # 3a. BACKEND freshness. clean / edited / untracked proceed; the rest stop. `edited`
273
- # proceeds because a hand-edited plan is exactly what this gate exists to judge —
274
- # it measures the document as it stands. `untracked` proceeds because a migrated
275
- # legacy master has no baseline to be behind, and the whole-document analysis plus
276
- # the stamp is what establishes one.
277
- - id: be_gate_na
278
- condition: "{{be_in_scope}} == false"
279
- action: set
280
- vars: { be_gate_ok: "true" }
281
- - id: be_gate_clean
282
- condition: "{{be_state}} == clean"
283
- action: set
284
- vars: { be_gate_ok: "true" }
285
- - id: be_gate_edited
286
- condition: "{{be_state}} == edited"
287
- action: set
288
- vars: { be_gate_ok: "true" }
289
- - id: be_gate_untracked
290
- condition: "{{be_state}} == untracked"
291
- action: set
292
- vars: { be_gate_ok: "true" }
293
- - id: be_msg_fresh
294
- condition: "{{be_state}} == fresh"
295
- action: set
296
- 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." }
297
- - id: be_msg_behind
298
- condition: "{{be_state}} == behind"
299
- action: set
300
- 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." }
301
- - id: be_msg_edited_behind
302
- condition: "{{be_state}} == edited_behind"
303
- action: set
304
- 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." }
305
- - id: require_be_current
306
- action: verify_context
307
- require_true: be_gate_ok
308
- on_false: stop
309
- on_false_message: "{{be_gate_msg}}"
310
-
311
- # 3b. FRONTEND freshness — the same contract, routed to the frontend lane's author.
312
- - id: fe_gate_na
313
- condition: "{{fe_in_scope}} == false"
314
- action: set
315
- vars: { fe_gate_ok: "true" }
316
- - id: fe_gate_clean
317
- condition: "{{fe_state}} == clean"
318
- action: set
319
- vars: { fe_gate_ok: "true" }
320
- - id: fe_gate_edited
321
- condition: "{{fe_state}} == edited"
322
- action: set
323
- vars: { fe_gate_ok: "true" }
324
- - id: fe_gate_untracked
325
- condition: "{{fe_state}} == untracked"
326
- action: set
327
- vars: { fe_gate_ok: "true" }
328
- - id: fe_msg_fresh
329
- condition: "{{fe_state}} == fresh"
330
- action: set
331
- 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." }
332
- - id: fe_msg_behind
333
- condition: "{{fe_state}} == behind"
334
- action: set
335
- 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." }
336
- - id: fe_msg_edited_behind
337
- condition: "{{fe_state}} == edited_behind"
338
- action: set
339
- 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." }
340
- - id: require_fe_current
341
- action: verify_context
342
- require_true: fe_gate_ok
343
- on_false: stop
344
- on_false_message: "{{fe_gate_msg}}"
345
-
346
- # 4. Load the domain GATE overlays (domain-rule-bindings `/aw-spec-gate`) as the
347
- # conformance CHECKLIST — validation input only, never authoring. The analyze step
348
- # sees them (include_standards: true); the repair steps do NOT, because a plan
349
- # conforms to the SPEC and an overlay constraint absent from the spec is a lift
350
- # failure to report, not a rule to design against. Empty for a non-domain project.
351
- - id: resolve_gate_overlays
352
- action: execute_script
353
- script: { ref: actions }
354
- subcommand: resolve-domain
355
- args: ["--command", "aw-spec-gate"]
356
- outputs:
357
- - { name: gate_overlays, from: "$.candidates" }
358
- - id: load_gate_overlays
359
- action: load_standards
360
- files_from_variable: gate_overlays
361
- optional: true
362
-
363
- # 5. Pre-execution hooks. The phase keys stay `before_analyze` / `after_analyze` —
364
- # they are configuration contract and outlive the rename.
365
- - id: resolve_pre_hooks
366
- action: execute_script
367
- script: { ref: actions }
368
- subcommand: resolve-hooks
369
- args: ["--phase", "before_analyze"]
370
- outputs:
371
- - { name: before_hooks, from: "$.hooks" }
372
- - id: dispatch_pre_hooks
373
- action: dispatch_hooks
374
- hooks_from_variable: before_hooks
375
-
376
- # 6. The context both lanes are judged in. One envelope, because the coverage
377
- # question spans them: a UX- lives only in the frontend plan, a BR- only in the
378
- # backend plan, and an FR- with a user-facing surface needs a home in each.
379
- # Both plans are `optional:` ONLY because a lane may be undeclared — for a
380
- # declared lane the freshness gate above already proved the file exists, so an
381
- # in-scope plan is never silently skipped here.
382
- - id: ctx_analyze
383
- action: build_context
384
- context:
385
- - { path: "{{spec_file}}" }
386
- - { path: "{{be_plan}}", optional: true }
387
- - { path: "{{fe_plan}}", optional: true }
388
- - { path: "{{feature_dir}}/interfaces.md", optional: true } # the published surface both plans cite by IF- id
389
-
390
- # 7. Conformance loop: analyze → REPORT gaps + ASK → repair the found gaps → re-check.
391
- # Bounded (3x). "Report and Skip" leaves both plans untouched and stops at the
392
- # report, which is the right answer whenever the fix belongs to another stage.
393
- - id: analyze_loop
394
- action: loop
395
- while: "{{analyze_ok}} != true"
396
- max_iterations: 3
397
- steps:
398
- # A stale `true` from a previous iteration must never arm a write, so the
399
- # frontend repair permission is rebuilt from scratch on every pass.
400
- - id: fe_repair_reset
401
- action: set
402
- vars: { fe_repair_go: "false" }
403
-
404
- - id: analyze
405
- action: agent_query
406
- include_standards: true # the domain gate overlays (the checklist) ride in the standards
407
- schema: { ok: true, gap_count: 0, gap_refs: "<comma-separated spec ids the gaps touch>", 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>" } ] }
408
- mock: { ok: true, gap_count: 0, gap_refs: "", backend_repair: false, frontend_repair: false, gaps: [] }
409
- task: |
410
- spec.md is the ANCHOR. Judge the feature's PLAN DOCUMENTS against it and list
411
- EVERY drift as a gap. In scope this run: backend-plan.md = {{be_in_scope}},
412
- frontend-plan.md = {{fe_in_scope}}. Judge ONLY the plans in scope; never
413
- report a gap against a lane that is not declared.
414
-
415
- SECTION NUMBERS ARE NOT STABLE, so identify every section by its ROLE and by
416
- the heading actually present in the document in front of you. A brownfield
417
- project's plans come from the code-derived template /appweaver onboard
418
- installs, whose layout differs from the greenfield one (its traceability,
419
- assumptions and deviations sections carry different numbers, and its
420
- component sections are named after the project's own structural axes). Any
421
- "§n" below is the GREENFIELD number, given only to say WHICH section is
422
- meant. Put the heading you actually found in `where`, and never report a
423
- missing section on the strength of a number.
424
-
425
- (1) FORWARD — COVERAGE. Every spec item has a design home, in the RIGHT lane:
426
- • FR- (requirement) — a home in backend-plan.md; and ALSO a screen/
427
- component/state in frontend-plan.md when it has a user-facing surface.
428
- • BR- (business rule) — backend-plan.md only, as a domain invariant.
429
- • CN- (constraint) — wherever it binds. A CN- that binds both lanes needs
430
- a home in BOTH; one that binds a single lane needs only that one.
431
- • DC- (data classification) — backend enforcement, plus what the frontend
432
- withholds from the client wherever a client sees the data.
433
- • IC- (integration contract) — the backend adapter/port that realises it;
434
- the frontend binds to it through interfaces.md, never directly.
435
- • UX- (experience standard) — frontend-plan.md ONLY. backend-plan.md does
436
- not carry them, so a UX- with no frontend row is a DROPPED requirement
437
- and is always a HIGH gap.
438
- • SC- (success criterion) — wherever it is met and how it is verified.
439
- A lifted CN-/DC-/IC- with no row in the relevant Traceability section is a
440
- HIGH gap: those sections are the two-way guarantee and must be complete.
441
- Edge Cases carry no ids by design — do not invent one for them.
442
-
443
- (2) THE DOMAIN MODEL MATCHES THE SPEC EXACTLY (backend). Entities and value
444
- objects are the spec's Key Entities under the same names, invariants are
445
- the spec's Business Rules, and the boundary is the spec's Aggregate
446
- Boundary. A renamed or invented entity is a HIGH gap.
447
-
448
- (3) BACKWARD — JUSTIFICATION. No design element exceeds or contradicts the
449
- spec. Anything without a spec basis is a drift UNLESS it is already logged
450
- as a DA-n assumption or a WA-n workaround in that plan's own ledgers — a
451
- logged item with a stated basis (including "per Const. §<n>" for standing
452
- technical machinery the constitution mandates) is accounted for, not a gap.
453
-
454
- (4) CROSS-LANE — the checks neither lane can make alone. Report these with
455
- lane "cross":
456
- • an FR- with a user-facing surface designed in one lane only;
457
- • a CN-/DC-/IC- that binds both lanes and is honoured in only one;
458
- • the frontend's contract-binding section (greenfield §5) citing an IF-
459
- id that interfaces.md does not publish, or an operation it needs that
460
- has no IF- entry and is not already raised as a contract gap;
461
- • either plan RESTATING the wording of a published interface instead of
462
- citing its IF- id — the spec owns requirement wording and interfaces.md
463
- owns contract wording; a second copy is drift by construction;
464
- • the two plans contradicting each other on the same behaviour.
465
-
466
- (5) COHERENCE. The backend layers hold (Application→Domain, API→Application,
467
- Infrastructure implements the declared ports); the frontend's state,
468
- components and flows are consistent with its own navigation. Both stay
469
- high-level and technology-agnostic.
470
-
471
- (6) OVERLAY BACKSTOP. The domain gate checklist is loaded in the standards.
472
- For each constraint it states (retention, immutability, append-only audit,
473
- data exposure, interop obligation …), confirm a plan realises it VIA a
474
- spec CN-/DC-/IC-. If a checklist constraint is in NEITHER the spec nor a
475
- plan, it is a HIGH gap: set spec_ref to the overlay name and PREFIX the
476
- gap text with "LIFT-FAILURE:". It was never lifted into the spec, so the
477
- fix is /appweaver specify — never fabricate it into a plan.
478
-
479
- For each gap set `route` to the command that actually fixes it: repair here
480
- when the plan is wrong and the spec is right; /appweaver sync-spec when the
481
- SPEC is what is wrong; /appweaver specify for a LIFT-FAILURE; /appweaver plan
482
- or /appweaver plan-ui when the plan simply lags the spec.
483
-
484
- Set backend_repair true only if at least one gap can be closed by rewriting
485
- backend-plan.md, and frontend_repair likewise for frontend-plan.md — false for
486
- a lane that is not in scope, and false when every gap on that lane routes to
487
- another command. Set gap_count to the number of gaps and gap_refs to their
488
- spec ids, comma-separated. Set ok=true ONLY when every plan in scope fully
489
- conforms to the spec AND no checklist constraint is unaccounted for.
490
- outputs:
491
- - { name: analyze_ok, from: "$.ok" }
492
- - { name: analyze_gaps, from: "$.gaps" }
493
- - { name: gap_count, from: "$.gap_count" }
494
- - { name: gap_refs, from: "$.gap_refs" }
495
- - { name: be_needs_repair, from: "$.backend_repair" }
496
- - { name: fe_needs_repair, from: "$.frontend_repair" }
497
-
498
- # The report's subject is the state the gate FOUND, not what survived repair —
499
- # a verdict that only ever describes the repaired document cannot show that a
500
- # repair happened, and a green earned by repair must stay visible as one.
501
- - id: record_found
502
- condition: "{{found_recorded}} == false"
503
- action: set
504
- vars:
505
- found_gaps: "{{analyze_gaps}}"
506
- found_count: "{{gap_count}}"
507
- found_refs: "{{gap_refs}}"
508
- found_recorded: "true"
509
-
510
- - id: conform_break
511
- action: break
512
- condition: "{{analyze_ok}} == true"
513
-
514
- - id: ask_repair
515
- action: ask
516
- prompt: |
517
- Spec ↔ plan gaps found (spec.md is the anchor; {{gap_count}} gap(s) on {{gap_refs}}):
518
- {{analyze_gaps}}
519
- Repair the plan document(s) to conform now, or write the report and stop?
520
- Repair closes only these gaps and records the rewrite in the plan's own
521
- Change History. Choose Report and Skip whenever the fix belongs elsewhere —
522
- a wrong SPEC goes through /appweaver sync-spec, a LIFT-FAILURE through
523
- /appweaver specify.
524
- options: ["Repair", "Report and Skip"]
525
- answer_var: repair_choice
526
-
527
- - id: skip_break
528
- action: break
529
- condition: "{{repair_choice}} == Report and Skip"
530
-
531
- - id: mark_repaired
532
- action: set
533
- vars: { repaired: "true" }
534
-
535
- # The frontend lane's write permission is the AND of "the model found something
536
- # to fix there" and "that lane is in scope" — the evaluator takes single
537
- # comparisons only, so the second half arrives as the assigned value.
538
- - id: fe_repair_arm
539
- condition: "{{fe_needs_repair}} == true"
540
- action: set
541
- vars: { fe_repair_go: "{{fe_in_scope}}" }
542
-
543
- - id: reconcile_be
544
- condition: "{{be_needs_repair}} == true"
545
- action: agent_author
546
- include_standards: false # rewrite-in-place: the SPEC anchors, not the overlays
547
- output_target: { type: file, path: "{{be_plan}}" }
548
- task: |
549
- spec.md is the ANCHOR — update backend-plan.md (in context) to CONFORM to it,
550
- closing the gaps below whose lane is "backend" or "cross" AND whose route is
551
- this repair. The spec WINS every time.
552
- {{analyze_gaps}}
553
- - SECTION NUMBERS ARE NOT STABLE: this document may be the greenfield
554
- four-layer template or the code-derived one /appweaver onboard installs,
555
- whose layout differs. Work from the headings actually in the document; the
556
- "§n" below name the GREENFIELD section so you know which one is meant.
557
- NEVER renumber or reorder what is there.
558
- - Give each uncovered spec FR-/BR-/CN-/DC-/IC-/SC- a design home: a lifted
559
- CN-/DC-/IC- lands in a domain invariant (§1), the persistence component
560
- (§4.1), an external-integrations adapter row (§4.2), or a cross-cutting
561
- row (§5).
562
- - Realign the domain model to the spec: entity and value-object names = the
563
- spec's Key Entities, invariants = the spec's Business Rules, boundary = the
564
- spec's Aggregate Boundary.
565
- - REMOVE, or move to a DA-n assumption, any design element with no spec basis.
566
- EXCEPTION: do NOT remove a cross-cutting element the technical constitution
567
- mandates — standing technical or infrastructure machinery rather than
568
- invented domain logic. MOVE it to a DA-n citing the mandating principle
569
- ("per Const. §<n>"); removing it could violate the constitution. Reserve
570
- REMOVE for genuine over-reach — invented domain entities, rules or behaviour
571
- the spec omits.
572
- - The published-surface section (§3) is an INDEX into interfaces.md: cite IF-
573
- ids, never restate a published contract's wording. If a gap says an operation
574
- has no IF- entry, note it there as a contract gap to publish — do not invent
575
- an IF- id, and never edit interfaces.md from here; /appweaver plan is its
576
- only publisher.
577
- - A "LIFT-FAILURE:" gap CANNOT be closed here: the constraint is not in the
578
- spec and you must not invent a domain rule the spec omits. Leave it open and
579
- add a DA-n reading "lift-failure: <constraint> — re-run /appweaver specify to
580
- lift it into CN-/DC-/IC-".
581
- - Leave every gap routed to /appweaver sync-spec or /appweaver specify OPEN.
582
- Fixing the plan to match a wrong spec buries the defect in the document that
583
- is supposed to reveal it.
584
- - Keep the Traceability section (§6) in sync with what you changed. Preserve
585
- every already-conformant section VERBATIM, and preserve the Assumptions (§7),
586
- Workarounds/Deviations (§8) and Change History (§9) sections as LEDGERS —
587
- append to them, never rewrite, renumber or drop them. Dropping the Change
588
- History section is refused downstream and stops the run. Write NO version and
589
- NO history row: plan-stamp does both.
590
- - Do NOT change spec.md, frontend-plan.md, interfaces.md or adr.md.
591
- Return the COMPLETE updated backend-plan.md content only.
592
-
593
- # plan-stamp is the plan's only version authority. Repairing without stamping
594
- # would leave the document reading as an unexplained hand edit to story-plan and
595
- # to the next /appweaver plan, and the rewrite would be absent from its ledger.
596
- - id: stamp_be
597
- condition: "{{be_needs_repair}} == true"
598
- action: execute_script
599
- script: { ref: actions }
600
- subcommand: plan-stamp
601
- args: ["--doc", "{{be_plan}}", "--feature-dir", "{{feature_dir}}", "--ids", "{{gap_refs}}", "--note", "Conformance repair (analyze-design)"]
602
- outputs:
603
- - { name: be_stamp_ok, from: "$.ok" }
604
- - { name: be_stamp_error, from: "$.error" }
605
-
606
- - id: reconcile_fe
607
- condition: "{{fe_repair_go}} == true"
608
- action: agent_author
609
- include_standards: false
610
- output_target: { type: file, path: "{{fe_plan}}" }
611
- task: |
612
- spec.md is the ANCHOR — update frontend-plan.md (in context) to CONFORM to it,
613
- closing the gaps below whose lane is "frontend" or "cross" AND whose route is
614
- this repair. The spec WINS every time.
615
- {{analyze_gaps}}
616
- - Give every uncovered UX- a home — this document is their ONLY home — and
617
- every FR- with a user-facing surface a screen, component or state.
618
- - Honour each CN-/DC- that binds this lane: a DC- says what the client is not
619
- shown, a CN- a bound the experience must meet.
620
- - SECTION NUMBERS ARE NOT STABLE — work from the headings actually in the
621
- document; the "§n" here name the greenfield sections so you know which is
622
- meant, and nothing is renumbered or reordered.
623
- - The Contract Binding section (§5) cites interfaces.md by IF- id and never
624
- restates a contract's wording. An operation with no published entry keeps the
625
- "(unpublished)" marker and is listed under contract gaps to raise — this
626
- lane never writes interfaces.md; /appweaver plan publishes it.
627
- - REMOVE, or move to a DA-n, any element with no spec basis, except what
628
- constitution.ui.md mandates — that moves to a DA-n citing the basis.
629
- - Leave every gap routed to /appweaver sync-spec or /appweaver specify OPEN.
630
- - Keep the Traceability section (§7) in sync. Preserve every already-conformant
631
- section VERBATIM, and preserve the Assumptions (§8), Workarounds (§9) and
632
- Change History sections as LEDGERS — append, never rewrite, renumber or drop.
633
- Dropping the Change History section is refused downstream and stops the run.
634
- Write NO version and NO history row: plan-stamp does both.
635
- - Do NOT change spec.md, backend-plan.md, interfaces.md or adr.md.
636
- Return the COMPLETE updated frontend-plan.md content only.
637
-
638
- - id: stamp_fe
639
- condition: "{{fe_repair_go}} == true"
640
- action: execute_script
641
- script: { ref: actions }
642
- subcommand: plan-stamp
643
- args: ["--doc", "{{fe_plan}}", "--feature-dir", "{{feature_dir}}", "--ids", "{{gap_refs}}", "--note", "Conformance repair (analyze-design)"]
644
- outputs:
645
- - { name: fe_stamp_ok, from: "$.ok" }
646
- - { name: fe_stamp_error, from: "$.error" }
647
-
648
- # 7b. The ledger-survival fence. plan-stamp refuses to baseline a document whose
649
- # Change History section went missing, so ok=false here means a repair landed on
650
- # disk having dropped the plan's ledger. That is the one loss this model never
651
- # absorbs silently — stop on it, before any verdict is written.
652
- - id: be_stamp_fence
653
- action: verify_context
654
- require_true: be_stamp_ok
655
- on_false: stop
656
- 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."
657
- - id: fe_stamp_fence
658
- action: verify_context
659
- require_true: fe_stamp_ok
660
- on_false: stop
661
- 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."
662
-
663
- # 8. The human report — written on EVERY run, green or red. It is the two-directional
664
- # coverage record, and a gate that leaves nothing behind when it passes gives a
665
- # reviewer no evidence that it ran at all.
666
- - id: write_report
667
- action: agent_author
668
- include_standards: false
669
- output_target: { type: file, path: "{{report_path}}" }
670
- task: |
671
- Write the Plan Analyze Report for this feature to the output.
672
- Verdict: conformant={{analyze_ok}}. Gaps as FOUND: {{found_count}} on
673
- {{found_refs}}. Gaps still open: {{gap_count}}. Repair was run: {{repaired}}.
674
- Lanes gated: backend={{be_in_scope}} (tracking {{be_state}}, derived from spec
675
- {{be_derived}}), frontend={{fe_in_scope}} (tracking {{fe_state}}, derived from
676
- spec {{fe_derived}}). Spec version: {{current_version}}.
677
- The gaps as found:
678
- {{found_gaps}}
679
- Markdown format:
680
- - "# Plan Analyze Report — <aggregate>", then a line naming the spec version and
681
- each gated plan with its tracking state.
682
- - A verdict line: "verdict: CONFORMS ✓" or "verdict: GAPS ✗ · <n> open".
683
- When repair ran, say so and give both numbers — found and still open.
684
- - "## Coverage (spec → plan)": a table | spec id | type | lane(s) it needs |
685
- design home | status |, one row per spec id you can see in the spec, marking
686
- each covered or UNCOVERED. This is the forward half of the two-way guarantee
687
- and it is the reason this report exists — write it even on a green run.
688
- - "## Gaps": a table | # | severity | lane | spec ref | where | gap | route |.
689
- Omit the section when there were none.
690
- - "## How to resolve": group by route and give the exact command for each — the
691
- spec is the anchor, so a wrong plan is repaired here or via /appweaver plan
692
- with accept_manual, a wrong SPEC goes through /appweaver sync-spec, and a
693
- "LIFT-FAILURE:" goes through /appweaver specify. Omit on a green run.
694
- Do not invent ids, versions or dates beyond the values given above.
695
- Return the complete report markdown only.
696
-
697
- # 9. The machine-readable verdict, written beside the report by the SCRIPT — CI
698
- # cannot branch on prose, and a verdict is exactly the kind of dated, derived
699
- # record the model never writes. `ok` from this step means the file was written;
700
- # the pass/fail verdict is `status` inside it.
701
- - id: gate_result
702
- action: execute_script
703
- script: { ref: actions }
704
- subcommand: gate-result
705
- # The lenient loader needs a flow sequence on ONE line.
706
- 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", "frontend_plan={{fe_plan}}", "--field", "frontend_in_scope={{fe_in_scope}}", "--field", "frontend_state={{fe_state}}", "--field", "frontend_derived={{fe_derived}}"]
707
- outputs:
708
- - { name: result_file, from: "$.result_file" }
709
-
710
- - id: analyze_gate
711
- action: verify_context
712
- require_true: analyze_ok
713
- on_false: stop
714
- 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-."
715
-
716
- # (No version is written here by design. This gate proves conformance; close-story is
717
- # the version authority downstream, and plan-stamp the plans' own.)
718
-
719
- # 10. Post-execution hooks.
720
- - id: resolve_post_hooks
721
- action: execute_script
722
- script: { ref: actions }
723
- subcommand: resolve-hooks
724
- args: ["--phase", "after_analyze"]
725
- outputs:
726
- - { name: after_hooks, from: "$.hooks" }
727
- - id: dispatch_post_hooks
728
- action: dispatch_hooks
729
- hooks_from_variable: after_hooks
730
-
731
- outputs:
732
- feature_dir: "{{feature_dir}}"
733
- spec_version: "{{current_version}}"
734
- backend_plan: "{{be_plan}}"
735
- backend_in_scope: "{{be_in_scope}}"
736
- backend_state: "{{be_state}}"
737
- backend_derived: "{{be_derived}}"
738
- frontend_plan: "{{fe_plan}}"
739
- frontend_in_scope: "{{fe_in_scope}}"
740
- frontend_state: "{{fe_state}}"
741
- frontend_derived: "{{fe_derived}}"
742
- conformant: "{{analyze_ok}}"
743
- gaps_found: "{{found_count}}"
744
- gaps_open: "{{gap_count}}"
745
- repaired: "{{repaired}}"
746
- report: "{{report_path}}"
747
- result_json: "{{result_file}}"
748
- gaps: "{{analyze_gaps}}"
749
- 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."