aw-spec-cli 0.1.29 → 0.1.30
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/.amp/agent.json +22 -0
- package/assets/.amp/settings-edit.json +5 -0
- package/assets/.amp/settings-read.json +12 -0
- package/assets/.appweaver/DESIGN-FORMAT.md +9 -0
- package/assets/.appweaver/STACK-PROFILES.md +102 -15
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +40 -18
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +56 -4
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +17 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +12 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +289 -24
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +370 -85
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +349 -65
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +39 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +63 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +5 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +430 -138
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +392 -76
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +104 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +20 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +26 -3
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +314 -44
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +249 -20
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +17 -2
- package/assets/.appweaver/scripts/python/aw-plangate.py +109 -19
- package/assets/.appweaver/scripts/python/aw-stack.py +1295 -9
- package/assets/.appweaver/templates/delta-design-template.md +7 -5
- package/assets/.appweaver/templates/delta-spec-template.md +2 -0
- package/assets/.appweaver/templates/design-template.md +17 -0
- package/assets/.appweaver/templates/ui-design-template.md +19 -4
- package/assets/.augment/agent.json +22 -0
- package/assets/.augment/commands/appweaver.md +12 -0
- package/assets/.cline/agent.json +22 -0
- package/assets/.clinerules/workflows/appweaver.md +7 -0
- package/assets/.codex/agent.json +22 -0
- package/assets/.continue/agent.json +22 -0
- package/assets/.continue/prompts/appweaver.md +13 -0
- package/assets/.cursor/agent.json +22 -0
- package/assets/.cursor/commands/appweaver.md +7 -0
- package/assets/.devin/agent.json +22 -0
- package/assets/.devin/workflows/appweaver.md +11 -0
- package/assets/.factory/agent.json +22 -0
- package/assets/.factory/commands/appweaver.md +12 -0
- package/assets/.gemini/agent.json +22 -0
- package/assets/.github/prompts/appweaver.prompt.md +13 -0
- package/assets/.kilo/agent.json +28 -0
- package/assets/.kilo/agents/appweaver-edit.md +12 -0
- package/assets/.kilo/agents/appweaver-read.md +12 -0
- package/assets/.kilo/commands/appweaver.md +11 -0
- package/assets/.kiro/agent.json +22 -0
- package/assets/.kiro/skills/appweaver/SKILL.md +12 -0
- package/assets/.opencode/agent.json +22 -0
- package/assets/.opencode/agents/appweaver-edit.md +12 -0
- package/assets/.opencode/agents/appweaver-read.md +13 -0
- package/assets/.opencode/commands/appweaver.md +11 -0
- package/assets/.roo/agent.json +22 -0
- package/assets/.roo/commands/appweaver.md +12 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +14 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/copy-assets.d.ts +16 -3
- package/dist/utils/copy-assets.d.ts.map +1 -1
- package/dist/utils/copy-assets.js +29 -14
- package/dist/utils/copy-assets.js.map +1 -1
- package/package.json +1 -1
|
@@ -19,6 +19,31 @@
|
|
|
19
19
|
# a frontend-only project runs 2 + 4; a full-stack project runs all four. Check 3 needs
|
|
20
20
|
# both plans to exist and is skipped, not failed, when only one lane is declared.
|
|
21
21
|
#
|
|
22
|
+
# IT GATES THE DEVELOPER'S WORKING LANE. .appweaver/lane.json (select-stack writes the
|
|
23
|
+
# default, select-lane narrows it, not committed) names the lanes this developer works on.
|
|
24
|
+
# Only a WORKING lane's plan is GATED here — checked, repaired on approval, and given its
|
|
25
|
+
# own receipt (.aw-plan-gate.<lane>.json). A declared lane that is somebody else's is READ:
|
|
26
|
+
# its plan is in context so check 3 can compare the two sides of the contract, but it is
|
|
27
|
+
# never repaired, a missing one never stops the run, and a finding that only its document
|
|
28
|
+
# can close is reported for its developer (plan-gaps `other`) instead of blocking this one.
|
|
29
|
+
# Each lane's receipt carries that lane's verdict, so a frontend plan left with gaps never
|
|
30
|
+
# refuses the backend's implement, and the reverse. With the default (every declared
|
|
31
|
+
# lane) every plan is gated, as before — each in its own receipt.
|
|
32
|
+
#
|
|
33
|
+
# THE REQUEST LEDGER IS READ BY SCRIPT. What the frontend plan asks of the service sits in
|
|
34
|
+
# its §5 "Contract deltas needed" table, one RQ-n per request, and the backend plan answers
|
|
35
|
+
# each in its API section's "Requests from the frontend lane" table. contract-requests reads
|
|
36
|
+
# both on every pass, so check 3 is TOLD which requests are open (a backend-lane gap: answer
|
|
37
|
+
# it, by publishing or declining) and which wait on the frontend (answered and not re-bound,
|
|
38
|
+
# or declined). A frontend developer's run therefore reports open requests for the backend
|
|
39
|
+
# developer instead of stopping on them, and a backend developer's repair answers them.
|
|
40
|
+
# A master run that gates the backend plan also redraws api-draft.json from its API
|
|
41
|
+
# section, so the draft the frontend generates against matches the plan as repaired.
|
|
42
|
+
#
|
|
43
|
+
# THE FRONTEND RECEIPT HASHES THE BACKEND'S PUBLISHED SURFACE TOO (the API section of
|
|
44
|
+
# backend-plan.md): when the backend developer changes it — an answered request, a renamed
|
|
45
|
+
# operation — the frontend lane is stale at implement until its plan is gated again.
|
|
46
|
+
#
|
|
22
47
|
# THIS STAGE VALIDATES; `plan` AUTHORS. A declared lane whose plan document is MISSING
|
|
23
48
|
# STOPS the run naming /appweaver plan — it is not scaffolded and repaired here. Filling
|
|
24
49
|
# an empty template through a 3-pass repair loop would author a plan without the lane's
|
|
@@ -41,6 +66,15 @@
|
|
|
41
66
|
# to be stale, and a stage that refuses to look is not a gate. It reports; it never writes
|
|
42
67
|
# a plan.
|
|
43
68
|
#
|
|
69
|
+
# THE MODE IS DECIDED PER LANE. A change's delta-spec names the lanes it touches on its
|
|
70
|
+
# **Lanes** line, and only a change that names a lane holds that lane's master plan still.
|
|
71
|
+
# So each gated lane is in MASTER mode — repaired on approval, receipted, stopped on — unless
|
|
72
|
+
# an open change names it, and in REPORT mode when one does: a backend-only change leaves
|
|
73
|
+
# the frontend plan free to be gated and repaired while it is in flight. A change with no
|
|
74
|
+
# **Lanes** line names every lane, so with such changes, or with every lane named, the
|
|
75
|
+
# whole run is in report mode as before. A report-mode lane's gaps are listed with the ones
|
|
76
|
+
# this run does not repair (plan-gaps `other`) and neither drive the repair nor the stop.
|
|
77
|
+
#
|
|
44
78
|
# TWO KINDS OF GAP, SPLIT BY WHO CAN CLOSE THEM (plan-gaps). A PLAN gap is closed by
|
|
45
79
|
# rewriting a lane plan, which this stage may do. A ROUTED gap can only be closed in
|
|
46
80
|
# spec.md or domain-model.md, which this stage may not write: a LIFT-FAILURE (a domain
|
|
@@ -49,15 +83,17 @@
|
|
|
49
83
|
# are reported and handed to /appweaver specify. Stopping on them would stop every feature
|
|
50
84
|
# whose spec missed a constraint, with no repair this stage could ever make.
|
|
51
85
|
#
|
|
52
|
-
# EVERY MASTER RUN LEAVES A RECEIPT (.aw-plan-gate
|
|
53
|
-
#
|
|
54
|
-
#
|
|
86
|
+
# EVERY MASTER RUN LEAVES A RECEIPT PER GATED LANE (.aw-plan-gate.<lane>.json), green or
|
|
87
|
+
# not: the bytes it checked — spec.md, the context's domain-model.md and that lane's plan —
|
|
88
|
+
# and that lane's verdict: conformant, routed-gaps or gaps. implement reads the receipt of
|
|
89
|
+
# each lane it generates, refuses moved bytes or `gaps`, and warns on `routed-gaps`.
|
|
55
90
|
#
|
|
56
91
|
# WHICH MODE IS READ FROM THE DIRECTORY, AND DEFAULTS TO REPORT. changes/<id>/ is the
|
|
57
|
-
# evidence (cr-list), not active_version.json's
|
|
58
|
-
# recently OPENED change and a crashed run can leave
|
|
59
|
-
# write is RAISED only by cr-list answering that
|
|
60
|
-
# arrives leaves
|
|
92
|
+
# evidence (cr-list, asked once per lane with --lane), not active_version.json's
|
|
93
|
+
# `active_change` — the ledger is the most recently OPENED change and a crashed run can leave
|
|
94
|
+
# the two disagreeing. A lane's permission to write is RAISED only by cr-list answering that
|
|
95
|
+
# no open change names it, so a reading that never arrives leaves that lane read-only rather
|
|
96
|
+
# than rewriting its master mid-delta.
|
|
61
97
|
#
|
|
62
98
|
# The change is closed by close-delta, the SINGLE cr-archive caller, and only after
|
|
63
99
|
# finalize-delta has merged the delta into the masters. Also runnable on demand to
|
|
@@ -72,8 +108,8 @@
|
|
|
72
108
|
# =============================================================================
|
|
73
109
|
|
|
74
110
|
name: aw-spec-analyze-plan
|
|
75
|
-
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.
|
|
76
|
-
version: 3.
|
|
111
|
+
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. Each gated lane runs in one of two modes, decided from the changes/ directory rather than the ledger and defaulting to the read-only one — MASTER (no open change names that lane on its **Lanes** line) checks, repairs on approval and stops on unreconciled gaps; REPORT (an open change names it) runs the same checks read-only, because a master plan may not be rewritten while a delta to that lane is in flight. With no lane in master mode the run writes plan-analyze-report.md without asking, repairing or stopping. It never opens, merges or closes a change.
|
|
112
|
+
version: 3.6.0
|
|
77
113
|
agent_agnostic: true
|
|
78
114
|
dialect: aw-spec.orchestrator/v1
|
|
79
115
|
|
|
@@ -92,17 +128,27 @@ vars:
|
|
|
92
128
|
analyze_ok: "false"
|
|
93
129
|
active_change: ""
|
|
94
130
|
version: ""
|
|
95
|
-
#
|
|
96
|
-
# read-only answer. has_changes starts at the BLOCKING value because an unanswered
|
|
97
|
-
# question is not a clean answer: cr-list runs without on_error: continue, so a failure
|
|
98
|
-
# stops the run outright, and a field that never arrived leaves this stage read-only
|
|
99
|
-
# instead of letting the repair loop rewrite master plans while a delta is in flight.
|
|
100
|
-
has_changes: "true"
|
|
101
|
-
open_ids: "" # cr-list's ids_csv — what is open, named, for the operator
|
|
131
|
+
open_ids: "" # every open change on the feature, named for the operator
|
|
102
132
|
open_count: "0"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
133
|
+
# WHICH MODE EACH LANE IS IN, read from the changes/ DIRECTORY (cr-list --lane: does an
|
|
134
|
+
# open change name the lane) and defaulted to the read-only answer. *_change_open starts
|
|
135
|
+
# at the BLOCKING value because an unanswered question is not a clean answer: cr-list runs
|
|
136
|
+
# without on_error: continue, so a failure stops the run outright, and a field that never
|
|
137
|
+
# arrived leaves that lane read-only instead of letting the repair loop rewrite its master
|
|
138
|
+
# plan while a delta to it is in flight.
|
|
139
|
+
be_change_open: "true"
|
|
140
|
+
fe_change_open: "true"
|
|
141
|
+
be_open_ids: "" # the open changes naming the backend
|
|
142
|
+
fe_open_ids: ""
|
|
143
|
+
be_no_change: "false" # RAISED only by cr-list answering that none names the lane
|
|
144
|
+
fe_no_change: "false"
|
|
145
|
+
be_may_repair: "false" # gated AND no open change names it ⇒ MASTER mode for this lane
|
|
146
|
+
fe_may_repair: "false"
|
|
147
|
+
be_lane_mode: "not gated" # master | report (…) — reported in the outputs
|
|
148
|
+
fe_lane_mode: "not gated"
|
|
149
|
+
repair_lanes: "" # the lanes this run may repair, as a csv for plan-gaps --lanes
|
|
150
|
+
gate_mode: "report" # master (some lane may be repaired) | report (none) — reported in the outputs
|
|
151
|
+
may_repair: "false" # some gated lane is in MASTER mode: the ask, the repair loop and the stop run
|
|
106
152
|
gate_ok: "false" # raised by a run with no plan gap left OR by report mode, which cannot repair
|
|
107
153
|
stamp_receipt: "false" # master mode ⇒ leave the receipt implement reads, whatever the verdict
|
|
108
154
|
repairs_left: "true" # lowered on the loop's final pass, which checks and never rewrites
|
|
@@ -113,15 +159,40 @@ vars:
|
|
|
113
159
|
plan_gap_count: "0"
|
|
114
160
|
routed_gaps: "[]" # the gaps only /appweaver specify can close
|
|
115
161
|
routed_csv: ""
|
|
116
|
-
receipt_file: ""
|
|
117
162
|
receipt_gated: ""
|
|
118
|
-
receipt_count: "0"
|
|
119
163
|
lanes_declared: "false"
|
|
120
164
|
be_declared: "false"
|
|
121
165
|
fe_declared: "false"
|
|
122
166
|
fe_excluded: "false" # this feature records no surface in the declared frontend lane (feature-lanes)
|
|
123
|
-
#
|
|
124
|
-
#
|
|
167
|
+
# THE WORKING LANES (.appweaver/lane.json) — the lanes this developer's plans are gated
|
|
168
|
+
# for. A lane that is declared and not worked is read only.
|
|
169
|
+
be_working: "false"
|
|
170
|
+
fe_working: "false"
|
|
171
|
+
working_lane: ""
|
|
172
|
+
work_lanes: "" # the working lanes as a csv, for plan-gaps --lanes
|
|
173
|
+
be_gated: "false" # worked AND its plan is on disk ⇒ checked, repairable, receipted
|
|
174
|
+
fe_gated: "false"
|
|
175
|
+
any_gated: "false"
|
|
176
|
+
stamp_be: "false"
|
|
177
|
+
stamp_fe: "false"
|
|
178
|
+
be_verdict: "gaps"
|
|
179
|
+
fe_verdict: "gaps"
|
|
180
|
+
be_plan_gaps: "0"
|
|
181
|
+
fe_plan_gaps: "0"
|
|
182
|
+
be_routed: "[]"
|
|
183
|
+
fe_routed: "[]"
|
|
184
|
+
be_receipt: ""
|
|
185
|
+
fe_receipt: ""
|
|
186
|
+
other_gaps: "0" # findings only the lane you do not work on can close — reported for its developer
|
|
187
|
+
other_csv: ""
|
|
188
|
+
# THE REQUEST LEDGER (contract-requests), re-read on every pass, and the draft.
|
|
189
|
+
req_backend_text: "none"
|
|
190
|
+
req_frontend_text: "none"
|
|
191
|
+
req_summary: "no contract request"
|
|
192
|
+
draft_path: ""
|
|
193
|
+
# PER-LANE PRESENCE. A lane is PRESENT when it is declared and its plan document is on
|
|
194
|
+
# disk — its plan is then in context — and MISSING when it is WORKED and the document is
|
|
195
|
+
# not. The engine has no
|
|
125
196
|
# compound conditions, so each AND is one ordered assignment taking the lane's own
|
|
126
197
|
# `declared` value under the exists/not-exists condition.
|
|
127
198
|
be_present: "false"
|
|
@@ -166,6 +237,7 @@ workflow:
|
|
|
166
237
|
- { name: lanes_declared, from: "$.declared" }
|
|
167
238
|
- { name: be_declared, from: "$.backend_declared" }
|
|
168
239
|
- { name: fe_declared, from: "$.frontend_declared" }
|
|
240
|
+
- { name: working_lane, from: "$.working_lane" }
|
|
169
241
|
|
|
170
242
|
- id: require_lane
|
|
171
243
|
action: verify_context
|
|
@@ -207,6 +279,8 @@ workflow:
|
|
|
207
279
|
- { name: be_declared, from: "$.backend_declared" }
|
|
208
280
|
- { name: fe_declared, from: "$.frontend_declared" }
|
|
209
281
|
- { name: fe_excluded, from: "$.frontend_feature_excluded" }
|
|
282
|
+
- { name: be_working, from: "$.backend_working" }
|
|
283
|
+
- { name: fe_working, from: "$.frontend_working" }
|
|
210
284
|
|
|
211
285
|
# The BOUNDED CONTEXT's domain model — where the aggregate root, its entities and its
|
|
212
286
|
# invariants (BR-n) live. The spec cites them by id and the plans record where each is
|
|
@@ -262,23 +336,43 @@ workflow:
|
|
|
262
336
|
on_false: stop
|
|
263
337
|
on_false_message: "No spec.md for the current feature. Run /appweaver specify (and /appweaver plan) first."
|
|
264
338
|
|
|
265
|
-
# 1b.
|
|
266
|
-
# Not version-read's `has_change`: that is bool(active_change), the most
|
|
267
|
-
# OPENED change in active_version.json, and a run that died between cr-setup
|
|
268
|
-
# the directory and the ledger recording it leaves the two disagreeing. The
|
|
269
|
-
# that may rewrite masters has to read the authority every other delta stage
|
|
270
|
-
# No on_error: continue — a cr-list that cannot answer stops the run.
|
|
339
|
+
# 1b. WHICH CHANGES ARE OPEN — asked of the changes/ DIRECTORY, which is what "open"
|
|
340
|
+
# means. Not version-read's `has_change`: that is bool(active_change), the most
|
|
341
|
+
# recently OPENED change in active_version.json, and a run that died between cr-setup
|
|
342
|
+
# writing the directory and the ledger recording it leaves the two disagreeing. The
|
|
343
|
+
# stage that may rewrite masters has to read the authority every other delta stage
|
|
344
|
+
# reads. No on_error: continue — a cr-list that cannot answer stops the run.
|
|
345
|
+
# This first call names every open change for the operator; the mode is decided by
|
|
346
|
+
# the two per-lane calls after it.
|
|
271
347
|
- id: cr_list
|
|
272
348
|
action: execute_script
|
|
273
349
|
script: { ref: actions }
|
|
274
350
|
subcommand: cr-list
|
|
275
351
|
args: ["--feature-dir", "{{feature_dir}}"]
|
|
276
352
|
outputs:
|
|
277
|
-
- { name: has_changes, from: "$.has_changes" }
|
|
278
353
|
- { name: open_ids, from: "$.ids_csv" }
|
|
279
354
|
- { name: open_count, from: "$.count" }
|
|
280
355
|
- { name: active_change, from: "$.active_change" }
|
|
281
356
|
|
|
357
|
+
# The question that decides each lane's mode: which open changes name the lane on their
|
|
358
|
+
# **Lanes** line. A change with no such line names every lane.
|
|
359
|
+
- id: cr_list_backend
|
|
360
|
+
action: execute_script
|
|
361
|
+
script: { ref: actions }
|
|
362
|
+
subcommand: cr-list
|
|
363
|
+
args: ["--feature-dir", "{{feature_dir}}", "--lane", "backend"]
|
|
364
|
+
outputs:
|
|
365
|
+
- { name: be_change_open, from: "$.has_changes" }
|
|
366
|
+
- { name: be_open_ids, from: "$.ids_csv" }
|
|
367
|
+
- id: cr_list_frontend
|
|
368
|
+
action: execute_script
|
|
369
|
+
script: { ref: actions }
|
|
370
|
+
subcommand: cr-list
|
|
371
|
+
args: ["--feature-dir", "{{feature_dir}}", "--lane", "frontend"]
|
|
372
|
+
outputs:
|
|
373
|
+
- { name: fe_change_open, from: "$.has_changes" }
|
|
374
|
+
- { name: fe_open_ids, from: "$.ids_csv" }
|
|
375
|
+
|
|
282
376
|
- id: read_version
|
|
283
377
|
action: execute_script
|
|
284
378
|
script: { ref: actions }
|
|
@@ -287,27 +381,13 @@ workflow:
|
|
|
287
381
|
outputs:
|
|
288
382
|
- { name: version, from: "$.version" }
|
|
289
383
|
|
|
290
|
-
#
|
|
291
|
-
#
|
|
292
|
-
#
|
|
293
|
-
#
|
|
294
|
-
#
|
|
295
|
-
#
|
|
296
|
-
|
|
297
|
-
condition: "{{has_changes}} == false"
|
|
298
|
-
action: set
|
|
299
|
-
vars: { no_open_change: "true" }
|
|
300
|
-
|
|
301
|
-
- id: enter_master_mode
|
|
302
|
-
condition: "{{no_open_change}} == true"
|
|
303
|
-
action: set
|
|
304
|
-
vars: { gate_mode: "master", may_repair: "true" }
|
|
305
|
-
|
|
306
|
-
# 2. WHICH LANES ARE GATED. Present = declared AND on disk; missing = declared AND not.
|
|
307
|
-
# Taking the lane's own `declared` value under the exists condition is how the AND is
|
|
308
|
-
# expressed — an UNDECLARED lane's flags stay false either way, which is what makes a
|
|
309
|
-
# stray plan document from a lane that was later removed silently ignored rather than
|
|
310
|
-
# gated against a stack nobody declared.
|
|
384
|
+
# 2. WHICH LANES ARE GATED. Present = declared AND on disk (its plan is READ); gated =
|
|
385
|
+
# present AND worked (its plan is CHECKED, repaired on approval and receipted); missing
|
|
386
|
+
# = worked AND not on disk. Taking the lane's own value under the exists condition is
|
|
387
|
+
# how each AND is expressed — an UNDECLARED lane's flags stay false either way, which
|
|
388
|
+
# is what makes a stray plan document from a lane that was later removed silently
|
|
389
|
+
# ignored rather than gated against a stack nobody declared, and a lane somebody else
|
|
390
|
+
# works on is read without ever being missing.
|
|
311
391
|
- id: be_flag_present
|
|
312
392
|
condition: "{{backend_plan_exists}} == true"
|
|
313
393
|
action: set
|
|
@@ -315,7 +395,7 @@ workflow:
|
|
|
315
395
|
- id: be_flag_missing
|
|
316
396
|
condition: "{{backend_plan_exists}} == false"
|
|
317
397
|
action: set
|
|
318
|
-
vars: { be_missing: "{{
|
|
398
|
+
vars: { be_missing: "{{be_working}}" }
|
|
319
399
|
- id: fe_flag_present
|
|
320
400
|
condition: "{{frontend_plan_exists}} == true"
|
|
321
401
|
action: set
|
|
@@ -323,10 +403,103 @@ workflow:
|
|
|
323
403
|
- id: fe_flag_missing
|
|
324
404
|
condition: "{{frontend_plan_exists}} == false"
|
|
325
405
|
action: set
|
|
326
|
-
vars: { fe_missing: "{{
|
|
406
|
+
vars: { fe_missing: "{{fe_working}}" }
|
|
407
|
+
- id: be_flag_gated
|
|
408
|
+
condition: "{{be_present}} == true"
|
|
409
|
+
action: set
|
|
410
|
+
vars: { be_gated: "{{be_working}}" }
|
|
411
|
+
- id: fe_flag_gated
|
|
412
|
+
condition: "{{fe_present}} == true"
|
|
413
|
+
action: set
|
|
414
|
+
vars: { fe_gated: "{{fe_working}}" }
|
|
415
|
+
- id: any_gated_be
|
|
416
|
+
condition: "{{be_gated}} == true"
|
|
417
|
+
action: set
|
|
418
|
+
vars: { any_gated: "true" }
|
|
419
|
+
- id: any_gated_fe
|
|
420
|
+
condition: "{{fe_gated}} == true"
|
|
421
|
+
action: set
|
|
422
|
+
vars: { any_gated: "true" }
|
|
423
|
+
|
|
424
|
+
# The working lanes as one csv for plan-gaps — an ordered pair of sets; an empty item
|
|
425
|
+
# left by the second is dropped by plan-gaps.
|
|
426
|
+
- id: work_lanes_be
|
|
427
|
+
condition: "{{be_working}} == true"
|
|
428
|
+
action: set
|
|
429
|
+
vars: { work_lanes: "backend" }
|
|
430
|
+
- id: work_lanes_fe
|
|
431
|
+
condition: "{{fe_working}} == true"
|
|
432
|
+
action: set
|
|
433
|
+
vars: { work_lanes: "{{work_lanes}},frontend" }
|
|
327
434
|
|
|
328
|
-
#
|
|
329
|
-
#
|
|
435
|
+
# 2b. THE WRITE PERMISSION, PER LANE, raised then verified. No open change names the lane
|
|
436
|
+
# ⇒ its plan IS the master ⇒ this stage owns it and may repair it. Anything else — a
|
|
437
|
+
# change naming it, or an answer that never arrived — leaves that lane read-only: it
|
|
438
|
+
# is still checked and reported, but its plan is not rewritten under a delta in
|
|
439
|
+
# flight. The permission is raised by the good state, never knocked down by the bad
|
|
440
|
+
# one, so the failure direction is refusal to write.
|
|
441
|
+
- id: be_no_change_raise
|
|
442
|
+
condition: "{{be_change_open}} == false"
|
|
443
|
+
action: set
|
|
444
|
+
vars: { be_no_change: "true" }
|
|
445
|
+
- id: fe_no_change_raise
|
|
446
|
+
condition: "{{fe_change_open}} == false"
|
|
447
|
+
action: set
|
|
448
|
+
vars: { fe_no_change: "true" }
|
|
449
|
+
- id: be_master_raise
|
|
450
|
+
condition: "{{be_no_change}} == true"
|
|
451
|
+
action: set
|
|
452
|
+
vars: { be_may_repair: "{{be_gated}}" }
|
|
453
|
+
- id: fe_master_raise
|
|
454
|
+
condition: "{{fe_no_change}} == true"
|
|
455
|
+
action: set
|
|
456
|
+
vars: { fe_may_repair: "{{fe_gated}}" }
|
|
457
|
+
- id: enter_master_mode_be
|
|
458
|
+
condition: "{{be_may_repair}} == true"
|
|
459
|
+
action: set
|
|
460
|
+
vars: { gate_mode: "master", may_repair: "true" }
|
|
461
|
+
- id: enter_master_mode_fe
|
|
462
|
+
condition: "{{fe_may_repair}} == true"
|
|
463
|
+
action: set
|
|
464
|
+
vars: { gate_mode: "master", may_repair: "true" }
|
|
465
|
+
- id: be_mode_report
|
|
466
|
+
condition: "{{be_gated}} == true"
|
|
467
|
+
action: set
|
|
468
|
+
vars: { be_lane_mode: "report (open change naming it: {{be_open_ids}})" }
|
|
469
|
+
- id: be_mode_master
|
|
470
|
+
condition: "{{be_may_repair}} == true"
|
|
471
|
+
action: set
|
|
472
|
+
vars: { be_lane_mode: "master" }
|
|
473
|
+
- id: fe_mode_report
|
|
474
|
+
condition: "{{fe_gated}} == true"
|
|
475
|
+
action: set
|
|
476
|
+
vars: { fe_lane_mode: "report (open change naming it: {{fe_open_ids}})" }
|
|
477
|
+
- id: fe_mode_master
|
|
478
|
+
condition: "{{fe_may_repair}} == true"
|
|
479
|
+
action: set
|
|
480
|
+
vars: { fe_lane_mode: "master" }
|
|
481
|
+
|
|
482
|
+
# The lanes this run may repair, for plan-gaps --lanes: a gap in any other lane is listed
|
|
483
|
+
# under `other` and neither drives the repair nor the stop. With no lane in master mode
|
|
484
|
+
# the working lanes are passed as they always were — the read-only run reports by them.
|
|
485
|
+
- id: repair_lanes_default
|
|
486
|
+
action: set
|
|
487
|
+
vars: { repair_lanes: "{{work_lanes}}" }
|
|
488
|
+
- id: repair_lanes_clear
|
|
489
|
+
condition: "{{may_repair}} == true"
|
|
490
|
+
action: set
|
|
491
|
+
vars: { repair_lanes: "" }
|
|
492
|
+
- id: repair_lanes_be
|
|
493
|
+
condition: "{{be_may_repair}} == true"
|
|
494
|
+
action: set
|
|
495
|
+
vars: { repair_lanes: "backend" }
|
|
496
|
+
- id: repair_lanes_fe
|
|
497
|
+
condition: "{{fe_may_repair}} == true"
|
|
498
|
+
action: set
|
|
499
|
+
vars: { repair_lanes: "{{repair_lanes}},frontend" }
|
|
500
|
+
|
|
501
|
+
# A WORKED lane with no plan is not a gap this gate repairs — it is work `plan` has not
|
|
502
|
+
# done. Two independent raises so neither lane can cancel the other's.
|
|
330
503
|
- id: plans_not_ok_backend
|
|
331
504
|
condition: "{{be_missing}} == true"
|
|
332
505
|
action: set
|
|
@@ -340,7 +513,13 @@ workflow:
|
|
|
340
513
|
action: verify_context
|
|
341
514
|
require_true: plans_ok
|
|
342
515
|
on_false: stop
|
|
343
|
-
on_false_message: "A
|
|
516
|
+
on_false_message: "A lane you work on has no plan document (your working lane: `{{working_lane}}` · backend worked={{be_working}} missing={{be_missing}} · frontend worked={{fe_working}} missing={{fe_missing}}). Run /appweaver plan — it authors each working 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."
|
|
517
|
+
|
|
518
|
+
- id: require_gated_lane
|
|
519
|
+
action: verify_context
|
|
520
|
+
require_true: any_gated
|
|
521
|
+
on_false: stop
|
|
522
|
+
on_false_message: "Nothing to gate for your working lane `{{working_lane}}` (.appweaver/lane.json): this feature records no surface in it (backend declared for this feature={{be_declared}}, frontend={{fe_declared}}). Run /appweaver select-lane to work on another lane, or /appweaver select-feature to gate a feature that has this lane."
|
|
344
523
|
|
|
345
524
|
# Check 3 needs BOTH documents. One more ordered assignment: with the backend present,
|
|
346
525
|
# take the frontend's own presence as the answer.
|
|
@@ -420,6 +599,20 @@ workflow:
|
|
|
420
599
|
while: "{{plan_ok}} != true"
|
|
421
600
|
max_iterations: 4
|
|
422
601
|
steps:
|
|
602
|
+
# The request ledger as the plans stand on this pass — a repair on the previous pass
|
|
603
|
+
# may have answered or re-bound a request.
|
|
604
|
+
- id: contract_requests
|
|
605
|
+
condition: "{{both_lanes}} == true"
|
|
606
|
+
action: execute_script
|
|
607
|
+
on_error: continue
|
|
608
|
+
script: { ref: actions }
|
|
609
|
+
subcommand: contract-requests
|
|
610
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
611
|
+
outputs:
|
|
612
|
+
- { name: req_backend_text, from: "$.backend_text" }
|
|
613
|
+
- { name: req_frontend_text, from: "$.frontend_text" }
|
|
614
|
+
- { name: req_summary, from: "$.summary" }
|
|
615
|
+
|
|
423
616
|
- id: analyze
|
|
424
617
|
action: agent_query
|
|
425
618
|
include_standards: true # the gate overlays (checklist) are in the standards
|
|
@@ -431,6 +624,12 @@ workflow:
|
|
|
431
624
|
that apply — backend plan present: {{be_present}}, frontend plan present:
|
|
432
625
|
{{fe_present}}, both present: {{both_lanes}}. A check for an absent lane is
|
|
433
626
|
SKIPPED, not failed: an undeclared lane is a project decision, not a gap.
|
|
627
|
+
WHICH PLANS ARE GATED: backend {{be_gated}}, frontend {{fe_gated}} — the lanes
|
|
628
|
+
the developer running this works on. Run checks 1, 2, 4 and 5 for a GATED plan
|
|
629
|
+
only. A plan that is present and NOT gated belongs to another developer: read it
|
|
630
|
+
for check 3, where the two sides of the contract meet, and nothing else. Every
|
|
631
|
+
gap still carries the `lane` whose document must change to close it, even when
|
|
632
|
+
that lane is not gated here — it is reported for that lane's developer.
|
|
434
633
|
|
|
435
634
|
CHECK 1 — spec.md → backend-plan.md (when the backend plan is in context):
|
|
436
635
|
(a) every spec item — FR / Business Rule / Constraint (CN-) / Data
|
|
@@ -457,7 +656,7 @@ workflow:
|
|
|
457
656
|
|
|
458
657
|
CHECK 3 — CONTRACT BINDING, backend-plan.md ↔ frontend-plan.md (ONLY when
|
|
459
658
|
{{both_lanes}} is true; skip entirely otherwise). This is the check neither
|
|
460
|
-
one-sided pass can make:
|
|
659
|
+
one-sided pass can make, and it runs whichever of the two plans is gated:
|
|
461
660
|
(a) EVERY call, endpoint or payload the frontend plan binds to EXISTS in the
|
|
462
661
|
service plan's published API surface. One that does not is a HIGH gap on the
|
|
463
662
|
`contract` check — lane `backend` when the spec requires the capability (the
|
|
@@ -465,9 +664,15 @@ workflow:
|
|
|
465
664
|
(b) the FIELD SHAPES agree in both directions — a field the frontend reads that
|
|
466
665
|
the service never returns, and a required field the service demands that the
|
|
467
666
|
frontend never sends, are both HIGH gaps;
|
|
468
|
-
(c)
|
|
469
|
-
|
|
470
|
-
|
|
667
|
+
(c) THE REQUEST LEDGER, read by script from both plans ({{req_summary}}). OPEN —
|
|
668
|
+
raised in the frontend plan's "Contract deltas needed" and not answered in the
|
|
669
|
+
backend plan's "Requests from the frontend lane": {{req_backend_text}}. Report
|
|
670
|
+
EACH as a HIGH `contract` gap on lane `backend`, route `plan`, naming the RQ-n:
|
|
671
|
+
the backend plan must publish it or decline it. WAITING ON THE FRONTEND PLAN —
|
|
672
|
+
answered and not yet re-bound, or declined: {{req_frontend_text}}. Report EACH as
|
|
673
|
+
a HIGH `contract` gap on lane `frontend`, route `plan`. `none` means nothing in
|
|
674
|
+
that group. A binding row that cites an open request's proposed id is that
|
|
675
|
+
request, not a separate gap under (a) or (e);
|
|
471
676
|
(d) the frontend plan cites the service's published surface and does NOT restate,
|
|
472
677
|
re-derive or contradict it, and states no server-side behaviour or persistence
|
|
473
678
|
assumption;
|
|
@@ -523,25 +728,37 @@ workflow:
|
|
|
523
728
|
|
|
524
729
|
# WHO CAN CLOSE EACH GAP, decided by script from the tags above: a LIFT-FAILURE is
|
|
525
730
|
# routed to specify whatever its tag says, and an untagged gap is a plan gap.
|
|
731
|
+
# --lanes: a plan gap tagged with a lane this run does not repair — one this
|
|
732
|
+
# developer does not work on, or one an open change names — is listed under `other`;
|
|
733
|
+
# it neither drives the repair nor the stop.
|
|
526
734
|
- id: classify_gaps
|
|
527
735
|
action: execute_script
|
|
528
736
|
script: { ref: actions }
|
|
529
737
|
subcommand: plan-gaps
|
|
530
|
-
args: ["--gaps", "{{analyze_gaps}}"]
|
|
738
|
+
args: ["--gaps", "{{analyze_gaps}}", "--lanes", "{{repair_lanes}}"]
|
|
531
739
|
outputs:
|
|
532
740
|
- { name: plan_ok, from: "$.plan_ok" }
|
|
533
741
|
- { name: gate_verdict, from: "$.verdict" }
|
|
534
742
|
- { name: plan_gap_count, from: "$.plan_gaps" }
|
|
535
743
|
- { name: routed_gaps, from: "$.routed" }
|
|
536
744
|
- { name: routed_csv, from: "$.routed_csv" }
|
|
745
|
+
- { name: other_gaps, from: "$.other_gaps" }
|
|
746
|
+
- { name: other_csv, from: "$.other_csv" }
|
|
747
|
+
- { name: be_verdict, from: "$.backend_verdict" }
|
|
748
|
+
- { name: be_plan_gaps, from: "$.backend_plan_gaps" }
|
|
749
|
+
- { name: be_routed, from: "$.backend_routed" }
|
|
750
|
+
- { name: fe_verdict, from: "$.frontend_verdict" }
|
|
751
|
+
- { name: fe_plan_gaps, from: "$.frontend_plan_gaps" }
|
|
752
|
+
- { name: fe_routed, from: "$.frontend_routed" }
|
|
537
753
|
- id: conform_break
|
|
538
754
|
action: break
|
|
539
755
|
condition: "{{analyze_ok}} == true"
|
|
540
756
|
|
|
541
|
-
#
|
|
542
|
-
# master plans are off limits and there is nothing to ask:
|
|
543
|
-
# Write the report and leave the loop. The report itself has
|
|
544
|
-
# readings, because the same gaps mean opposite things either side of
|
|
757
|
+
# NO LANE IN MASTER MODE — an open change names every gated lane (or the mode could not
|
|
758
|
+
# be established), so the master plans are off limits and there is nothing to ask:
|
|
759
|
+
# repair is not on offer. Write the report and leave the loop. The report itself has
|
|
760
|
+
# to name the two readings, because the same gaps mean opposite things either side of
|
|
761
|
+
# the merge.
|
|
545
762
|
- id: write_report_readonly
|
|
546
763
|
condition: "{{may_repair}} == false"
|
|
547
764
|
action: agent_author
|
|
@@ -549,12 +766,13 @@ workflow:
|
|
|
549
766
|
output_target: { type: file, path: "{{feature_dir}}/plan-analyze-report.md" }
|
|
550
767
|
task: |
|
|
551
768
|
Write a Plan Analyze Report to the output from these spec↔plan conformance gaps
|
|
552
|
-
(spec.md is the ANCHOR). This run was READ-ONLY: change
|
|
553
|
-
|
|
769
|
+
(spec.md is the ANCHOR). This run was READ-ONLY: an open change names each lane it
|
|
770
|
+
checked — backend plan: {{be_lane_mode}}; frontend plan: {{fe_lane_mode}} — so the
|
|
771
|
+
master lane plans were checked but NOT repaired.
|
|
554
772
|
{{analyze_gaps}}
|
|
555
773
|
Markdown format:
|
|
556
774
|
- "# Plan Analyze Report — <aggregate>" plus today's date.
|
|
557
|
-
- A verdict line: "verdict: GAPS ✗ · <n> gap(s) · read-only (
|
|
775
|
+
- A verdict line: "verdict: GAPS ✗ · <n> gap(s) · read-only (backend plan: {{be_lane_mode}}; frontend plan: {{fe_lane_mode}})".
|
|
558
776
|
- "## Gaps" as a table: | # | check | lane | severity | spec ref | plan location | gap |.
|
|
559
777
|
- "## How to read this" — state BOTH readings and that only the operator knows
|
|
560
778
|
which applies, because it depends on whether finalize-delta has already run:
|
|
@@ -567,10 +785,15 @@ workflow:
|
|
|
567
785
|
(b) BEFORE finalize-delta, the masters have not moved, so these gaps are real
|
|
568
786
|
master drift — but this is still not the stage that fixes it, because the
|
|
569
787
|
change in flight is planned in its OWN delta plans. Gate those with
|
|
570
|
-
/appweaver analyze-plan-delta, and re-run /appweaver analyze-plan
|
|
571
|
-
|
|
788
|
+
/appweaver analyze-plan-delta, and re-run /appweaver analyze-plan once no open
|
|
789
|
+
change names the lane, when it runs in master mode.
|
|
572
790
|
- "## Not repaired here": this report is the whole output of a read-only run. No
|
|
573
791
|
plan document was written.
|
|
792
|
+
- "## Left to another developer or a later run" — only when this list is not
|
|
793
|
+
empty: findings in a
|
|
794
|
+
plan this run does not repair — a lane this developer does not work on, for that
|
|
795
|
+
lane's developer to pick up, or a lane an open change names, re-gated once that
|
|
796
|
+
change closes: {{other_csv}}
|
|
574
797
|
Return the complete report markdown only.
|
|
575
798
|
- id: readonly_break
|
|
576
799
|
action: break
|
|
@@ -623,6 +846,11 @@ workflow:
|
|
|
623
846
|
invariant routes to /appweaver specify, which authors domain-model.md; a
|
|
624
847
|
`contract` gap is closed in the lane the row names. Then re-run
|
|
625
848
|
/appweaver analyze-plan to clear the report.
|
|
849
|
+
- "## Left to another developer or a later run" — only when this list is not
|
|
850
|
+
empty: findings in a
|
|
851
|
+
plan this run does not repair — a lane this developer does not work on, for that
|
|
852
|
+
lane's developer to pick up, or a lane an open change names, re-gated once that
|
|
853
|
+
change closes: {{other_csv}}
|
|
626
854
|
Return the complete report markdown only.
|
|
627
855
|
- id: skip_break
|
|
628
856
|
action: break
|
|
@@ -640,7 +868,7 @@ workflow:
|
|
|
640
868
|
- id: be_repair_flag
|
|
641
869
|
condition: "{{backend_repair}} == true"
|
|
642
870
|
action: set
|
|
643
|
-
vars: { be_repair: "{{
|
|
871
|
+
vars: { be_repair: "{{be_may_repair}}" }
|
|
644
872
|
- id: be_repair_last_pass
|
|
645
873
|
condition: "{{repairs_left}} == false"
|
|
646
874
|
action: set
|
|
@@ -651,7 +879,7 @@ workflow:
|
|
|
651
879
|
- id: fe_repair_flag
|
|
652
880
|
condition: "{{frontend_repair}} == true"
|
|
653
881
|
action: set
|
|
654
|
-
vars: { fe_repair: "{{
|
|
882
|
+
vars: { fe_repair: "{{fe_may_repair}}" }
|
|
655
883
|
- id: fe_repair_last_pass
|
|
656
884
|
condition: "{{repairs_left}} == false"
|
|
657
885
|
action: set
|
|
@@ -680,6 +908,13 @@ workflow:
|
|
|
680
908
|
leave it open and add a DA-n saying it routes to /appweaver specify.
|
|
681
909
|
- CONTRACT gaps whose lane is `backend`: PUBLISH the missing surface in the API
|
|
682
910
|
section as field shapes, so the frontend plan has something real to bind to.
|
|
911
|
+
- ANSWER EVERY OPEN CONTRACT REQUEST: {{req_backend_text}}. Record each in the API
|
|
912
|
+
section's "Requests from the frontend lane" table (| Request | Outcome |
|
|
913
|
+
Operation id | Why |; add the table if there is none): PUBLISH it as an endpoint,
|
|
914
|
+
under the operation id it proposes unless that clashes with one already in use,
|
|
915
|
+
or DECLINE it with the reason — a request the spec gives no basis for is
|
|
916
|
+
declined as a spec gap for /appweaver specify, never published on a guess.
|
|
917
|
+
Keep every row the table already has.
|
|
683
918
|
- REMOVE, or move to a DA-n assumption, any plan element with no spec basis —
|
|
684
919
|
never keep a plan that exceeds or contradicts the spec. EXCEPTION: do NOT
|
|
685
920
|
remove a cross-cutting element that is (or may be) mandated by the technical
|
|
@@ -694,6 +929,8 @@ workflow:
|
|
|
694
929
|
CN-/DC-/IC-". (The gate will stop and direct the user to specify.)
|
|
695
930
|
- Keep the Traceability section in sync. Preserve every already-conformant
|
|
696
931
|
section VERBATIM. Do NOT change the spec, the domain model or the frontend plan.
|
|
932
|
+
Close only the gaps whose lane is `backend`; a frontend gap is not this
|
|
933
|
+
document's.
|
|
697
934
|
Return the COMPLETE updated backend-plan.md content only.
|
|
698
935
|
|
|
699
936
|
- id: repair_frontend
|
|
@@ -718,8 +955,13 @@ workflow:
|
|
|
718
955
|
published surface (in context). CITE it; do not restate, re-derive or
|
|
719
956
|
contradict it, and introduce NO server-side behaviour or persistence
|
|
720
957
|
assumption. A capability the experience needs that the surface does not offer
|
|
721
|
-
|
|
722
|
-
|
|
958
|
+
is a CONTRACT REQUEST in §5's "Contract deltas needed" table — a new RQ-n, the
|
|
959
|
+
operation id it proposes, what it sends and receives, the spec ids it serves —
|
|
960
|
+
and the binding row that needs it cites that id; it is the service lane's to
|
|
961
|
+
publish, never a client-side workaround. Keep every request with its id.
|
|
962
|
+
- THE BACKEND'S ANSWERS: {{req_frontend_text}}. Re-bind every binding row of an
|
|
963
|
+
answered request to the operation id the backend gave it; plan around a
|
|
964
|
+
declined one, or log a DA-n routing the need to /appweaver specify.
|
|
723
965
|
- Use the domain model's UBIQUITOUS LANGUAGE exactly in labels, states and
|
|
724
966
|
messages, and cite BR-nnn rather than restating a rule. DO NOT EDIT
|
|
725
967
|
domain-model.md or backend-plan.md.
|
|
@@ -740,21 +982,55 @@ workflow:
|
|
|
740
982
|
# EVERY MASTER RUN LEAVES ONE, and it is written BEFORE the stop below, so a red run
|
|
741
983
|
# records its `gaps` verdict too and implement refuses it. A REPORT-mode run leaves
|
|
742
984
|
# none: it checked the masters while a delta was in flight, and they are about to move.
|
|
985
|
+
#
|
|
986
|
+
# ONE RECEIPT PER MASTER-MODE LANE, each with that lane's own verdict, so the two lanes'
|
|
987
|
+
# implement runs never refuse each other. A lane read but not gated gets none from this
|
|
988
|
+
# run, and neither does a lane an open change names: it was checked in report mode.
|
|
743
989
|
- id: flag_receipt
|
|
744
990
|
condition: "{{may_repair}} == true"
|
|
745
991
|
action: set
|
|
746
992
|
vars: { stamp_receipt: "true" }
|
|
747
|
-
|
|
748
|
-
|
|
993
|
+
- id: flag_receipt_be
|
|
994
|
+
condition: "{{stamp_receipt}} == true"
|
|
995
|
+
action: set
|
|
996
|
+
vars: { stamp_be: "{{be_may_repair}}" }
|
|
997
|
+
- id: flag_receipt_fe
|
|
749
998
|
condition: "{{stamp_receipt}} == true"
|
|
999
|
+
action: set
|
|
1000
|
+
vars: { stamp_fe: "{{fe_may_repair}}" }
|
|
1001
|
+
|
|
1002
|
+
# THE DRAFT, redrawn from the backend plan as this run leaves it — a repair may have
|
|
1003
|
+
# published an endpoint or answered a request. Master mode only, like the receipt, and
|
|
1004
|
+
# only for a gated backend plan. Advisory: it never fails the gate.
|
|
1005
|
+
- id: contract_draft
|
|
1006
|
+
condition: "{{stamp_be}} == true"
|
|
1007
|
+
action: execute_script
|
|
1008
|
+
on_error: continue
|
|
1009
|
+
script: { ref: actions }
|
|
1010
|
+
subcommand: contract-draft
|
|
1011
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
1012
|
+
outputs:
|
|
1013
|
+
- { name: draft_path, from: "$.path" }
|
|
1014
|
+
|
|
1015
|
+
- id: plan_gate_stamp_backend
|
|
1016
|
+
condition: "{{stamp_be}} == true"
|
|
1017
|
+
action: execute_script
|
|
1018
|
+
script: { ref: actions }
|
|
1019
|
+
subcommand: plan-gate-stamp
|
|
1020
|
+
args: ["--feature-dir", "{{feature_dir}}", "--lane", "backend", "--spec-version", "{{version}}", "--mode", "{{gate_mode}}", "--verdict", "{{be_verdict}}", "--plan-gaps", "{{be_plan_gaps}}", "--routed", "{{be_routed}}"]
|
|
1021
|
+
outputs:
|
|
1022
|
+
- { name: be_receipt, from: "$.file" }
|
|
1023
|
+
- { name: receipt_gated, from: "$.gated" }
|
|
1024
|
+
|
|
1025
|
+
- id: plan_gate_stamp_frontend
|
|
1026
|
+
condition: "{{stamp_fe}} == true"
|
|
750
1027
|
action: execute_script
|
|
751
1028
|
script: { ref: actions }
|
|
752
1029
|
subcommand: plan-gate-stamp
|
|
753
|
-
args: ["--feature-dir", "{{feature_dir}}", "--spec-version", "{{version}}", "--mode", "{{gate_mode}}", "--verdict", "{{
|
|
1030
|
+
args: ["--feature-dir", "{{feature_dir}}", "--lane", "frontend", "--spec-version", "{{version}}", "--mode", "{{gate_mode}}", "--verdict", "{{fe_verdict}}", "--plan-gaps", "{{fe_plan_gaps}}", "--routed", "{{fe_routed}}"]
|
|
754
1031
|
outputs:
|
|
755
|
-
- { name:
|
|
1032
|
+
- { name: fe_receipt, from: "$.file" }
|
|
756
1033
|
- { name: receipt_gated, from: "$.gated" }
|
|
757
|
-
- { name: receipt_count, from: "$.count" }
|
|
758
1034
|
|
|
759
1035
|
# THE STOP IS MASTER MODE'S ALONE, and only a PLAN gap triggers it. Two independent
|
|
760
1036
|
# raises, because the two reasons to pass are unrelated: no gap is left that a plan
|
|
@@ -775,7 +1051,7 @@ workflow:
|
|
|
775
1051
|
action: verify_context
|
|
776
1052
|
require_true: gate_ok
|
|
777
1053
|
on_false: stop
|
|
778
|
-
on_false_message: "The lane
|
|
1054
|
+
on_false_message: "The plans of your working lane `{{working_lane}}` do not conform to the spec: {{plan_gap_count}} gap(s) a plan rewrite closes are still open (all gaps: {{analyze_gaps}}). Either you chose `Report and Skip`, in which case plan-analyze-report.md beside the plans lists them — or repair ran up to three times and the fourth pass, which checks and does not rewrite, still found these. Every gap above was read off the plans as they stand on disk now. Each gated lane's receipt records its own verdict, so /appweaver implement refuses a lane left with `gaps` until this gate is re-run clean, and generates a lane that passed. A `contract` gap is closed in the lane its row names. Gaps routed to `specify` ({{routed_csv}}) are not what stopped this run: they need /appweaver specify, which authors the spec and the model. Fix out of band, or re-run and choose Repair."
|
|
779
1055
|
|
|
780
1056
|
# (No change-close here by design — this stage never archives anything in either mode,
|
|
781
1057
|
# and cr-archive belongs to close-delta, which runs after finalize-delta's merge.)
|
|
@@ -805,9 +1081,18 @@ outputs:
|
|
|
805
1081
|
plan_gaps: "{{plan_gap_count}}" # open gaps a plan rewrite closes
|
|
806
1082
|
routed_to_specify: "{{routed_csv}}" # open gaps only /appweaver specify can close
|
|
807
1083
|
version: "{{version}}"
|
|
808
|
-
gate_mode: "{{gate_mode}}" # master (
|
|
809
|
-
|
|
810
|
-
|
|
1084
|
+
gate_mode: "{{gate_mode}}" # master (some lane repairable) | report (read-only)
|
|
1085
|
+
backend_mode: "{{be_lane_mode}}" # master | report (an open change names the backend) | not gated
|
|
1086
|
+
frontend_mode: "{{fe_lane_mode}}"
|
|
1087
|
+
open_changes: "{{open_ids}}" # every open change on the feature, whichever lanes it names
|
|
1088
|
+
working_lane: "{{working_lane}}" # the developer's working lane(s) — .appweaver/lane.json
|
|
1089
|
+
gated_backend: "{{be_gated}}" # checked by this run; repaired and receipted in master mode only
|
|
1090
|
+
gated_frontend: "{{fe_gated}}"
|
|
1091
|
+
backend_receipt: "{{be_receipt}}" # .aw-plan-gate.backend.json, with the backend's own verdict ({{be_verdict}})
|
|
1092
|
+
frontend_receipt: "{{fe_receipt}}" # .aw-plan-gate.frontend.json, with the frontend's own verdict ({{fe_verdict}})
|
|
811
1093
|
gate_receipt_at: "{{receipt_gated}}"
|
|
812
|
-
|
|
813
|
-
|
|
1094
|
+
other_lane_gaps: "{{other_csv}}" # findings in a plan this run does not repair — another developer's lane, or one an open change names
|
|
1095
|
+
contract_requests: "{{req_summary}}" # the frontend plan's RQ-n requests and the backend plan's answers, as this run left them
|
|
1096
|
+
api_draft: "{{draft_path}}" # api-draft.json, redrawn from the gated backend plan's API section — commit it with the plan
|
|
1097
|
+
note: "Ran for your working lane `{{working_lane}}` — backend plan: {{be_lane_mode}}; frontend plan: {{fe_lane_mode}} ({{open_count}} change(s) open: {{open_ids}}). A lane is in master mode unless an open change names it on its **Lanes** line. Gated: spec→backend-plan ({{be_gated}}), spec→frontend-plan ({{fe_gated}}), the plan↔plan contract binding ({{both_lanes}} — it needs both plans, and reads the one you do not work on without repairing it) and each gated plan against domain-model.md. {{other_gaps}} finding(s) are in a plan this run does not repair — a lane you do not work on, for its developer, or a lane an open change names — and are reported, not repaired: {{other_csv}}. The domain model was READ, never written: a gap that needs a new entity, event or invariant is fixed by /appweaver specify. A plan in REPORT mode was not rewritten either — a master plan may not move while a delta that names its lane is in flight."
|
|
1098
|
+
next: "MASTER mode (no open change names the lane, verdict={{gate_verdict}}): /appweaver implement — no gap a plan rewrite closes is left; version unchanged, this gate closes nothing. If the verdict is routed-gaps, implement generates and warns: {{routed_csv}} — each is a spec or model gap, lifted by opening a change with /appweaver specify. REPORT mode (an open change names the lane — backend: {{be_open_ids}}; frontend: {{fe_open_ids}}): that lane's plan was checked read-only, and a run with no lane in master mode wrote plan-analyze-report.md only. If finalize-delta has already merged, the gaps ARE that merge — the lane plans are derived from spec.md + domain-model.md and finalize-delta moves only those two, so re-derive with /appweaver plan main after /appweaver close-delta, then re-run /appweaver analyze-plan in master mode. If the change has NOT merged yet, gate it where it lives: /appweaver analyze-plan-delta."
|