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