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.
- package/assets/.appweaver/DESIGN-FORMAT.md +18 -18
- package/assets/.appweaver/ORCH-DIALECT.md +1 -3
- package/assets/.appweaver/RULE-PARTITION.md +29 -13
- package/assets/.appweaver/STACK-PROFILES.md +10 -9
- package/assets/.appweaver/orchestrator/.state/smoke.state.json +32 -0
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +4 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +251 -0
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +104 -35
- package/assets/.appweaver/orchestrator/orchestrator.py +5 -4
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +94 -36
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md +115 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +2 -9
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +16 -40
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +38 -17
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +8 -8
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +10 -11
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +673 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +535 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +643 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +84 -701
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +7 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +16 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +243 -144
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +877 -578
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +32 -91
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +836 -514
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +132 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +38 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +271 -979
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1085 -3385
- package/assets/.appweaver/scripts/bash/aw-xref.sh +1 -2
- package/assets/.appweaver/scripts/bash/common.sh +47 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +914 -3220
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +19 -3
- package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
- package/assets/.appweaver/scripts/python/aw-xref.py +86 -350
- package/assets/.appweaver/templates/constitution-ui-template.md +1 -1
- package/assets/.appweaver/templates/delta-design-template.md +120 -30
- package/assets/.appweaver/templates/delta-domain-model-template.md +81 -0
- package/assets/.appweaver/templates/delta-spec-template.md +43 -17
- package/assets/.appweaver/templates/design-template.md +68 -98
- package/assets/.appweaver/templates/domain-model-template.md +130 -0
- package/assets/.appweaver/templates/okf-concept-template.md +2 -2
- package/assets/.appweaver/templates/source-map-template.md +3 -3
- package/assets/.appweaver/templates/spec-template.md +100 -122
- package/assets/.appweaver/templates/ui-delta-design-template.md +159 -0
- package/assets/.appweaver/templates/ui-design-template.md +46 -75
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +7 -18
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +34 -39
- package/assets/guardrails/requirement-spec.txt +2 -2
- package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +0 -105
- package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +0 -87
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -813
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -663
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -600
- package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -1147
- package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1121
- 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)"
|