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
@@ -0,0 +1,535 @@
1
+ # =============================================================================
2
+ # aw-spec-analyze-plan (orchestrator) — the spec↔plan conformance GATE, BOTH LANES
3
+ #
4
+ # The single, discrete conformance gate on the forward path:
5
+ # specify → plan → analyze-plan → implement.
6
+ #
7
+ # FOUR CHECKS, one per way a plan can be wrong:
8
+ # 1. spec.md → backend-plan.md — every spec item has a service home
9
+ # 2. spec.md → frontend-plan.md — every experience item has a screen home
10
+ # 3. backend-plan.md ↔ frontend-plan.md — CONTRACT BINDING: every call the frontend
11
+ # makes exists in the service's published surface, with the same field shapes. This
12
+ # is the check neither one-sided pass can make: a frontend call the backend never
13
+ # defines passes both coverage checks and fails at integration.
14
+ # 4. the plans → domain-model.md — no invented aggregate, no unenforced invariant. The
15
+ # model is spec-tier and is READ here, never written.
16
+ # Plus the OVERLAY BACKSTOP: the domain gate checklist, as a fifth pass over both plans.
17
+ #
18
+ # WHICH CHECKS RUN COMES FROM stacks.json. A backend-only project runs 1 + 4 (+ backstop);
19
+ # a frontend-only project runs 2 + 4; a full-stack project runs all four. Check 3 needs
20
+ # both plans to exist and is skipped, not failed, when only one lane is declared.
21
+ #
22
+ # THIS STAGE VALIDATES; `plan` AUTHORS. A declared lane whose plan document is MISSING
23
+ # STOPS the run naming /appweaver plan — it is not scaffolded and repaired here. Filling
24
+ # an empty template through a 3-pass repair loop would author a plan without the lane's
25
+ # rule set, without the template selection and without the feature's real source files,
26
+ # which is exactly the work `plan` does properly.
27
+ #
28
+ # BOOTSTRAP-ONLY, and it NEVER opens, merges or closes a change. It gates a feature whose
29
+ # spec.md + lane plans ARE the masters. With a change OPEN the delta docs are the
30
+ # authoritative in-flight edit and the master pair must not be rewritten under them, so
31
+ # the run STOPS and routes to /appweaver analyze-plan-delta (the delta's own gate). The
32
+ # change is closed by close-delta, the SINGLE cr-archive caller, and only after
33
+ # finalize-delta has merged the delta into the masters. Also runnable on demand to
34
+ # re-anchor a hand-edited spec — again, only when no change is open.
35
+ #
36
+ # RENAMED FROM `analyze-design`, following design → plan. It also absorbs the ⌛
37
+ # `analyze-design-ui` phase that was never built as its own command: the frontend lane is
38
+ # checked here, by the same gate, in the same run.
39
+ #
40
+ # Launch: /appweaver analyze-plan [strategy]
41
+ # Prereq: /appweaver plan (a spec.md + one plan document per declared lane).
42
+ # =============================================================================
43
+
44
+ name: aw-spec-analyze-plan
45
+ description: The spec↔plan conformance gate for every declared lane — confirm backend-plan.md and frontend-plan.md follow spec.md, that they bind to each other's contract, and that neither contradicts the bounded context's domain model (domain overlays are a backstop); with approval, repair the drifting plan. Lanes come from .appweaver/stacks.json, so one command gates backend-only, frontend-only and full-stack projects. Bootstrap-only — with a change open it stops and routes to analyze-plan-delta; it never closes a change.
46
+ version: 3.0.0
47
+ agent_agnostic: true
48
+ dialect: aw-spec.orchestrator/v1
49
+
50
+ scripts:
51
+ actions:
52
+ sh: .appweaver/scripts/bash/aw-spec-actions.sh
53
+ ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
54
+
55
+ standards:
56
+ - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
57
+
58
+ vars:
59
+ analyze_ok: "false"
60
+ active_change: ""
61
+ has_change: "false"
62
+ version: ""
63
+ no_open_change: "true" # NOT has_change ⇒ the masters are authoritative ⇒ this gate applies
64
+ lanes_declared: "false"
65
+ be_declared: "false"
66
+ fe_declared: "false"
67
+ # PER-LANE PRESENCE. A lane is CHECKED when it is declared and its plan document is on
68
+ # disk, and MISSING when it is declared and the document is not. The engine has no
69
+ # compound conditions, so each AND is one ordered assignment taking the lane's own
70
+ # `declared` value under the exists/not-exists condition.
71
+ be_present: "false"
72
+ fe_present: "false"
73
+ be_missing: "false"
74
+ fe_missing: "false"
75
+ plans_ok: "true" # every declared lane has its plan ⇒ there is something to gate
76
+ both_lanes: "false" # both plans present ⇒ the contract check applies
77
+ # Which document the repair pass rewrites. Each is raised only when the analyze pass
78
+ # reported drift IN that lane AND that lane is present.
79
+ be_repair: "false"
80
+ fe_repair: "false"
81
+ backend_repair: "false"
82
+ frontend_repair: "false"
83
+ backend_plan: ""
84
+ frontend_plan: ""
85
+ # The plan paths AS CONTEXT — bound only for a lane this run actually gates. feature-paths
86
+ # resolves both paths whatever the declaration says, so putting them straight into the
87
+ # envelope would show the gate a document belonging to a lane nobody declared: a frontend
88
+ # plan left behind by a removed lane would still be read, and the checks that skip it
89
+ # would be contradicted by its presence in the payload. An unbound var renders empty and
90
+ # the engine drops it.
91
+ be_plan_ctx: ""
92
+ fe_plan_ctx: ""
93
+ domain_model: ""
94
+
95
+ workflow:
96
+
97
+ # 0. WHICH LANES EXIST. The declaration is the same one `plan` wrote against, read the
98
+ # same way — lane-resolve, no on-disk check: this gate cares which documents were
99
+ # supposed to be authored, not where the code will live.
100
+ - id: lane_resolve
101
+ action: execute_script
102
+ on_error: continue # never crash raw on an undeclared lane — the gate below explains it
103
+ script: { ref: actions }
104
+ subcommand: lane-resolve
105
+ outputs:
106
+ - { name: lanes_declared, from: "$.declared" }
107
+ - { name: be_declared, from: "$.backend_declared" }
108
+ - { name: fe_declared, from: "$.frontend_declared" }
109
+
110
+ - id: require_lane
111
+ action: verify_context
112
+ require_true: lanes_declared
113
+ on_false: stop
114
+ on_false_message: "No lane is declared in .appweaver/stacks.json, so there is no plan to gate. Run /appweaver select-stack and declare the backend stack, the frontend stack, or both, then /appweaver plan, then re-run analyze-plan."
115
+
116
+ # 1. Resolve the current feature, its spec and its lane plans. feature-paths resolves a
117
+ # legacy design.md / design.ui.md under the plan keys too, so a tree that has not been
118
+ # through `plan` since the rename is gated on the document that is actually there.
119
+ - id: feature_paths
120
+ action: execute_script
121
+ on_error: continue # never crash raw on 'no feature set' — the gate below explains it
122
+ script: { ref: actions }
123
+ subcommand: feature-paths
124
+ outputs:
125
+ - { name: feature, from: "$.feature" }
126
+ - { name: feature_dir, from: "$.feature_dir" }
127
+ - { name: spec_file, from: "$.spec_file" }
128
+ - { name: spec_exists, from: "$.spec_exists" }
129
+ - { name: backend_plan, from: "$.backend_plan" }
130
+ - { name: frontend_plan, from: "$.frontend_plan" }
131
+ - { name: backend_plan_exists, from: "$.backend_plan_exists" }
132
+ - { name: frontend_plan_exists, from: "$.frontend_plan_exists" }
133
+
134
+ # The BOUNDED CONTEXT's domain model — where the aggregate root, its entities and its
135
+ # invariants (BR-n) live. The spec cites them by id and the plans record where each is
136
+ # ENFORCED, so check 4 has nothing to check without it in context.
137
+ - id: context_paths
138
+ action: execute_script
139
+ on_error: continue
140
+ script: { ref: actions }
141
+ subcommand: context-paths
142
+ outputs:
143
+ - { name: context, from: "$.context" }
144
+ - { name: domain_model, from: "$.domain_model" }
145
+ - { name: domain_model_exists, from: "$.domain_model_exists" }
146
+
147
+ - id: require_feature
148
+ action: verify_context
149
+ require_non_empty: feature_dir
150
+ on_empty: stop
151
+ on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run analyze-plan."
152
+
153
+ - id: verify_spec
154
+ action: verify_context
155
+ require_true: spec_exists
156
+ on_false: stop
157
+ on_false_message: "No spec.md for the current feature. Run /appweaver specify (and /appweaver plan) first."
158
+
159
+ # 1b. Read the open change (opened by specify, or by plan on a plan-only change).
160
+ # has_change drives the BOOTSTRAP-ONLY guard below — it closes nothing.
161
+ - id: read_change
162
+ action: execute_script
163
+ script: { ref: actions }
164
+ subcommand: version-read
165
+ args: ["--feature-dir", "{{feature_dir}}"]
166
+ outputs:
167
+ - { name: active_change, from: "$.active_change" }
168
+ - { name: has_change, from: "$.has_change" }
169
+ - { name: version, from: "$.version" }
170
+
171
+ # 1c. BOOTSTRAP-ONLY GUARD. A change open ⇒ the delta spec + delta plans are the
172
+ # authoritative in-flight edit and this stage is the WRONG gate: its repair loop
173
+ # rewrites the MASTER plans, which would drift the masters out from under the delta
174
+ # before finalize-delta merges it. Stop BEFORE any authoring so the wrong-stage run
175
+ # has zero side effects, and route to the delta's own gate.
176
+ - id: flag_change_open
177
+ condition: "{{has_change}} == true"
178
+ action: set
179
+ vars: { no_open_change: "false" }
180
+
181
+ - id: require_no_change
182
+ action: verify_context
183
+ require_true: no_open_change
184
+ on_false: stop
185
+ on_false_message: "Change {{active_change}} is OPEN on this feature — analyze-plan is the BOOTSTRAP gate and must not rewrite the master plans while a delta is in flight. Gate the delta instead: /appweaver analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta (merges the delta into spec.md and domain-model.md — not the lane plans, which are derived from those two) → analyze-slice → close-delta (archives the change + bumps the version). Re-run analyze-plan once every change is closed, to re-anchor the masters."
186
+
187
+ # 2. WHICH LANES ARE GATED. Present = declared AND on disk; missing = declared AND not.
188
+ # Taking the lane's own `declared` value under the exists condition is how the AND is
189
+ # expressed — an UNDECLARED lane's flags stay false either way, which is what makes a
190
+ # stray plan document from a lane that was later removed silently ignored rather than
191
+ # gated against a stack nobody declared.
192
+ - id: be_flag_present
193
+ condition: "{{backend_plan_exists}} == true"
194
+ action: set
195
+ vars: { be_present: "{{be_declared}}" }
196
+ - id: be_flag_missing
197
+ condition: "{{backend_plan_exists}} == false"
198
+ action: set
199
+ vars: { be_missing: "{{be_declared}}" }
200
+ - id: fe_flag_present
201
+ condition: "{{frontend_plan_exists}} == true"
202
+ action: set
203
+ vars: { fe_present: "{{fe_declared}}" }
204
+ - id: fe_flag_missing
205
+ condition: "{{frontend_plan_exists}} == false"
206
+ action: set
207
+ vars: { fe_missing: "{{fe_declared}}" }
208
+
209
+ # A declared lane with no plan is not a gap this gate repairs — it is work `plan` has
210
+ # not done. Two independent raises so neither lane can cancel the other's.
211
+ - id: plans_not_ok_backend
212
+ condition: "{{be_missing}} == true"
213
+ action: set
214
+ vars: { plans_ok: "false" }
215
+ - id: plans_not_ok_frontend
216
+ condition: "{{fe_missing}} == true"
217
+ action: set
218
+ vars: { plans_ok: "false" }
219
+
220
+ - id: require_plans
221
+ action: verify_context
222
+ require_true: plans_ok
223
+ on_false: stop
224
+ on_false_message: "A declared lane has no plan document (backend declared={{be_declared}} missing={{be_missing}} · frontend declared={{fe_declared}} missing={{fe_missing}}). Run /appweaver plan — it authors each declared lane's plan from the spec, the domain model, that lane's rule set and the feature's real source files. This gate validates a plan; it does not write one."
225
+
226
+ # Check 3 needs BOTH documents. One more ordered assignment: with the backend present,
227
+ # take the frontend's own presence as the answer.
228
+ - id: flag_both_lanes
229
+ condition: "{{be_present}} == true"
230
+ action: set
231
+ vars: { both_lanes: "{{fe_present}}" }
232
+
233
+ # Bind each present lane's plan as a CONTEXT path. A lane that is not gated leaves its
234
+ # var empty, so its document never reaches the payload.
235
+ - id: be_ctx_path
236
+ condition: "{{be_present}} == true"
237
+ action: set
238
+ vars: { be_plan_ctx: "{{backend_plan}}" }
239
+ - id: fe_ctx_path
240
+ condition: "{{fe_present}} == true"
241
+ action: set
242
+ vars: { fe_plan_ctx: "{{frontend_plan}}" }
243
+
244
+ # 2b. Load the domain GATE overlays (the domain-rule-bindings `/aw-spec-gate` checklist) as
245
+ # the conformance checklist — VALIDATION input only, NEVER authoring. The analyze
246
+ # step sees them (include_standards: true); the repair steps do NOT (a plan conforms
247
+ # to the SPEC, not to the overlays). Empty/no-op for a non-domain project.
248
+ - id: resolve_gate_overlays
249
+ action: execute_script
250
+ script: { ref: actions }
251
+ subcommand: resolve-domain
252
+ args: ["--command", "aw-spec-gate"]
253
+ outputs:
254
+ - { name: gate_overlays, from: "$.candidates" }
255
+ - id: load_gate_overlays
256
+ action: load_standards
257
+ files_from_variable: gate_overlays
258
+ optional: true
259
+
260
+ # 3. Pre-execution hooks.
261
+ - id: resolve_pre_hooks
262
+ action: execute_script
263
+ script: { ref: actions }
264
+ subcommand: resolve-hooks
265
+ args: ["--phase", "before_analyze"]
266
+ outputs:
267
+ - { name: before_hooks, from: "$.hooks" }
268
+ - id: dispatch_pre_hooks
269
+ action: dispatch_hooks
270
+ hooks_from_variable: before_hooks
271
+
272
+ # 4. THE GATE'S CONTEXT — the anchor, the model and the plans of the lanes being gated.
273
+ # A lane that is not gated left its *_plan_ctx var empty, so its document drops out and
274
+ # the same envelope serves all three project shapes. It stays the envelope for the
275
+ # repair steps too: a repair rewrites one plan against exactly what the check read.
276
+ - id: ctx_gate
277
+ action: build_context
278
+ context:
279
+ - { path: "{{spec_file}}" } # THE ANCHOR
280
+ - { path: "{{domain_model}}", optional: true } # spec-tier, READ here and never written
281
+ - { path: "{{be_plan_ctx}}", optional: true }
282
+ - { path: "{{fe_plan_ctx}}", optional: true }
283
+
284
+ # 5. Conformance + REPAIR loop (spec is the anchor): check → REPORT gaps + ASK →
285
+ # Repair on approval → re-check; "Report and Skip" writes plan-analyze-report.md
286
+ # and stops. Bounded (3×). The repair is PER LANE, so a frontend drift never
287
+ # rewrites a conformant backend plan.
288
+ - id: analyze_loop
289
+ action: loop
290
+ while: "{{analyze_ok}} != true"
291
+ max_iterations: 3
292
+ steps:
293
+ - id: analyze
294
+ action: agent_query
295
+ include_standards: true # the gate overlays (checklist) are in the standards
296
+ schema: { ok: true, backend_repair: false, frontend_repair: false, gaps: [ { severity: "high", check: "<spec-backend|spec-frontend|contract|model|overlay>", lane: "<backend|frontend>", spec_ref: "<FR/BR/CN/DC/IC/UX/US/SC or overlay or —>", where: "<plan section>", gap: "<how it drifts>" } ] }
297
+ mock: { ok: true, backend_repair: false, frontend_repair: false, gaps: [] }
298
+ task: |
299
+ spec.md is the ANCHOR. Gate this feature's LANE PLANS against it and against the
300
+ bounded context's domain model, and list EVERY drift as a gap. Run the checks
301
+ that apply — backend plan present: {{be_present}}, frontend plan present:
302
+ {{fe_present}}, both present: {{both_lanes}}. A check for an absent lane is
303
+ SKIPPED, not failed: an undeclared lane is a project decision, not a gap.
304
+
305
+ CHECK 1 — spec.md → backend-plan.md (when the backend plan is in context):
306
+ (a) every spec item — FR / Business Rule / Constraint (CN-) / Data
307
+ Classification (DC-) / Integration Contract (IC-) / user story / success
308
+ criterion — has a service home, and the Traceability section is complete. A
309
+ lifted CN-/DC-/IC- with no plan row is a HIGH gap. EXCEPT the Experience
310
+ Standards (UX-), which the service plan excludes by contract;
311
+ (b) NO plan element exceeds or contradicts the spec — anything without a spec
312
+ basis is a drift unless already logged as a DA-n assumption;
313
+ (c) the layers are coherent (Application→Domain, API→Application, Infra
314
+ implements the declared ports) and it stays high-level + technology-agnostic.
315
+
316
+ CHECK 2 — spec.md → frontend-plan.md (when the frontend plan is in context):
317
+ (a) every Experience Standard (UX-) has a home here — the service plan excludes
318
+ them, so a UX- in neither plan is a DROPPED requirement, a HIGH gap;
319
+ (b) every user story has a screen and a flow; every interaction-state FR- has a
320
+ flow row; the interaction-state set (loading, empty, error, permission-denied,
321
+ in-flight) is present for each interaction it describes;
322
+ (c) each CN-/DC-/IC-/SC- with an experience obligation is realised (authorization
323
+ surface, what may not reach the client, binding rows, performance) and the
324
+ Traceability section is complete;
325
+ (d) it stays technology-agnostic — no framework, router, state library, component
326
+ library or styling approach.
327
+
328
+ CHECK 3 — CONTRACT BINDING, backend-plan.md ↔ frontend-plan.md (ONLY when
329
+ {{both_lanes}} is true; skip entirely otherwise). This is the check neither
330
+ one-sided pass can make:
331
+ (a) EVERY call, endpoint or payload the frontend plan binds to EXISTS in the
332
+ service plan's published API surface. One that does not is a HIGH gap on the
333
+ `contract` check — lane `backend` when the spec requires the capability (the
334
+ service must publish it), lane `frontend` when the experience invented it;
335
+ (b) the FIELD SHAPES agree in both directions — a field the frontend reads that
336
+ the service never returns, and a required field the service demands that the
337
+ frontend never sends, are both HIGH gaps;
338
+ (c) anything the frontend plan raised under "Contract deltas needed" is
339
+ UNRESOLVED by definition: report it as a gap so it is closed here rather than
340
+ discovered at integration;
341
+ (d) the frontend plan cites the service's published surface and does NOT restate,
342
+ re-derive or contradict it, and states no server-side behaviour or persistence
343
+ assumption.
344
+
345
+ CHECK 4 — the plans → domain-model.md (when the model is in context; skip if it
346
+ is not):
347
+ (a) the backend plan's DOMAIN layer matches the model EXACTLY — entities and
348
+ value objects are its aggregate section's entities (SAME names), the boundary is
349
+ its Boundary block, states are the lifecycle it describes;
350
+ (b) EVERY invariant (BR-nnn) the spec cites has a named ENFORCEMENT POINT in the
351
+ backend plan. An invariant nothing enforces is a HIGH gap; a plan that RESTATES
352
+ what an invariant says instead of citing it by id is a gap too — the model is the
353
+ only place that text lives;
354
+ (c) NO aggregate, entity, event or invariant appears in a plan that the model
355
+ does not define. A plan may not invent domain structure: the fix is
356
+ /appweaver specify (which authors the model), never the plan;
357
+ (d) both plans use the model's UBIQUITOUS LANGUAGE — the same terms in names,
358
+ states, labels and messages, no synonyms.
359
+
360
+ CHECK 5 — OVERLAY BACKSTOP. The domain gate checklist is loaded in the standards.
361
+ For each constraint it states (retention, immutability, append-only audit,
362
+ data-exposure, interop obligation, etc.), confirm the plans realise it via a spec
363
+ CN-/DC-/IC-. If a checklist constraint is present in NEITHER the spec nor either
364
+ plan, it is a HIGH gap — set spec_ref to the overlay name and PREFIX the gap text
365
+ with "LIFT-FAILURE:" (it was never lifted into the spec; the fix is
366
+ /appweaver specify, not the plan — do not fabricate it here).
367
+
368
+ Tag every gap with its `check` and the `lane` whose document must change to close
369
+ it. Set backend_repair true if any gap needs the backend plan rewritten, and
370
+ frontend_repair likewise. (If a plan is still the empty template, treat the WHOLE
371
+ spec as gaps against it.) Set ok=true ONLY when every applicable check passes and
372
+ no checklist constraint is unaccounted for.
373
+ outputs:
374
+ - { name: analyze_ok, from: "$.ok" }
375
+ - { name: analyze_gaps, from: "$.gaps" }
376
+ - { name: backend_repair, from: "$.backend_repair" }
377
+ - { name: frontend_repair, from: "$.frontend_repair" }
378
+ - id: conform_break
379
+ action: break
380
+ condition: "{{analyze_ok}} == true"
381
+ - id: ask_repair
382
+ action: ask
383
+ prompt: |
384
+ analyze-plan found spec↔plan gaps (spec.md is the anchor; domain-model.md is
385
+ read-only here):
386
+ {{analyze_gaps}}
387
+ Repair the drifting plan(s) to conform now, or report the gaps and stop?
388
+ options: ["Repair", "Report and Skip"]
389
+ answer_var: repair_choice
390
+ - id: write_report
391
+ condition: "{{repair_choice}} == Report and Skip"
392
+ action: agent_author
393
+ include_standards: false
394
+ output_target: { type: file, path: "{{feature_dir}}/plan-analyze-report.md" }
395
+ task: |
396
+ Write a Plan Analyze Report to the output from these spec↔plan conformance gaps
397
+ (spec.md is the ANCHOR; the user chose to skip repair):
398
+ {{analyze_gaps}}
399
+ Markdown format:
400
+ - "# Plan Analyze Report — <aggregate>" plus today's date.
401
+ - A verdict line: "verdict: GAPS ✗ · <n> gap(s)".
402
+ - "## Gaps" as a table: | # | check | lane | severity | spec ref | plan location | gap |.
403
+ - "## How to resolve": the spec is the anchor — update the named plan to follow
404
+ it for each gap (out of band). A `model` gap whose fix is a missing entity or
405
+ invariant routes to /appweaver specify, which authors domain-model.md; a
406
+ `contract` gap is closed in the lane the row names. Then re-run
407
+ /appweaver analyze-plan to clear the report.
408
+ Return the complete report markdown only.
409
+ - id: skip_break
410
+ action: break
411
+ condition: "{{repair_choice}} == Report and Skip"
412
+
413
+ # WHICH DOCUMENT GETS REWRITTEN. A repair runs only where the analyze pass reported
414
+ # drift in that lane AND that lane is present — two ordered assignments per lane,
415
+ # cleared first so a lane that was repaired on the previous iteration and is clean
416
+ # on this one is not rewritten again.
417
+ - id: be_repair_clear
418
+ action: set
419
+ vars: { be_repair: "false" }
420
+ - id: be_repair_flag
421
+ condition: "{{backend_repair}} == true"
422
+ action: set
423
+ vars: { be_repair: "{{be_present}}" }
424
+ - id: fe_repair_clear
425
+ action: set
426
+ vars: { fe_repair: "false" }
427
+ - id: fe_repair_flag
428
+ condition: "{{frontend_repair}} == true"
429
+ action: set
430
+ vars: { fe_repair: "{{fe_present}}" }
431
+
432
+ - id: repair_backend
433
+ condition: "{{be_repair}} == true"
434
+ action: agent_author
435
+ include_standards: false # rewrite-in-place: spec anchors — NOT the overlays
436
+ output_target: { type: file, path: "{{backend_plan}}" }
437
+ task: |
438
+ spec.md is the ANCHOR — update backend-plan.md (in context) to CONFORM to it,
439
+ closing the gaps whose lane is `backend` (the spec WINS every time):
440
+ {{analyze_gaps}}
441
+ - Add a plan home for each uncovered spec FR/BR/CN/DC/IC/US/SC (a lifted
442
+ CN-/DC-/IC- lands in a §1 domain invariant enforcement · the persistence
443
+ component or an External Integrations table adapter row · a Cross-Cutting
444
+ Concerns row). UX- items are NOT this document's — leave them to the frontend
445
+ plan.
446
+ - Realign the DOMAIN layer to the CONTEXT'S DOMAIN MODEL: entity/value-object
447
+ names = its entities, the boundary = its Boundary block, and every cited
448
+ BR-nnn gets a named ENFORCEMENT POINT. Cite invariants by id; never restate
449
+ what one says. DO NOT EDIT domain-model.md — it is spec-tier and read-only
450
+ here. If a gap can only be closed by ADDING to the model, you cannot close it:
451
+ leave it open and add a DA-n saying it routes to /appweaver specify.
452
+ - CONTRACT gaps whose lane is `backend`: PUBLISH the missing surface in the API
453
+ section as field shapes, so the frontend plan has something real to bind to.
454
+ - REMOVE, or move to a DA-n assumption, any plan element with no spec basis —
455
+ never keep a plan that exceeds or contradicts the spec. EXCEPTION: do NOT
456
+ remove a cross-cutting element that is (or may be) mandated by the technical
457
+ constitution — it cites a principle as its basis ("per Const. §<n>"), or it is
458
+ standing technical/infrastructure machinery rather than invented domain logic.
459
+ MOVE such an element to a DA-n citing the mandating principle; removing it
460
+ could violate the constitution. Reserve REMOVE for genuine over-reach —
461
+ invented domain entities/rules/behaviour the spec omits.
462
+ - A "LIFT-FAILURE:" gap you CANNOT close — the constraint is not in the spec, and
463
+ you must NOT invent a domain rule the spec omits. Leave it open and add a DA-n
464
+ note "lift-failure: <constraint> — re-run /appweaver specify to lift it into
465
+ CN-/DC-/IC-". (The gate will stop and direct the user to specify.)
466
+ - Keep the Traceability section in sync. Preserve every already-conformant
467
+ section VERBATIM. Do NOT change the spec, the domain model or the frontend plan.
468
+ Return the COMPLETE updated backend-plan.md content only.
469
+
470
+ - id: repair_frontend
471
+ condition: "{{fe_repair}} == true"
472
+ action: agent_author
473
+ include_standards: false # rewrite-in-place: spec anchors — NOT the overlays
474
+ output_target: { type: file, path: "{{frontend_plan}}" }
475
+ task: |
476
+ spec.md is the ANCHOR — update frontend-plan.md (in context) to CONFORM to it,
477
+ closing the gaps whose lane is `frontend` (the spec WINS every time):
478
+ {{analyze_gaps}}
479
+ - Give every uncovered Experience Standard (UX-), user story and
480
+ interaction-state FR- a home: a screen in the navigation section, a flow row,
481
+ and the full state set (loading, empty, error, permission-denied, in-flight).
482
+ UX- items live HERE and nowhere else.
483
+ - Realise each CN-/DC-/IC-/SC- with an experience obligation, citing its spec id
484
+ — the authorization surface and access conditions, what may not reach the
485
+ client or its logs, storage and URLs, the binding rows, the performance row.
486
+ - CONTRACT gaps whose lane is `frontend`: bind ONLY to the service plan's
487
+ published surface (in context). CITE it; do not restate, re-derive or
488
+ contradict it, and introduce NO server-side behaviour or persistence
489
+ assumption. A capability the experience needs that the surface does not offer
490
+ stays under "Contract deltas needed" — it is the service lane's to publish,
491
+ never a client-side workaround.
492
+ - Use the domain model's UBIQUITOUS LANGUAGE exactly in labels, states and
493
+ messages, and cite BR-nnn rather than restating a rule. DO NOT EDIT
494
+ domain-model.md or backend-plan.md.
495
+ - A state the UI can reach that the spec does not describe is a SPEC gap: log it
496
+ as a DA-n that routes to /appweaver specify; never invent the requirement here.
497
+ - Stay TECHNOLOGY-AGNOSTIC: no framework, router, state library, component
498
+ library or styling approach — responsibilities, field shapes, events and states
499
+ only.
500
+ - Keep the Traceability section in sync, both ways. Preserve every
501
+ already-conformant section VERBATIM.
502
+ Return the COMPLETE updated frontend-plan.md content only.
503
+
504
+ - id: analyze_gate
505
+ action: verify_context
506
+ require_true: analyze_ok
507
+ on_false: stop
508
+ on_false_message: "The lane plans do not conform to the spec (gaps: {{analyze_gaps}}) — repair was skipped (see plan-analyze-report.md), or it could not be reconciled in 3 passes. A 'LIFT-FAILURE:' gap means a domain constraint from the gate checklist was never lifted into the spec, and a `model` gap means the plan needs an entity or invariant domain-model.md does not define — both are fixed by /appweaver specify, which authors the spec and the model; this gate may not write either. A `contract` gap is closed in the lane its row names. Otherwise fix out of band, or re-run and choose Repair."
509
+
510
+ # (No change-close here by design — this stage only ever runs with NO change open,
511
+ # and cr-archive belongs to close-delta, which runs after finalize-delta's merge.)
512
+
513
+ # 6. Post-execution hooks.
514
+ - id: resolve_post_hooks
515
+ action: execute_script
516
+ script: { ref: actions }
517
+ subcommand: resolve-hooks
518
+ args: ["--phase", "after_analyze"]
519
+ outputs:
520
+ - { name: after_hooks, from: "$.hooks" }
521
+ - id: dispatch_post_hooks
522
+ action: dispatch_hooks
523
+ hooks_from_variable: after_hooks
524
+
525
+ outputs:
526
+ feature_dir: "{{feature_dir}}"
527
+ bounded_context: "{{context}}"
528
+ backend_plan: "{{backend_plan}}" # empty when no backend lane is declared
529
+ frontend_plan: "{{frontend_plan}}" # empty when no frontend lane is declared
530
+ domain_model: "{{domain_model}}"
531
+ conformant: "{{analyze_ok}}"
532
+ gaps: "{{analyze_gaps}}"
533
+ version: "{{version}}"
534
+ note: "Checked: spec→backend-plan ({{be_present}}), spec→frontend-plan ({{fe_present}}), the plan↔plan contract binding ({{both_lanes}} — it needs both lanes) and both plans against domain-model.md. The domain model was READ, never written: a gap that needs a new entity, event or invariant is fixed by /appweaver specify."
535
+ next: "/appweaver implement (the plans conform to the spec; version unchanged — this gate closes nothing)"