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.
Files changed (67) hide show
  1. package/assets/.amp/agent.json +22 -0
  2. package/assets/.amp/settings-edit.json +5 -0
  3. package/assets/.amp/settings-read.json +12 -0
  4. package/assets/.appweaver/DESIGN-FORMAT.md +9 -0
  5. package/assets/.appweaver/STACK-PROFILES.md +102 -15
  6. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +40 -18
  7. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +56 -4
  8. package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +17 -1
  9. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +12 -3
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +289 -24
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +370 -85
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +349 -65
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +39 -7
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +63 -4
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +5 -4
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +430 -138
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +392 -76
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +104 -0
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +20 -3
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +26 -3
  21. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +314 -44
  22. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +249 -20
  23. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +17 -2
  24. package/assets/.appweaver/scripts/python/aw-plangate.py +109 -19
  25. package/assets/.appweaver/scripts/python/aw-stack.py +1295 -9
  26. package/assets/.appweaver/templates/delta-design-template.md +7 -5
  27. package/assets/.appweaver/templates/delta-spec-template.md +2 -0
  28. package/assets/.appweaver/templates/design-template.md +17 -0
  29. package/assets/.appweaver/templates/ui-design-template.md +19 -4
  30. package/assets/.augment/agent.json +22 -0
  31. package/assets/.augment/commands/appweaver.md +12 -0
  32. package/assets/.cline/agent.json +22 -0
  33. package/assets/.clinerules/workflows/appweaver.md +7 -0
  34. package/assets/.codex/agent.json +22 -0
  35. package/assets/.continue/agent.json +22 -0
  36. package/assets/.continue/prompts/appweaver.md +13 -0
  37. package/assets/.cursor/agent.json +22 -0
  38. package/assets/.cursor/commands/appweaver.md +7 -0
  39. package/assets/.devin/agent.json +22 -0
  40. package/assets/.devin/workflows/appweaver.md +11 -0
  41. package/assets/.factory/agent.json +22 -0
  42. package/assets/.factory/commands/appweaver.md +12 -0
  43. package/assets/.gemini/agent.json +22 -0
  44. package/assets/.github/prompts/appweaver.prompt.md +13 -0
  45. package/assets/.kilo/agent.json +28 -0
  46. package/assets/.kilo/agents/appweaver-edit.md +12 -0
  47. package/assets/.kilo/agents/appweaver-read.md +12 -0
  48. package/assets/.kilo/commands/appweaver.md +11 -0
  49. package/assets/.kiro/agent.json +22 -0
  50. package/assets/.kiro/skills/appweaver/SKILL.md +12 -0
  51. package/assets/.opencode/agent.json +22 -0
  52. package/assets/.opencode/agents/appweaver-edit.md +12 -0
  53. package/assets/.opencode/agents/appweaver-read.md +13 -0
  54. package/assets/.opencode/commands/appweaver.md +11 -0
  55. package/assets/.roo/agent.json +22 -0
  56. package/assets/.roo/commands/appweaver.md +12 -0
  57. package/dist/commands/init.d.ts.map +1 -1
  58. package/dist/commands/init.js +14 -1
  59. package/dist/commands/init.js.map +1 -1
  60. package/dist/utils/config.d.ts.map +1 -1
  61. package/dist/utils/config.js +14 -0
  62. package/dist/utils/config.js.map +1 -1
  63. package/dist/utils/copy-assets.d.ts +16 -3
  64. package/dist/utils/copy-assets.d.ts.map +1 -1
  65. package/dist/utils/copy-assets.js +29 -14
  66. package/dist/utils/copy-assets.js.map +1 -1
  67. 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.json), green or not: the bytes it
53
- # checked and the verdict — conformant, routed-gaps or gaps. implement reads it and
54
- # refuses moved bytes or `gaps`, and warns on `routed-gaps`.
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 `active_change` — the ledger is the most
58
- # recently OPENED change and a crashed run can leave the two disagreeing. The permission to
59
- # write is RAISED only by cr-list answering that nothing is open, so a reading that never
60
- # arrives leaves this stage read-only rather than rewriting masters mid-delta.
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. Runs in one of two modes, decided from the changes/ directory rather than the ledger and defaulting to the read-only one — MASTER (no change open) checks, repairs on approval and stops on unreconciled gaps; REPORT (a change open) runs the same checks read-only and writes plan-analyze-report.md without asking, repairing or stopping, because the master plans may not be rewritten while a delta is in flight. It never opens, merges or closes a change.
76
- version: 3.3.0
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
- # WHICH MODE THIS RUN IS IN, read from the changes/ DIRECTORY and defaulted to the
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
- no_open_change: "false" # RAISED only by cr-list answering that nothing is open
104
- gate_mode: "report" # report | master — reported in the outputs
105
- may_repair: "false" # the ask, the repair loop and the conformance stop are MASTER-only
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
- # PER-LANE PRESENCE. A lane is CHECKED when it is declared and its plan document is on
124
- # disk, and MISSING when it is declared and the document is not. The engine has no
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. IS ANY CHANGE OPEN — asked of the changes/ DIRECTORY, which is what "open" means.
266
- # Not version-read's `has_change`: that is bool(active_change), the most recently
267
- # OPENED change in active_version.json, and a run that died between cr-setup writing
268
- # the directory and the ledger recording it leaves the two disagreeing. The stage
269
- # that may rewrite masters has to read the authority every other delta stage reads.
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
- # 1c. THE WRITE PERMISSION, raised then verified. Nothing open ⇒ spec.md + the lane plans
291
- # ARE the masters ⇒ this stage owns them and may repair. Anything else — a change
292
- # open, or an answer that never arrived — leaves may_repair down and the run is
293
- # read-only: the checks still run and still report, but no plan is rewritten under a
294
- # delta in flight. The permission is raised by the good state, never knocked down by
295
- # the bad one, so the failure direction is refusal to write.
296
- - id: no_open_change_raise
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: "{{be_declared}}" }
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: "{{fe_declared}}" }
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
- # A declared lane with no plan is not a gap this gate repairs — it is work `plan` has
329
- # not done. Two independent raises so neither lane can cancel the other's.
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 declared lane has no plan document (backend declared={{be_declared}} missing={{be_missing}} · frontend declared={{fe_declared}} missing={{fe_missing}}). Run /appweaver plan — it authors each declared lane's plan from the spec, the domain model, that lane's rule set and the feature's real source files. This gate validates a plan; it does not write one."
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) anything the frontend plan raised under "Contract deltas needed" is
469
- UNRESOLVED by definition: report it as a gap so it is closed here rather than
470
- discovered at integration;
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
- # REPORT MODE — a change is open (or the mode could not be established), so the
542
- # master plans are off limits and there is nothing to ask: repair is not on offer.
543
- # Write the report and leave the loop. The report itself has to name the two
544
- # readings, because the same gaps mean opposite things either side of the merge.
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(s) {{open_ids}} are open
553
- on this feature, so the master lane plans were checked but NOT repaired.
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 (change open: {{open_ids}})".
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 in master
571
- mode once every change is closed.
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: "{{be_present}}" }
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: "{{fe_present}}" }
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
- stays under "Contract deltas needed" — it is the service lane's to publish,
722
- never a client-side workaround.
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
- - id: plan_gate_stamp
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", "{{gate_verdict}}", "--plan-gaps", "{{plan_gap_count}}", "--routed", "{{routed_gaps}}"]
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: receipt_file, from: "$.file" }
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 plans 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. The receipt records verdict `gaps`, so /appweaver implement will refuse until this gate is re-run clean. 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."
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 (repair allowed) | report (read-only)
809
- open_changes: "{{open_ids}}" # empty in master mode, by definition
810
- gate_receipt: "{{receipt_file}}" # written by every master run, with its verdict; implement reads it
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
- note: "Ran in {{gate_mode}} mode ({{open_count}} change(s) open: {{open_ids}}). Checked: spec→backend-plan ({{be_present}}), spec→frontend-plan ({{fe_present}}), the plan↔plan contract binding ({{both_lanes}} — it needs both lanes) and both plans against domain-model.md. The domain model was READ, never written: a gap that needs a new entity, event or invariant is fixed by /appweaver specify. In REPORT mode no plan was rewritten either — the master plans are derived documents and may not move while a delta is in flight."
813
- next: "MASTER mode (no change open, 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 (change(s) {{open_ids}} open): this run was read-only and 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."
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."