aw-spec-cli 0.1.16 → 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 (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,673 @@
1
+ # =============================================================================
2
+ # aw-spec-analyze-plan-delta (orchestrator) — the delta-spec↔delta-plan GATE, BOTH LANES
3
+ #
4
+ # The CR analog of analyze-plan, scoped to ONE open change. The delta spec is the ANCHOR:
5
+ # confirm this change's delta plans STRICTLY realise it, and that everything they claim to
6
+ # MODIFY really exists in the masters. This stage VALIDATES the delta only — it does NOT
7
+ # touch spec.md / the master plans / domain-model.md, and it does NOT close the change
8
+ # (finalize-delta merges the delta later; close-delta closes it at the very end).
9
+ #
10
+ # IT ASKS WHICH CHANGE, exactly as `plan` does. Many changes may be open on one feature, so
11
+ # this gate LISTS them and asks rather than inferring one. A bare CR id in the command's
12
+ # argument selects without a prompt, one open change auto-confirms, and the chosen change
13
+ # is ACTIVATED so the stages after this one act on the same delta.
14
+ #
15
+ # THE FIVE CHECKS:
16
+ # 1. delta-spec.md → delta-backend-plan.md — every ADDED/MODIFIED spec item realised
17
+ # 2. delta-spec.md → delta-frontend-plan.md — every experience item realised
18
+ # 3. delta plan ↔ delta plan — CONTRACT BINDING across the change: what the frontend
19
+ # delta binds to must appear in the service delta's "Contract deltas for the frontend
20
+ # lane" table or in the master's published surface. A call neither publishes passes
21
+ # both one-sided checks and fails at integration.
22
+ # 4. THE EFFECTIVE MODEL — domain-model.md + this change's model delta + the model
23
+ # deltas of every LOWER-WAVE change (its predecessors). Every structural item the
24
+ # model delta adds has a realisation row; nothing structural is invented in a plan.
25
+ # 5. MASTER INTEGRITY — every MODIFIED element names something the master plan really
26
+ # defines, and every MODIFIED spec id something spec.md really defines. You cannot
27
+ # modify what does not exist; that is an ADD.
28
+ # Plus the OVERLAY BACKSTOP (the domain gate checklist over the constraints this change
29
+ # touches), and the WAVE DISCIPLINE check below.
30
+ #
31
+ # PREDECESSORS, NOT SIBLINGS. A wave is a dependency level. A LOWER-wave change is
32
+ # committed to landing first, so this delta may be planned on top of it — and both halves
33
+ # of each predecessor are in context, its model delta AND its delta-spec, because a plan
34
+ # that realises a predecessor's behaviour without its spec cannot be told apart from one
35
+ # that invented it. A SAME-wave change is ordered against this one by nothing at all: it
36
+ # is visible so it can be designed around, and building on it silently is the defect this
37
+ # check exists to catch.
38
+ #
39
+ # OVERLAP IS COMPUTED, NOT ASKED. cr-overlap reads the `## Aggregate:` headings of every
40
+ # pending model delta and reports which other open changes touch the same aggregates.
41
+ # That is a fact about two files, so a regex answers it exactly and the result is handed
42
+ # to the model as a finding rather than as a question.
43
+ #
44
+ # specify → plan → ⟨review⟩ → analyze-plan-delta ← you are here
45
+ # → implement → build → analyze-slice-delta → finalize-delta → analyze-slice
46
+ # → close-delta
47
+ #
48
+ # RENAMED FROM `analyze-delta-design`, following design → plan, and it absorbs the
49
+ # frontend half that never existed as its own command.
50
+ #
51
+ # Launch: /appweaver analyze-plan-delta [CR-id]
52
+ # Prereq: /appweaver specify (delta-spec + model delta) + /appweaver plan (delta plans).
53
+ # =============================================================================
54
+
55
+ name: aw-spec-analyze-plan-delta
56
+ description: The delta-spec↔delta-plan conformance gate for ONE open change — confirm its delta-backend-plan.md and delta-frontend-plan.md realise delta-spec.md, bind to each other's contract deltas, realise this change's domain model delta, and only MODIFY elements the masters really define. Checked against the EFFECTIVE model (domain-model.md plus this change's model delta plus every lower-wave predecessor's, whose delta-specs are in context too) and told, deterministically, which other open changes touch the same aggregates. Asks which open change it is gating when more than one is open; a CR id as the argument selects without a prompt. Does not modify the masters or close the change.
57
+ version: 2.0.0
58
+ agent_agnostic: true
59
+ dialect: aw-spec.orchestrator/v1
60
+
61
+ inputs:
62
+ user_input:
63
+ description: "The open change to gate — a bare CR id selects it without a prompt. Omitted, the run lists the open changes and asks."
64
+ required: false
65
+ bind: USER_INPUT
66
+
67
+ scripts:
68
+ actions:
69
+ sh: .appweaver/scripts/bash/aw-spec-actions.sh
70
+ ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
71
+
72
+ standards:
73
+ - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
74
+
75
+ vars:
76
+ analyze_ok: "false"
77
+ lanes_declared: "false"
78
+ be_declared: "false"
79
+ fe_declared: "false"
80
+ has_changes: "false"
81
+ cr_choice: ""
82
+ cr_id: ""
83
+ wave: "1"
84
+ predecessor_docs: [] # each lower-wave change's model delta AND its delta-spec
85
+ predecessor_count: "0"
86
+ sibling_count: "0"
87
+ overlap_report: ""
88
+ # PER-LANE PRESENCE. A lane is gated when it is declared AND has a master plan (only
89
+ # then is there a base for a delta) AND this change has a delta plan for it. The engine
90
+ # has no compound conditions, so each AND is one ordered assignment.
91
+ be_gated: "false" # declared AND a master plan exists
92
+ fe_gated: "false"
93
+ be_present: "false" # gated AND this change's delta plan exists ⇒ check it
94
+ fe_present: "false"
95
+ be_missing: "false" # gated AND it does NOT ⇒ `plan` has not run for this change
96
+ fe_missing: "false"
97
+ deltas_ok: "true"
98
+ any_lane: "false"
99
+ both_lanes: "false" # both delta plans present ⇒ the contract check applies
100
+ be_repair: "false"
101
+ fe_repair: "false"
102
+ backend_repair: "false"
103
+ frontend_repair: "false"
104
+ delta_backend_plan: ""
105
+ delta_frontend_plan: ""
106
+ # The plan paths AS CONTEXT — bound only for a lane this run actually gates. feature-paths
107
+ # and cr-paths resolve every path whatever the declaration says, so putting them straight
108
+ # into the envelope would show the gate documents belonging to a lane nobody declared.
109
+ # An unbound var renders empty and the engine drops it.
110
+ be_master_ctx: ""
111
+ fe_master_ctx: ""
112
+ be_delta_ctx: ""
113
+ fe_delta_ctx: ""
114
+ domain_model: ""
115
+
116
+ workflow:
117
+
118
+ # 0. WHICH LANES EXIST — the same declaration `plan` wrote against.
119
+ - id: lane_resolve
120
+ action: execute_script
121
+ on_error: continue # never crash raw on an undeclared lane — the gate below explains it
122
+ script: { ref: actions }
123
+ subcommand: lane-resolve
124
+ outputs:
125
+ - { name: lanes_declared, from: "$.declared" }
126
+ - { name: be_declared, from: "$.backend_declared" }
127
+ - { name: fe_declared, from: "$.frontend_declared" }
128
+
129
+ - id: require_lane
130
+ action: verify_context
131
+ require_true: lanes_declared
132
+ on_false: stop
133
+ on_false_message: "No lane is declared in .appweaver/stacks.json, so there is no delta plan to gate. Run /appweaver select-stack, then /appweaver plan, then re-run analyze-plan-delta."
134
+
135
+ # 1. Resolve the current feature, its spec and its MASTER plans — the base every
136
+ # MODIFIED element in a delta has to name.
137
+ - id: feature_paths
138
+ action: execute_script
139
+ on_error: continue # never crash raw on 'no feature set' — the gate below explains it
140
+ script: { ref: actions }
141
+ subcommand: feature-paths
142
+ outputs:
143
+ - { name: feature, from: "$.feature" }
144
+ - { name: feature_dir, from: "$.feature_dir" }
145
+ - { name: spec_file, from: "$.spec_file" }
146
+ - { name: spec_exists, from: "$.spec_exists" }
147
+ - { name: backend_plan, from: "$.backend_plan" }
148
+ - { name: frontend_plan, from: "$.frontend_plan" }
149
+ - { name: backend_plan_exists, from: "$.backend_plan_exists" }
150
+ - { name: frontend_plan_exists, from: "$.frontend_plan_exists" }
151
+
152
+ - id: require_feature
153
+ action: verify_context
154
+ require_non_empty: feature_dir
155
+ on_empty: stop
156
+ on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run analyze-plan-delta."
157
+
158
+ - id: verify_spec
159
+ action: verify_context
160
+ require_true: spec_exists
161
+ on_false: stop
162
+ on_false_message: "No spec.md for the current feature. Run /appweaver specify (and /appweaver plan) first."
163
+
164
+ # 2. WHICH CHANGES ARE OPEN. The evidence is changes/<cr-id>/ on disk; a closed change
165
+ # has moved to changes/archive/. `matched_label` resolves a CR id passed as the
166
+ # command's argument to the whole option string, so the pick below can auto-select it
167
+ # — the engine matches a preselection against the option text exactly.
168
+ - id: cr_list
169
+ action: execute_script
170
+ script: { ref: actions }
171
+ subcommand: cr-list
172
+ args: ["--feature-dir", "{{feature_dir}}", "--preselect", "{{USER_INPUT}}"]
173
+ outputs:
174
+ - { name: change_options, from: "$.select_options" }
175
+ - { name: matched_label, from: "$.matched_label" }
176
+ - { name: has_changes, from: "$.has_changes" }
177
+ - { name: change_count, from: "$.count" }
178
+
179
+ - id: require_change
180
+ action: verify_context
181
+ require_true: has_changes
182
+ on_false: stop
183
+ on_false_message: "No open change on this feature — there is no delta to gate. Run /appweaver specify <change> (which opens the change and writes its delta-spec + model delta), then /appweaver plan to author its delta plans, then re-run analyze-plan-delta. To re-anchor the MASTERS instead, run /appweaver analyze-plan."
184
+
185
+ # 2b. Ask which one. A CR id in the argument auto-selects via matched_label; a feature
186
+ # with exactly one open change auto-confirms.
187
+ - id: pick_change
188
+ action: user_select
189
+ prompt: "Which open change is this gate for? Deltas sharing a wave are independent; a higher wave is planned on top of the lower ones."
190
+ options_from_variable: change_options
191
+ preselect_from: matched_label
192
+ single_option_confirm: true
193
+ outputs:
194
+ - { name: cr_choice, from: selection }
195
+
196
+ # 2c. Make the chosen change the ACTIVE one. finalize-delta and close-delta still resolve
197
+ # the change through active_version.json.active_change (implement asks again, and
198
+ # re-activates whatever it is answered), so without this a delta gated here could be
199
+ # merged as a different one.
200
+ - id: activate
201
+ action: execute_script
202
+ script: { ref: actions }
203
+ subcommand: cr-activate
204
+ args: ["--cr-id", "{{cr_choice}}", "--feature-dir", "{{feature_dir}}"]
205
+ outputs:
206
+ - { name: active_change, from: "$.active_change" }
207
+
208
+ # 3. The change's documents, its wave, and everything that comes BEFORE it —
209
+ # predecessor_docs carries each lower-wave change's model delta AND its delta-spec,
210
+ # which is what a build_context `context_from:` inlines below.
211
+ - id: cr_paths
212
+ action: execute_script
213
+ script: { ref: actions }
214
+ subcommand: cr-paths
215
+ args: ["--cr-id", "{{cr_choice}}", "--feature-dir", "{{feature_dir}}"]
216
+ outputs:
217
+ - { name: cr_id, from: "$.cr_id" } # normalised: the label's first token
218
+ - { name: cr_dir, from: "$.cr_dir" }
219
+ - { name: delta_spec, from: "$.delta_spec" }
220
+ - { name: delta_spec_exists, from: "$.delta_spec_exists" }
221
+ - { name: delta_model, from: "$.delta_model" }
222
+ - { name: delta_backend_plan, from: "$.delta_backend_plan" }
223
+ - { name: delta_frontend_plan, from: "$.delta_frontend_plan" }
224
+ - { name: delta_backend_plan_exists, from: "$.delta_backend_plan_exists" }
225
+ - { name: delta_frontend_plan_exists, from: "$.delta_frontend_plan_exists" }
226
+ - { name: wave, from: "$.wave" }
227
+ - { name: predecessor_docs, from: "$.predecessor_docs" }
228
+ - { name: predecessor_count, from: "$.predecessor_count" }
229
+ - { name: sibling_count, from: "$.sibling_count" }
230
+ - { name: domain_model, from: "$.domain_model" }
231
+
232
+ # 3b. WHICH OTHER OPEN CHANGES TOUCH THE SAME AGGREGATES. Deterministic: the
233
+ # `## Aggregate:` headings of the pending model deltas are the merge unit
234
+ # domain-model.md is organised by, so this is a regex over two files rather than a
235
+ # judgement. The result reaches the check below as a FINDING, not a question.
236
+ - id: cr_overlap
237
+ action: execute_script
238
+ on_error: continue
239
+ script: { ref: actions }
240
+ subcommand: cr-overlap
241
+ args: ["--cr-id", "{{cr_id}}", "--feature-dir", "{{feature_dir}}"]
242
+ outputs:
243
+ - { name: overlap_report, from: "$.overlap_report" }
244
+ - { name: overlap_count, from: "$.overlap_count" }
245
+ - { name: sibling_overlaps, from: "$.sibling_overlaps" }
246
+ - { name: predecessor_overlaps, from: "$.predecessor_overlaps" }
247
+
248
+ # 4. WHICH LANES ARE GATED. Declared AND a master plan exists (only then is there a base
249
+ # for a delta) — then split on whether this change has that lane's delta plan.
250
+ # A lane whose MASTER was bootstrapped in the same `plan` run has no delta plan by
251
+ # design: it was authored from the whole spec and there is nothing for a delta to
252
+ # add. Keying on the master's presence is what keeps that case out of the gate.
253
+ - id: be_flag_gated
254
+ condition: "{{backend_plan_exists}} == true"
255
+ action: set
256
+ vars: { be_gated: "{{be_declared}}" }
257
+ - id: fe_flag_gated
258
+ condition: "{{frontend_plan_exists}} == true"
259
+ action: set
260
+ vars: { fe_gated: "{{fe_declared}}" }
261
+ - id: be_flag_present
262
+ condition: "{{delta_backend_plan_exists}} == true"
263
+ action: set
264
+ vars: { be_present: "{{be_gated}}" }
265
+ - id: be_flag_missing
266
+ condition: "{{delta_backend_plan_exists}} == false"
267
+ action: set
268
+ vars: { be_missing: "{{be_gated}}" }
269
+ - id: fe_flag_present
270
+ condition: "{{delta_frontend_plan_exists}} == true"
271
+ action: set
272
+ vars: { fe_present: "{{fe_gated}}" }
273
+ - id: fe_flag_missing
274
+ condition: "{{delta_frontend_plan_exists}} == false"
275
+ action: set
276
+ vars: { fe_missing: "{{fe_gated}}" }
277
+
278
+ # A lane with a master but no delta plan for this change is not a gap this gate repairs
279
+ # — it is work `plan` has not done for this change. Two independent raises.
280
+ - id: deltas_not_ok_backend
281
+ condition: "{{be_missing}} == true"
282
+ action: set
283
+ vars: { deltas_ok: "false" }
284
+ - id: deltas_not_ok_frontend
285
+ condition: "{{fe_missing}} == true"
286
+ action: set
287
+ vars: { deltas_ok: "false" }
288
+
289
+ - id: require_deltas
290
+ action: verify_context
291
+ require_true: deltas_ok
292
+ on_false: stop
293
+ on_false_message: "Change {{cr_id}} has no delta plan for a lane that has a master plan (backend missing={{be_missing}} · frontend missing={{fe_missing}}). Run /appweaver plan {{cr_id}} — it authors that change's delta plan for every lane whose master exists, against the delta spec, the effective model and the feature's real source files. This gate validates a delta plan; it does not write one."
294
+
295
+ - id: any_backend
296
+ condition: "{{be_present}} == true"
297
+ action: set
298
+ vars: { any_lane: "true" }
299
+ - id: any_frontend
300
+ condition: "{{fe_present}} == true"
301
+ action: set
302
+ vars: { any_lane: "true" }
303
+
304
+ - id: require_any_lane
305
+ action: verify_context
306
+ require_true: any_lane
307
+ on_false: stop
308
+ on_false_message: "Change {{cr_id}} has no delta plan to gate on any declared lane. Run /appweaver plan {{cr_id}} first."
309
+
310
+ # Check 3 needs BOTH delta plans. One more ordered assignment.
311
+ - id: flag_both_lanes
312
+ condition: "{{be_present}} == true"
313
+ action: set
314
+ vars: { both_lanes: "{{fe_present}}" }
315
+
316
+ # Bind the CONTEXT paths per lane: the master (the base a MODIFIED element must name) for
317
+ # a gated lane, the delta plan for a present one. A lane that is neither leaves both
318
+ # empty, so its documents never reach the payload.
319
+ - id: be_master_ctx_path
320
+ condition: "{{be_gated}} == true"
321
+ action: set
322
+ vars: { be_master_ctx: "{{backend_plan}}" }
323
+ - id: fe_master_ctx_path
324
+ condition: "{{fe_gated}} == true"
325
+ action: set
326
+ vars: { fe_master_ctx: "{{frontend_plan}}" }
327
+ - id: be_delta_ctx_path
328
+ condition: "{{be_present}} == true"
329
+ action: set
330
+ vars: { be_delta_ctx: "{{delta_backend_plan}}" }
331
+ - id: fe_delta_ctx_path
332
+ condition: "{{fe_present}} == true"
333
+ action: set
334
+ vars: { fe_delta_ctx: "{{delta_frontend_plan}}" }
335
+
336
+ # 4b. Load the domain GATE overlays (the domain-rule-bindings `/aw-spec-gate` checklist)
337
+ # as the conformance checklist — VALIDATION input only, NEVER authoring. analyze sees
338
+ # them (include_standards: true); the repair steps do NOT (the delta conforms to the
339
+ # delta SPEC, not to the overlays). Empty/no-op for a non-domain project.
340
+ - id: resolve_gate_overlays
341
+ action: execute_script
342
+ script: { ref: actions }
343
+ subcommand: resolve-domain
344
+ args: ["--command", "aw-spec-gate"]
345
+ outputs:
346
+ - { name: gate_overlays, from: "$.candidates" }
347
+ - id: load_gate_overlays
348
+ action: load_standards
349
+ files_from_variable: gate_overlays
350
+ optional: true
351
+
352
+ # 5. Pre-execution hooks.
353
+ - id: resolve_pre_hooks
354
+ action: execute_script
355
+ script: { ref: actions }
356
+ subcommand: resolve-hooks
357
+ args: ["--phase", "before_analyze_cr"]
358
+ outputs:
359
+ - { name: before_hooks, from: "$.hooks" }
360
+ - id: dispatch_pre_hooks
361
+ action: dispatch_hooks
362
+ hooks_from_variable: before_hooks
363
+
364
+ # 6. THE GATE'S CONTEXT — the delta, the masters it changes, and the EFFECTIVE MODEL.
365
+ # context_from inlines each PREDECESSOR's two halves (its model delta and its
366
+ # delta-spec); a wave-1 delta has none, which is the common case. A lane that is not
367
+ # gated left its *_ctx vars empty and drops out, so the same envelope serves all three
368
+ # project shapes, and it stays the envelope for the repair steps: a repair rewrites one
369
+ # delta plan against exactly what the check read.
370
+ - id: ctx_gate
371
+ action: build_context
372
+ context_from: predecessor_docs # LOWER waves only — committed to landing first
373
+ context:
374
+ - { path: "{{delta_spec}}" } # THE ANCHOR for this change
375
+ - { path: "{{delta_model}}", optional: true } # its model half — unfilled on a plan-only change
376
+ - { path: "{{be_delta_ctx}}", optional: true }
377
+ - { path: "{{fe_delta_ctx}}", optional: true }
378
+ - { path: "{{spec_file}}" } # the master spec (READ ONLY)
379
+ - { path: "{{be_master_ctx}}", optional: true } # the master plans (READ ONLY) — the base
380
+ - { path: "{{fe_master_ctx}}", optional: true } # every MODIFIED element must name
381
+ - { path: "{{domain_model}}", optional: true } # the canonical model (READ ONLY)
382
+
383
+ # 7. Conformance + REPAIR loop (the delta spec is the anchor): check → REPORT gaps +
384
+ # ASK → Repair on approval → re-check; "Report and Skip" writes the report + stops.
385
+ # Bounded (3×). The repair is PER LANE, so a frontend drift never rewrites a
386
+ # conformant service delta.
387
+ - id: analyze_loop
388
+ action: loop
389
+ while: "{{analyze_ok}} != true"
390
+ max_iterations: 3
391
+ steps:
392
+ - id: analyze
393
+ action: agent_query
394
+ include_standards: true # the gate overlays (checklist) are in the standards
395
+ schema: { ok: true, backend_repair: false, frontend_repair: false, gaps: [ { severity: "high", check: "<spec-backend|spec-frontend|contract|model|master|wave|overlay>", lane: "<backend|frontend>", spec_ref: "<delta FR/BR/CN/DC/IC/UX/US or overlay or —>", where: "<delta-plan section>", gap: "<how it drifts>" } ] }
396
+ mock: { ok: true, backend_repair: false, frontend_repair: false, gaps: [] }
397
+ task: |
398
+ delta-spec.md is the ANCHOR for change {{cr_id}} (wave {{wave}}). Gate this
399
+ change's DELTA PLANS against it, against the masters they change, and against the
400
+ effective domain model, and list EVERY drift as a gap. Run the checks that apply
401
+ — service delta present: {{be_present}}, experience delta present: {{fe_present}},
402
+ both present: {{both_lanes}}. A check for an absent lane is SKIPPED, not failed.
403
+
404
+ CHECK 1 — delta-spec.md → delta-backend-plan.md (when it is in context):
405
+ (a) every ADDED / MODIFIED delta-spec item — FR / BR / CN- / DC- / IC- / user
406
+ story — has a home in the delta plan (an ADDED or MODIFIED element that serves
407
+ it). A delta-spec item with no delta-plan element is a HIGH gap. EXCEPT the
408
+ Experience Standards (UX-), which the service lane excludes by contract;
409
+ (b) the delta plan introduces NOTHING beyond the delta spec — any element with no
410
+ delta-spec basis is a drift unless logged as a delta DA-n;
411
+ (c) shared elements have ONE owning definition (no duplicated element), the
412
+ layers stay coherent and it stays technology-agnostic;
413
+ (d) PLAN-ONLY CHANGE — if the delta-spec is an unfilled stub, the spec is
414
+ deliberately unchanged and this change moves only HOW existing ids are realised.
415
+ Then every changed element must still trace to an EXISTING spec id in the master
416
+ spec (in context). An element tracing to nothing at all is still a HIGH gap.
417
+
418
+ CHECK 2 — delta-spec.md → delta-frontend-plan.md (when it is in context):
419
+ (a) every UX- item in the delta-spec has a home HERE — the service lane excludes
420
+ them, so a UX- in neither delta is a DROPPED requirement, a HIGH gap;
421
+ (b) every new or altered interaction carries its FULL state set (loading, empty,
422
+ error, permission-denied, in-flight). A missing state is a HIGH gap, not a later
423
+ refinement;
424
+ (c) every user story and interaction-state FR- in the delta-spec has a screen and
425
+ a flow; the lifted CN-/DC-/IC-/SC- obligations are realised citing their ids;
426
+ (d) it stays technology-agnostic — no framework, router, state library, component
427
+ library or styling approach.
428
+
429
+ CHECK 3 — CONTRACT BINDING across the change (ONLY when {{both_lanes}} is true;
430
+ skip entirely otherwise):
431
+ (a) EVERY call, endpoint or payload the experience delta binds to is EITHER in
432
+ the service delta's "Contract deltas for the frontend lane" table OR already in
433
+ the MASTER backend plan's published surface. One that is in neither is a HIGH
434
+ `contract` gap — lane `backend` when the delta-spec requires the capability, lane
435
+ `frontend` when the experience invented it;
436
+ (b) FIELD SHAPES agree in both directions — a field the experience reads that the
437
+ service never returns, and a required field the service demands that the
438
+ experience never sends, are both HIGH gaps;
439
+ (c) anything the experience delta raised under "Contract deltas needed" is
440
+ UNRESOLVED by definition: report it so it is closed here rather than at
441
+ integration;
442
+ (d) a contract the service delta REMOVES or narrows that the master frontend plan
443
+ still uses is a HIGH gap — the experience delta has to carry the matching change.
444
+
445
+ CHECK 4 — THE EFFECTIVE MODEL. It is domain-model.md PLUS this change's model
446
+ delta PLUS the model deltas of every LOWER-WAVE change in context (its
447
+ predecessors, whose delta-specs are in context too). {{predecessor_count}}
448
+ predecessor document(s) were inlined.
449
+ (a) EVERY item this change's model delta ADDS / MODIFIES / REMOVES has a
450
+ realisation row in the service delta's "Model changes this plan realises",
451
+ saying where it lands and WHERE the invariant is ENFORCED. An item in the model
452
+ delta with no row is a structural change nothing implements — a HIGH gap;
453
+ (b) NO aggregate, entity, event or invariant appears in a delta plan that the
454
+ effective model does not define. A plan may not invent domain structure: the fix
455
+ is /appweaver specify (which authors the model delta), never the plan;
456
+ (c) a plan that RESTATES what an invariant says instead of citing its BR-nnn is a
457
+ gap — the model is the only place that text lives;
458
+ (d) DEPENDENCIES ON A PREDECESSOR. Where a delta plan relies on something a
459
+ lower-wave change introduces — in its model delta or in its delta-spec — that
460
+ change MUST be named under "## Depends on". An undeclared dependency is a HIGH
461
+ `wave` gap: the declaration is what orders the finalizes and what stops a
462
+ recall from silently invalidating this plan;
463
+ (e) both deltas use the model's UBIQUITOUS LANGUAGE exactly — same terms, no
464
+ synonyms.
465
+
466
+ CHECK 5 — MASTER INTEGRITY (the masters are in context, READ ONLY):
467
+ (a) every MODIFIED element names a REAL element the master plan defines — you
468
+ cannot MODIFY what the master does not define (that would be an ADD). A dangling
469
+ MODIFIED reference is a HIGH `master` gap;
470
+ (b) every MODIFIED / REMOVED spec id names one the master spec really defines;
471
+ (c) nothing in a delta plan edits the master: the delta plans are the whole plan
472
+ for this change, and no stage merges them back — a master plan is re-derived from
473
+ the spec and the model that finalize-delta merges.
474
+
475
+ CHECK 6 — WAVE DISCIPLINE. This change is wave {{wave}}. Other open changes
476
+ touching the same aggregates, computed from the pending model deltas:
477
+ {{overlap_report}}
478
+ ({{sibling_overlaps}} same-wave, {{predecessor_overlaps}} lower-wave;
479
+ {{sibling_count}} same-or-higher-wave change(s) are open in total.)
480
+ (a) a SAME-WAVE overlap is ordered against this change by nothing. If either
481
+ delta plan assumes it — builds on its structure, or expects its contract — that
482
+ is a HIGH `wave` gap: either raise this change's wave and declare the dependency,
483
+ or plan around it (expand-then-contract). Merely NAMING it under "Other open
484
+ deltas" is correct and is not a gap;
485
+ (b) a same-wave overlap that the plans do not mention at all is a MEDIUM `wave`
486
+ gap — it has to be visible to whoever implements this change;
487
+ (c) a LOWER-wave overlap may be built on, but only with the "## Depends on"
488
+ declaration check 4(d) requires.
489
+
490
+ CHECK 7 — OVERLAY BACKSTOP. The domain gate checklist is loaded in the standards.
491
+ For each constraint it states (retention, immutability, append-only audit,
492
+ data-exposure, interop obligation, etc.) that THIS CHANGE touches, confirm the
493
+ delta realises it via a delta CN-/DC-/IC-. If the change touches such a
494
+ constraint and it is in NEITHER the delta spec NOR a delta plan, it is a HIGH gap
495
+ — set spec_ref to the overlay name and PREFIX the gap text with "LIFT-FAILURE:"
496
+ (re-run /appweaver specify to lift it into the delta spec; do NOT fabricate it in
497
+ a delta plan).
498
+
499
+ Tag every gap with its `check` and the `lane` whose document must change to close
500
+ it. Set backend_repair true if any gap needs the service delta rewritten, and
501
+ frontend_repair likewise. (If a delta plan is still the empty template, treat the
502
+ WHOLE delta spec as gaps against it.) Set ok=true ONLY when every applicable
503
+ check passes and no touched checklist constraint is unaccounted for.
504
+ outputs:
505
+ - { name: analyze_ok, from: "$.ok" }
506
+ - { name: analyze_gaps, from: "$.gaps" }
507
+ - { name: backend_repair, from: "$.backend_repair" }
508
+ - { name: frontend_repair, from: "$.frontend_repair" }
509
+ - id: conform_break
510
+ action: break
511
+ condition: "{{analyze_ok}} == true"
512
+ - id: ask_repair
513
+ action: ask
514
+ prompt: |
515
+ analyze-plan-delta found delta-spec↔delta-plan gaps on {{cr_id}} (the delta spec
516
+ is the anchor; the masters and the domain model are read-only here):
517
+ {{analyze_gaps}}
518
+ Repair the drifting delta plan(s) now, or report the gaps and stop?
519
+ options: ["Repair", "Report and Skip"]
520
+ answer_var: repair_choice
521
+ - id: write_report
522
+ condition: "{{repair_choice}} == Report and Skip"
523
+ action: agent_author
524
+ include_standards: false
525
+ output_target: { type: file, path: "{{cr_dir}}/delta-plan-analyze-report.md" }
526
+ task: |
527
+ Write a Delta Plan Analyze Report to the output from these delta-spec↔delta-plan
528
+ conformance gaps (the delta spec is the ANCHOR; the user chose to skip repair):
529
+ {{analyze_gaps}}
530
+ Markdown format:
531
+ - "# Delta Plan Analyze Report — {{cr_id}}" plus today's date.
532
+ - A verdict line: "verdict: GAPS ✗ · <n> gap(s)".
533
+ - "## Gaps" as a table: | # | check | lane | severity | delta-spec ref | delta-plan location | gap |.
534
+ - "## How to resolve": the delta spec is the anchor — update the named delta plan
535
+ to realise it for each gap (out of band). A `model` gap whose fix is a missing
536
+ entity or invariant routes to /appweaver specify, which authors the model
537
+ delta; a `wave` gap is closed by declaring "## Depends on" (and raising the
538
+ wave) or by planning around the sibling; a "LIFT-FAILURE:" gap means the
539
+ constraint was never lifted into the delta spec. Then re-run
540
+ /appweaver analyze-plan-delta {{cr_id}} to clear the report.
541
+ Return the complete report markdown only.
542
+ - id: skip_break
543
+ action: break
544
+ condition: "{{repair_choice}} == Report and Skip"
545
+
546
+ # WHICH DOCUMENT GETS REWRITTEN — drift reported in that lane AND that lane present.
547
+ # Cleared first, so a lane repaired on the previous iteration and clean on this one
548
+ # is not rewritten again.
549
+ - id: be_repair_clear
550
+ action: set
551
+ vars: { be_repair: "false" }
552
+ - id: be_repair_flag
553
+ condition: "{{backend_repair}} == true"
554
+ action: set
555
+ vars: { be_repair: "{{be_present}}" }
556
+ - id: fe_repair_clear
557
+ action: set
558
+ vars: { fe_repair: "false" }
559
+ - id: fe_repair_flag
560
+ condition: "{{frontend_repair}} == true"
561
+ action: set
562
+ vars: { fe_repair: "{{fe_present}}" }
563
+
564
+ - id: repair_backend
565
+ condition: "{{be_repair}} == true"
566
+ action: agent_author
567
+ include_standards: false # rewrite-in-place: the delta spec anchors — NOT the overlays
568
+ output_target: { type: file, path: "{{delta_backend_plan}}" }
569
+ task: |
570
+ delta-spec.md is the ANCHOR — update the SERVICE delta plan for {{cr_id}} (in
571
+ context) to REALISE it, closing the gaps whose lane is `backend` (the delta spec
572
+ WINS every time):
573
+ {{analyze_gaps}}
574
+ - Add a delta-plan element for each uncovered ADDED/MODIFIED delta-spec
575
+ FR/BR/CN/DC/IC/US, in the correct section, serving that spec id. On a PLAN-ONLY
576
+ change (an unfilled delta-spec) trace every changed element to an EXISTING
577
+ master spec id instead — you are changing the realisation, never inventing a
578
+ requirement.
579
+ - Ensure every MODIFIED element names a REAL element from the master plan (in
580
+ context); if the master does not define it, make it an ADDED element instead.
581
+ - Fill "## Model changes this plan realises" with a row per item this change's
582
+ MODEL DELTA adds, modifies or removes — where it lands, and where each
583
+ invariant is ENFORCED. Cite BR-nnn by id; never restate what one says. DO NOT
584
+ EDIT domain-model.md or the model delta: they are spec-tier. A gap that can
585
+ only be closed by ADDING to the model stays open with a DA-n saying it routes
586
+ to /appweaver specify.
587
+ - CONTRACT gaps whose lane is `backend`: publish the missing surface in
588
+ "## Contract deltas for the frontend lane" as field shapes. That table is the
589
+ only part of this document the experience delta may bind to.
590
+ - WAVE gaps: name a lower-wave change you build on under "## Depends on", and
591
+ name a same-wave overlap under "Other open deltas" — you may NOT assume a
592
+ same-wave change's structure or contract, because nothing orders it against
593
+ this one.
594
+ - REMOVE, or move to a delta DA-n note, any element with no delta-spec basis —
595
+ never keep a plan that exceeds the delta spec.
596
+ - A "LIFT-FAILURE:" gap you CANNOT close — the constraint is not in the delta
597
+ spec, and you must NOT invent a domain rule it omits. Leave it open and add a
598
+ note "lift-failure: <constraint> — re-run /appweaver specify to lift it into
599
+ the delta spec".
600
+ - Keep the technology-agnostic form (signatures + pre/post/invariant, field
601
+ shapes — NO code). Preserve every already-conformant section VERBATIM. Do NOT
602
+ change the delta spec, the masters or the domain model. Do NOT edit the Wave /
603
+ Base version / Delta spec / Model delta headers — scripts stamp those.
604
+ Return the COMPLETE updated delta plan content only.
605
+
606
+ - id: repair_frontend
607
+ condition: "{{fe_repair}} == true"
608
+ action: agent_author
609
+ include_standards: false # rewrite-in-place: the delta spec anchors — NOT the overlays
610
+ output_target: { type: file, path: "{{delta_frontend_plan}}" }
611
+ task: |
612
+ delta-spec.md is the ANCHOR — update the EXPERIENCE delta plan for {{cr_id}} (in
613
+ context) to REALISE it, closing the gaps whose lane is `frontend` (the delta spec
614
+ WINS every time):
615
+ {{analyze_gaps}}
616
+ - Give every uncovered UX-, user story and interaction-state FR- from the delta
617
+ spec a home: a screen, a flow, and the FULL state set (loading, empty, error,
618
+ permission-denied, in-flight). UX- items live HERE and nowhere else.
619
+ - Ensure every MODIFIED component, screen or state container names a REAL element
620
+ from the master frontend plan (in context); if the master does not define it,
621
+ make it an ADDED element instead. A shared component is MODIFIED in place,
622
+ never copied into the change.
623
+ - CONTRACT gaps whose lane is `frontend`: bind ONLY to the service delta's
624
+ "## Contract deltas for the frontend lane" table and to the master plan's
625
+ published surface. CITE them; do not restate, re-derive or contradict them, and
626
+ introduce NO server-side behaviour or persistence assumption. A capability the
627
+ experience needs that neither offers stays under "Contract deltas needed" — it
628
+ is the service lane's to publish, never a client-side workaround.
629
+ - WAVE gaps: name a lower-wave change you build on under "## Depends on", and a
630
+ same-wave overlap under "Other open deltas"; never assume a same-wave change's
631
+ screens, shared components or contract.
632
+ - Use the domain model's UBIQUITOUS LANGUAGE exactly in labels, states and
633
+ messages, and cite BR-nnn rather than restating a rule. DO NOT EDIT
634
+ domain-model.md, the model delta or either master.
635
+ - A state the UI can reach that the delta spec does not describe is a SPEC gap:
636
+ log it as a DA-n that routes to /appweaver specify; never invent the
637
+ requirement here.
638
+ - Stay TECHNOLOGY-AGNOSTIC: no framework, router, state library, component
639
+ library or styling approach. Preserve every already-conformant section
640
+ VERBATIM. Do NOT edit the Wave / Base version / Delta spec / Service delta
641
+ headers — scripts stamp those.
642
+ Return the COMPLETE updated delta plan content only.
643
+
644
+ - id: analyze_gate
645
+ action: verify_context
646
+ require_true: analyze_ok
647
+ on_false: stop
648
+ on_false_message: "The delta plans for {{cr_id}} do not realise the delta spec (gaps: {{analyze_gaps}}) — repair was skipped (see {{cr_dir}}/delta-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 delta spec, and a `model` gap means a plan needs structure the effective model does not define — both are fixed by /appweaver specify, which authors the delta spec and the model delta; this gate may not write either. A `wave` gap is closed by declaring '## Depends on' (and raising the wave with cr-wave) or by planning around the sibling. Otherwise fix out of band, or re-run and choose Repair."
649
+
650
+ # 8. Post-execution hooks.
651
+ - id: resolve_post_hooks
652
+ action: execute_script
653
+ script: { ref: actions }
654
+ subcommand: resolve-hooks
655
+ args: ["--phase", "after_analyze_cr"]
656
+ outputs:
657
+ - { name: after_hooks, from: "$.hooks" }
658
+ - id: dispatch_post_hooks
659
+ action: dispatch_hooks
660
+ hooks_from_variable: after_hooks
661
+
662
+ outputs:
663
+ feature_dir: "{{feature_dir}}"
664
+ cr_id: "{{cr_id}}"
665
+ cr_dir: "{{cr_dir}}"
666
+ wave: "{{wave}}"
667
+ delta_backend_plan: "{{delta_backend_plan}}" # empty when that lane has no delta plan
668
+ delta_frontend_plan: "{{delta_frontend_plan}}"
669
+ conformant: "{{analyze_ok}}"
670
+ gaps: "{{analyze_gaps}}"
671
+ overlaps: "{{overlap_report}}"
672
+ note: "Gated change {{cr_id}} (wave {{wave}}) against its delta spec, the masters it changes, and the EFFECTIVE model — domain-model.md plus this change's model delta plus the {{predecessor_count}} predecessor change(s) from LOWER waves (each one's model delta and its delta-spec). Same-wave changes are visible but never assumed: {{sibling_count}} open at the same or a higher wave, overlapping this one on — {{overlap_report}}. The masters and the model were READ, never written."
673
+ next: "/appweaver implement {{cr_id}} → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta (implement is one command for both lanes and both paths — passing the id above skips its 'which change' prompt; the delta plans realise the delta spec; the change stays OPEN through finalize-delta's merge and analyze-slice's verification, and only close-delta archives it and bumps the version)"