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
@@ -28,6 +28,25 @@
28
28
  # Plus the OVERLAY BACKSTOP (the domain gate checklist over the constraints this change
29
29
  # touches), and the WAVE DISCIPLINE check below.
30
30
  #
31
+ # ONLY THE LANES THE CHANGE NAMES. A change's delta-spec carries a **Lanes** line, and
32
+ # `plan` writes a delta plan only for the lanes on it. A lane it leaves out is not gated, so
33
+ # a backend-only change is not refused for having no frontend delta plan. A change with no
34
+ # such line touches every lane.
35
+ #
36
+ # ONLY THE LANES YOU WORK ON ARE GATED (.appweaver/lane.json). The change menu lists only the
37
+ # changes naming a working lane. A named lane you work on is gated: its delta plan must
38
+ # exist, is checked and is repaired on approval. A named lane somebody else works on is
39
+ # READ: its delta plan, once its developer has written it, is in context for check 3 and
40
+ # nothing else, and a gap only that plan can close is reported for its developer rather than
41
+ # repaired or stopped on. Its delta plan not existing yet is a note, not a refusal — check 3
42
+ # waits until both halves are on disk.
43
+ #
44
+ # A SERVICE CHANGE THAT BREAKS A FRONTEND BINDING IS A WARNING (contract-impact). An
45
+ # operation the service delta changes or removes that the master frontend plan binds to,
46
+ # and that the change's frontend delta plan does not name, is reported by script before the
47
+ # check — whatever lanes the change names. The frontend half may follow from its own
48
+ # developer or in a follow-up change, so it never stops the gate.
49
+ #
31
50
  # PREDECESSORS, NOT SIBLINGS. A wave is a dependency level. A LOWER-wave change is
32
51
  # committed to landing first, so this delta may be planned on top of it — and both halves
33
52
  # of each predecessor are in context, its model delta AND its delta-spec, because a plan
@@ -53,8 +72,8 @@
53
72
  # =============================================================================
54
73
 
55
74
  name: aw-spec-analyze-plan-delta
56
- description: The delta-spec↔delta-plan conformance gate for ONE open change — confirm its delta-backend-plan.md and delta-frontend-plan.md realise delta-spec.md, bind to each other's contract deltas, realise this change's domain model delta, and only MODIFY elements the masters really define. Checked against the EFFECTIVE model (domain-model.md plus this change's model delta plus every lower-wave predecessor's, whose delta-specs are in context too) and told, deterministically, which other open changes touch the same aggregates. Asks which open change it is gating when more than one is open; a CR id as the argument selects without a prompt. Does not modify the masters or close the change.
57
- version: 2.0.0
75
+ description: The delta-spec↔delta-plan conformance gate for ONE open change — confirm its delta-backend-plan.md and delta-frontend-plan.md realise delta-spec.md, bind to each other's contract deltas, realise this change's domain model delta, and only MODIFY elements the masters really define. Checked against the EFFECTIVE model (domain-model.md plus this change's model delta plus every lower-wave predecessor's, whose delta-specs are in context too) and told, deterministically, which other open changes touch the same aggregates. Gates only the lanes the change names that you work on — the other developer's delta plan is read for the contract check, its gaps are reported for them, and its absence is a note. Warns, by script, when the service delta changes or removes an operation the frontend binds to and the frontend half does not follow. Asks which open change it is gating when more than one names your lane; a CR id as the argument selects without a prompt. Does not modify the masters or close the change.
76
+ version: 2.2.0
58
77
  agent_agnostic: true
59
78
  dialect: aw-spec.orchestrator/v1
60
79
 
@@ -87,9 +106,13 @@ vars:
87
106
  sibling_count: "0"
88
107
  overlap_report: ""
89
108
  # PER-LANE PRESENCE. A lane is gated when it is declared AND has a master plan (only
90
- # then is there a base for a delta) AND this change has a delta plan for it. The engine
91
- # has no compound conditions, so each AND is one ordered assignment.
92
- be_gated: "false" # declared AND a master plan exists
109
+ # then is there a base for a delta) AND the change's **Lanes** line names it AND this
110
+ # change has a delta plan for it. The engine has no compound conditions, so each AND is
111
+ # one ordered assignment.
112
+ cr_lanes_csv: ""
113
+ cr_touches_backend: "true" # every lane until cr-paths answers
114
+ cr_touches_frontend: "true"
115
+ be_gated: "false" # declared AND a master plan exists AND the change names it
93
116
  fe_gated: "false"
94
117
  be_present: "false" # gated AND this change's delta plan exists ⇒ check it
95
118
  fe_present: "false"
@@ -97,7 +120,39 @@ vars:
97
120
  fe_missing: "false"
98
121
  deltas_ok: "true"
99
122
  any_lane: "false"
100
- both_lanes: "false" # both delta plans present ⇒ the contract check applies
123
+ both_lanes: "false" # both delta plans in context ⇒ the contract check applies
124
+ # THE WORKING LANES (.appweaver/lane.json). A named lane is GATED only when it is worked;
125
+ # one that is not is SEEN (its delta plan read for check 3) or AWAITED (not written yet).
126
+ be_working: "false"
127
+ fe_working: "false"
128
+ working_lane: ""
129
+ work_lanes: "" # the working lanes as a csv, for plan-gaps --lanes
130
+ working_kind: "" # the one lane worked when one lane of two is; empty otherwise
131
+ report_file: "" # delta-plan-analyze-report[.<working_kind>].md in the change
132
+ be_named: "false" # declared AND a master AND the change names it
133
+ fe_named: "false"
134
+ be_seen: "false" # named AND its delta plan on disk ⇒ in context
135
+ fe_seen: "false"
136
+ be_awaited: "false" # named, not worked, no delta plan yet ⇒ the other developer's half
137
+ fe_awaited: "false"
138
+ no_awaited: "true"
139
+ awaited_txt: ""
140
+ apd_hidden_note: "" # open changes naming only a lane not worked here
141
+ apd_hidden_csv: ""
142
+ apd_preselect_hidden: ""
143
+ preselect_visible: "true"
144
+ # THE GAP SPLIT (plan-gaps --lanes): a gap only the other developer's delta plan can close
145
+ # is listed for them and neither drives the repair nor the stop.
146
+ own_plan_gaps: "0"
147
+ own_routed: "0"
148
+ other_gaps: "0"
149
+ other_csv: ""
150
+ own_clear: "false" # the only gaps left are the other developer's
151
+ gate_ok: "false"
152
+ no_other_gaps: "true"
153
+ # D4b — operations the service delta changes or removes that the frontend binds to.
154
+ impact_csv: ""
155
+ no_impact: "true"
101
156
  be_repair: "false"
102
157
  fe_repair: "false"
103
158
  backend_repair: "false"
@@ -126,6 +181,7 @@ workflow:
126
181
  - { name: lanes_declared, from: "$.declared" }
127
182
  - { name: be_declared, from: "$.backend_declared" }
128
183
  - { name: fe_declared, from: "$.frontend_declared" }
184
+ - { name: working_lane, from: "$.working_lane" }
129
185
 
130
186
  - id: require_lane
131
187
  action: verify_context
@@ -166,6 +222,20 @@ workflow:
166
222
  - { name: be_declared, from: "$.backend_declared" }
167
223
  - { name: fe_declared, from: "$.frontend_declared" }
168
224
  - { name: fe_excluded, from: "$.frontend_feature_excluded" }
225
+ - { name: be_working, from: "$.backend_working" }
226
+ - { name: fe_working, from: "$.frontend_working" }
227
+ - { name: working_kind, from: "$.working_kind" }
228
+
229
+ # The working lanes as one csv for plan-gaps — an ordered pair of sets; an empty item
230
+ # left by the second is dropped by plan-gaps.
231
+ - id: work_lanes_be
232
+ condition: "{{be_working}} == true"
233
+ action: set
234
+ vars: { work_lanes: "backend" }
235
+ - id: work_lanes_fe
236
+ condition: "{{fe_working}} == true"
237
+ action: set
238
+ vars: { work_lanes: "{{work_lanes}},frontend" }
169
239
 
170
240
  - id: require_feature
171
241
  action: verify_context
@@ -180,25 +250,43 @@ workflow:
180
250
  on_false_message: "No spec.md for the current feature. Run /appweaver specify (and /appweaver plan) first."
181
251
 
182
252
  # 2. WHICH CHANGES ARE OPEN. The evidence is changes/<cr-id>/ on disk; a closed change
183
- # has moved to changes/archive/. `matched_label` resolves a CR id passed as the
253
+ # has moved to changes/archive/. Only the changes naming a working lane on their
254
+ # **Lanes** line are listed (`--lane`, last in the args so an empty value cannot be
255
+ # mistaken for the next flag). `matched_label` resolves a CR id passed as the
184
256
  # command's argument to the whole option string, so the pick below can auto-select it
185
257
  # — the engine matches a preselection against the option text exactly.
186
258
  - id: cr_list
187
259
  action: execute_script
188
260
  script: { ref: actions }
189
261
  subcommand: cr-list
190
- args: ["--feature-dir", "{{feature_dir}}", "--preselect", "{{USER_INPUT}}"]
262
+ args: ["--feature-dir", "{{feature_dir}}", "--preselect", "{{USER_INPUT}}", "--lane", "{{working_lane}}"]
191
263
  outputs:
192
264
  - { name: change_options, from: "$.select_options" }
193
265
  - { name: matched_label, from: "$.matched_label" }
194
266
  - { name: has_changes, from: "$.has_changes" }
195
267
  - { name: change_count, from: "$.count" }
268
+ - { name: apd_hidden_csv, from: "$.hidden_ids_csv" }
269
+ - { name: apd_preselect_hidden, from: "$.preselect_hidden" }
270
+
271
+ - id: preselect_hidden_flag
272
+ condition: "{{apd_preselect_hidden}} != "
273
+ action: set
274
+ vars: { preselect_visible: "false" }
275
+ - id: require_visible_change
276
+ action: verify_context
277
+ require_true: preselect_visible
278
+ on_false: stop
279
+ on_false_message: "Change {{apd_preselect_hidden}} does not touch your working lane `{{working_lane}}` (.appweaver/lane.json): its **Lanes** line names only the other lane, so there is no delta plan of yours to gate. Its own lane's developer gates it — or run /appweaver select-lane to work on that lane."
280
+ - id: hidden_note_set
281
+ condition: "{{apd_hidden_csv}} != "
282
+ action: set
283
+ vars: { apd_hidden_note: " Open for a lane you do not work on, and not listed: {{apd_hidden_csv}}." }
196
284
 
197
285
  - id: require_change
198
286
  action: verify_context
199
287
  require_true: has_changes
200
288
  on_false: stop
201
- on_false_message: "No open change on this feature — there is no delta to gate. Run /appweaver specify <change> (which opens the change and writes its delta-spec + model delta), then /appweaver plan to author its delta plans, then re-run analyze-plan-delta. To re-anchor the MASTERS instead, run /appweaver analyze-plan."
289
+ on_false_message: "No open change on this feature names your working lane `{{working_lane}}` — there is no delta to gate.{{apd_hidden_note}} Run /appweaver specify <change> (which opens the change and writes its delta-spec + model delta), then /appweaver plan to author its delta plans, then re-run analyze-plan-delta. To re-anchor the MASTERS instead, run /appweaver analyze-plan."
202
290
 
203
291
  # 2b. Ask which one. A CR id in the argument auto-selects via matched_label; a feature
204
292
  # with exactly one open change auto-confirms.
@@ -246,6 +334,11 @@ workflow:
246
334
  - { name: predecessor_count, from: "$.predecessor_count" }
247
335
  - { name: sibling_count, from: "$.sibling_count" }
248
336
  - { name: domain_model, from: "$.domain_model" }
337
+ # The lanes this change touches — its delta-spec's **Lanes** line, every lane when it
338
+ # records none.
339
+ - { name: cr_lanes_csv, from: "$.lanes_csv" }
340
+ - { name: cr_touches_backend, from: "$.touches_backend" }
341
+ - { name: cr_touches_frontend, from: "$.touches_frontend" }
249
342
 
250
343
  # 3b. WHICH OTHER OPEN CHANGES TOUCH THE SAME AGGREGATES. Deterministic: each pending
251
344
  # model delta's `**Aggregates touched**:` header names the `## Aggregate:` sections
@@ -264,7 +357,8 @@ workflow:
264
357
  - { name: predecessor_overlaps, from: "$.predecessor_overlaps" }
265
358
 
266
359
  # 4. WHICH LANES ARE GATED. Declared AND a master plan exists (only then is there a base
267
- # for a delta) — then split on whether this change has that lane's delta plan.
360
+ # for a delta) AND named on the change's **Lanes** line — then split on whether this
361
+ # change has that lane's delta plan.
268
362
  # A lane whose MASTER was bootstrapped in the same `plan` run has no delta plan by
269
363
  # design: it was authored from the whole spec and there is nothing for a delta to
270
364
  # add. Keying on the master's presence is what keeps that case out of the gate.
@@ -276,6 +370,56 @@ workflow:
276
370
  condition: "{{frontend_plan_exists}} == true"
277
371
  action: set
278
372
  vars: { fe_gated: "{{fe_declared}}" }
373
+ # A lane the change's **Lanes** line leaves out has no part in it: no delta plan is
374
+ # written for it, so none is required here.
375
+ - id: be_not_in_change
376
+ condition: "{{cr_touches_backend}} == false"
377
+ action: set
378
+ vars: { be_gated: "false" }
379
+ - id: fe_not_in_change
380
+ condition: "{{cr_touches_frontend}} == false"
381
+ action: set
382
+ vars: { fe_gated: "false" }
383
+ # NAMED is kept before the working-lane knockdown: a named lane somebody else works on is
384
+ # read, not gated.
385
+ - id: be_named_set
386
+ action: set
387
+ vars: { be_named: "{{be_gated}}" }
388
+ - id: fe_named_set
389
+ action: set
390
+ vars: { fe_named: "{{fe_gated}}" }
391
+ - id: be_not_worked
392
+ condition: "{{be_working}} == false"
393
+ action: set
394
+ vars: { be_gated: "false" }
395
+ - id: fe_not_worked
396
+ condition: "{{fe_working}} == false"
397
+ action: set
398
+ vars: { fe_gated: "false" }
399
+ - id: be_flag_seen
400
+ condition: "{{delta_backend_plan_exists}} == true"
401
+ action: set
402
+ vars: { be_seen: "{{be_named}}" }
403
+ - id: fe_flag_seen
404
+ condition: "{{delta_frontend_plan_exists}} == true"
405
+ action: set
406
+ vars: { fe_seen: "{{fe_named}}" }
407
+ - id: be_flag_awaited
408
+ condition: "{{delta_backend_plan_exists}} == false"
409
+ action: set
410
+ vars: { be_awaited: "{{be_named}}" }
411
+ - id: be_awaited_mine
412
+ condition: "{{be_working}} == true"
413
+ action: set
414
+ vars: { be_awaited: "false" }
415
+ - id: fe_flag_awaited
416
+ condition: "{{delta_frontend_plan_exists}} == false"
417
+ action: set
418
+ vars: { fe_awaited: "{{fe_named}}" }
419
+ - id: fe_awaited_mine
420
+ condition: "{{fe_working}} == true"
421
+ action: set
422
+ vars: { fe_awaited: "false" }
279
423
  - id: be_flag_present
280
424
  condition: "{{delta_backend_plan_exists}} == true"
281
425
  action: set
@@ -304,11 +448,47 @@ workflow:
304
448
  action: set
305
449
  vars: { deltas_ok: "false" }
306
450
 
451
+ # The other developer's half, not written yet: said, never refused. Only a lane that is
452
+ # not worked can be awaited, so at most one is.
453
+ - id: awaited_be_txt
454
+ condition: "{{be_awaited}} == true"
455
+ action: set
456
+ vars: { awaited_txt: "backend", no_awaited: "false" }
457
+ - id: awaited_fe_txt
458
+ condition: "{{fe_awaited}} == true"
459
+ action: set
460
+ vars: { awaited_txt: "frontend", no_awaited: "false" }
461
+ - id: note_awaited
462
+ action: verify_context
463
+ require_true: no_awaited
464
+ on_false: continue
465
+ on_false_message: "NOTE — change {{cr_id}} also names the {{awaited_txt}} lane, whose developer has not written its delta plan yet. This run gates your half only; the contract between the two halves (check 3) is checked once both delta plans are on disk."
466
+
467
+ # D4b. What the service delta changes or removes that the frontend binds to, by script.
468
+ - id: contract_impact
469
+ condition: "{{delta_backend_plan_exists}} == true"
470
+ action: execute_script
471
+ on_error: continue
472
+ script: { ref: actions }
473
+ subcommand: contract-impact
474
+ args: ["--delta-backend", "{{delta_backend_plan}}", "--frontend-plan", "{{frontend_plan}}", "--delta-frontend", "{{delta_frontend_plan}}"]
475
+ outputs:
476
+ - { name: impact_csv, from: "$.unaddressed_csv" }
477
+ - id: impact_flag
478
+ condition: "{{impact_csv}} != "
479
+ action: set
480
+ vars: { no_impact: "false" }
481
+ - id: warn_contract_impact
482
+ action: verify_context
483
+ require_true: no_impact
484
+ on_false: continue
485
+ on_false_message: "WARNING — change {{cr_id}} changes or removes operation(s) the frontend plan binds to, and its frontend delta plan does not carry the matching change: {{impact_csv}}. The change names {{cr_lanes_csv}}. Until the frontend follows, it calls an operation the service is changing: the frontend half of this change carries it (if the change names only the backend, widen it with `.appweaver/scripts/bash/aw-spec-actions.sh cr-lanes --cr-id {{cr_id}} --set both`, then the frontend developer runs /appweaver plan {{cr_id}}), or a follow-up change does."
486
+
307
487
  - id: require_deltas
308
488
  action: verify_context
309
489
  require_true: deltas_ok
310
490
  on_false: stop
311
- on_false_message: "Change {{cr_id}} has no delta plan for a lane that has a master plan (backend missing={{be_missing}} · frontend missing={{fe_missing}}). Run /appweaver plan {{cr_id}} — it authors that change's delta plan for every lane whose master exists, against the delta spec, the effective model and the feature's real source files. This gate validates a delta plan; it does not write one."
491
+ on_false_message: "Change {{cr_id}} has no delta plan for a lane it touches ({{cr_lanes_csv}}) that has a master plan (backend missing={{be_missing}} · frontend missing={{fe_missing}}). Run /appweaver plan {{cr_id}} — it authors that change's delta plan for every lane the change names whose master exists, against the delta spec, the effective model and the feature's real source files. This gate validates a delta plan; it does not write one."
312
492
 
313
493
  - id: any_backend
314
494
  condition: "{{be_present}} == true"
@@ -325,15 +505,17 @@ workflow:
325
505
  on_false: stop
326
506
  on_false_message: "Change {{cr_id}} has no delta plan to gate on any declared lane. Run /appweaver plan {{cr_id}} first."
327
507
 
328
- # Check 3 needs BOTH delta plans. One more ordered assignment.
508
+ # Check 3 needs BOTH delta plans in context — gated or only read. One more ordered
509
+ # assignment.
329
510
  - id: flag_both_lanes
330
- condition: "{{be_present}} == true"
511
+ condition: "{{be_seen}} == true"
331
512
  action: set
332
- vars: { both_lanes: "{{fe_present}}" }
513
+ vars: { both_lanes: "{{fe_seen}}" }
333
514
 
334
515
  # Bind the CONTEXT paths per lane: the master (the base a MODIFIED element must name) for
335
- # a gated lane, the delta plan for a present one. A lane that is neither leaves both
336
- # empty, so its documents never reach the payload.
516
+ # a gated lane, the delta plan — and its master — for a lane whose delta plan is in
517
+ # context. A lane that is neither leaves both empty, so its documents never reach the
518
+ # payload.
337
519
  - id: be_master_ctx_path
338
520
  condition: "{{be_gated}} == true"
339
521
  action: set
@@ -343,13 +525,23 @@ workflow:
343
525
  action: set
344
526
  vars: { fe_master_ctx: "{{frontend_plan}}" }
345
527
  - id: be_delta_ctx_path
346
- condition: "{{be_present}} == true"
528
+ condition: "{{be_seen}} == true"
347
529
  action: set
348
530
  vars: { be_delta_ctx: "{{delta_backend_plan}}" }
349
531
  - id: fe_delta_ctx_path
350
- condition: "{{fe_present}} == true"
532
+ condition: "{{fe_seen}} == true"
351
533
  action: set
352
534
  vars: { fe_delta_ctx: "{{delta_frontend_plan}}" }
535
+ # A read lane's master too: check 3(d) holds the experience delta to what the master
536
+ # frontend plan uses, whichever developer runs it.
537
+ - id: be_master_ctx_seen
538
+ condition: "{{be_seen}} == true"
539
+ action: set
540
+ vars: { be_master_ctx: "{{backend_plan}}" }
541
+ - id: fe_master_ctx_seen
542
+ condition: "{{fe_seen}} == true"
543
+ action: set
544
+ vars: { fe_master_ctx: "{{frontend_plan}}" }
353
545
 
354
546
  # 4b. Load the domain GATE overlays (the domain-rule-bindings `/aw-spec-gate` checklist)
355
547
  # as the conformance checklist — VALIDATION input only, NEVER authoring. analyze sees
@@ -398,6 +590,17 @@ workflow:
398
590
  - { path: "{{fe_master_ctx}}", optional: true } # every MODIFIED element must name
399
591
  - { path: "{{domain_model}}", optional: true } # the canonical model (READ ONLY)
400
592
 
593
+ # The report "Report and Skip" writes. A developer working one lane of two writes their
594
+ # own, delta-plan-analyze-report.<lane>.md, so two developers gating the same change never
595
+ # rewrite each other's report; a developer on every lane writes delta-plan-analyze-report.md.
596
+ - id: report_file_set
597
+ action: set
598
+ vars: { report_file: "{{cr_dir}}/delta-plan-analyze-report.md" }
599
+ - id: report_file_lane
600
+ condition: "{{working_kind}} != "
601
+ action: set
602
+ vars: { report_file: "{{cr_dir}}/delta-plan-analyze-report.{{working_kind}}.md" }
603
+
401
604
  # 7. Conformance + REPAIR loop (the delta spec is the anchor): check → REPORT gaps +
402
605
  # ASK → Repair on approval → re-check; "Report and Skip" writes the report + stops.
403
606
  # Bounded (3×). The repair is PER LANE, so a frontend drift never rewrites a
@@ -416,8 +619,14 @@ workflow:
416
619
  delta-spec.md is the ANCHOR for change {{cr_id}} (wave {{wave}}). Gate this
417
620
  change's DELTA PLANS against it, against the masters they change, and against the
418
621
  effective domain model, and list EVERY drift as a gap. Run the checks that apply
419
- — service delta present: {{be_present}}, experience delta present: {{fe_present}},
420
- both present: {{both_lanes}}. A check for an absent lane is SKIPPED, not failed.
622
+ — service delta in context: {{be_seen}}, experience delta in context: {{fe_seen}},
623
+ both: {{both_lanes}}. A check for an absent lane is SKIPPED, not failed.
624
+ WHICH DELTA PLANS ARE GATED: service {{be_present}}, experience {{fe_present}} —
625
+ the lanes the developer running this works on. Run checks 1, 2, 4 and 5 for a
626
+ GATED delta plan only. A delta plan in context and NOT gated belongs to another
627
+ developer: read it for check 3, where the two halves of the contract meet, and
628
+ nothing else. Every gap still carries the `lane` whose document must change to
629
+ close it, even when that lane is not gated here.
421
630
 
422
631
  CHECK 1 — delta-spec.md → delta-backend-plan.md (when it is in context):
423
632
  (a) every ADDED / MODIFIED delta-spec item — FR / BR / CN- / DC- / IC- / user
@@ -533,6 +742,41 @@ workflow:
533
742
  - id: conform_break
534
743
  action: break
535
744
  condition: "{{analyze_ok}} == true"
745
+
746
+ # WHO CAN CLOSE EACH GAP. A gap tagged with a lane this developer does not work on is
747
+ # the other developer's: it is listed under `other` and, when nothing else is left,
748
+ # ends the loop without a repair. A LIFT-FAILURE is shared whatever its lane, so it
749
+ # keeps the gate red. With every lane worked nothing is `other` and the loop runs as
750
+ # it always has.
751
+ - id: classify_gaps
752
+ action: execute_script
753
+ script: { ref: actions }
754
+ subcommand: plan-gaps
755
+ args: ["--gaps", "{{analyze_gaps}}", "--lanes", "{{work_lanes}}"]
756
+ outputs:
757
+ - { name: own_plan_gaps, from: "$.plan_gaps" }
758
+ - { name: own_routed, from: "$.routed_gaps" }
759
+ - { name: other_gaps, from: "$.other_gaps" }
760
+ - { name: other_csv, from: "$.other_csv" }
761
+ - id: own_clear_reset
762
+ action: set
763
+ vars: { own_clear: "false" }
764
+ - id: own_clear_raise
765
+ condition: "{{other_gaps}} != 0"
766
+ action: set
767
+ vars: { own_clear: "true" }
768
+ - id: own_clear_plan
769
+ condition: "{{own_plan_gaps}} != 0"
770
+ action: set
771
+ vars: { own_clear: "false" }
772
+ - id: own_clear_routed
773
+ condition: "{{own_routed}} != 0"
774
+ action: set
775
+ vars: { own_clear: "false" }
776
+ - id: own_clear_break
777
+ action: break
778
+ condition: "{{own_clear}} == true"
779
+
536
780
  - id: ask_repair
537
781
  action: ask
538
782
  prompt: |
@@ -546,7 +790,7 @@ workflow:
546
790
  condition: "{{repair_choice}} == Report and Skip"
547
791
  action: agent_author
548
792
  include_standards: false
549
- output_target: { type: file, path: "{{cr_dir}}/delta-plan-analyze-report.md" }
793
+ output_target: { type: file, path: "{{report_file}}" }
550
794
  task: |
551
795
  Write a Delta Plan Analyze Report to the output from these delta-spec↔delta-plan
552
796
  conformance gaps (the delta spec is the ANCHOR; the user chose to skip repair):
@@ -665,11 +909,27 @@ workflow:
665
909
  headers — scripts stamp those.
666
910
  Return the COMPLETE updated delta plan content only.
667
911
 
912
+ # THE STOP. Two independent raises: nothing drifts, or what drifts is only the other
913
+ # developer's to close — reported for them below.
914
+ - id: gate_ok_conformant
915
+ condition: "{{analyze_ok}} == true"
916
+ action: set
917
+ vars: { gate_ok: "true" }
918
+ - id: gate_ok_others_only
919
+ condition: "{{own_clear}} == true"
920
+ action: set
921
+ vars: { gate_ok: "true", no_other_gaps: "false" }
922
+ - id: note_other_gaps
923
+ action: verify_context
924
+ require_true: no_other_gaps
925
+ on_false: continue
926
+ on_false_message: "NOTE — your half of change {{cr_id}} passes. What is left is in the delta plan of a lane you do not work on, for its developer to close (their own analyze-plan-delta {{cr_id}} repairs it): {{other_csv}}"
927
+
668
928
  - id: analyze_gate
669
929
  action: verify_context
670
- require_true: analyze_ok
930
+ require_true: gate_ok
671
931
  on_false: stop
672
- on_false_message: "The delta plans for {{cr_id}} do not realise the delta spec (gaps: {{analyze_gaps}}) — repair was skipped (see {{cr_dir}}/delta-plan-analyze-report.md), or it could not be reconciled in 3 passes. A 'LIFT-FAILURE:' gap means a domain constraint from the gate checklist was never lifted into the delta spec, and a `model` gap means a plan needs structure the effective model does not define — both are fixed by /appweaver specify, which authors the delta spec and the model delta; this gate may not write either. A `wave` gap is closed by declaring '## Depends on' — which is what sets the wave, since specify derives it from that section and the merge fence re-checks it — or by planning around the sibling. Otherwise fix out of band, or re-run and choose Repair."
932
+ on_false_message: "The delta plans for {{cr_id}} do not realise the delta spec (gaps: {{analyze_gaps}}) — repair was skipped (see {{report_file}}), or it could not be reconciled in 3 passes. A 'LIFT-FAILURE:' gap means a domain constraint from the gate checklist was never lifted into the delta spec, and a `model` gap means a plan needs structure the effective model does not define — both are fixed by /appweaver specify, which authors the delta spec and the model delta; this gate may not write either. A `wave` gap is closed by declaring '## Depends on' — which is what sets the wave, since specify derives it from that section and the merge fence re-checks it — or by planning around the sibling. Otherwise fix out of band, or re-run and choose Repair."
673
933
 
674
934
  # 8. Post-execution hooks.
675
935
  - id: resolve_post_hooks
@@ -690,8 +950,13 @@ outputs:
690
950
  wave: "{{wave}}"
691
951
  delta_backend_plan: "{{delta_backend_plan}}" # empty when that lane has no delta plan
692
952
  delta_frontend_plan: "{{delta_frontend_plan}}"
693
- conformant: "{{analyze_ok}}"
953
+ conformant: "{{analyze_ok}}" # every check passed, the other developer's half included; false on a run that finishes means only other_lane_gaps are left
694
954
  gaps: "{{analyze_gaps}}"
695
955
  overlaps: "{{overlap_report}}"
956
+ working_lane: "{{working_lane}}" # the developer's working lane(s) — .appweaver/lane.json
957
+ gated_backend: "{{be_present}}" # checked and repaired by this run
958
+ gated_frontend: "{{fe_present}}"
959
+ other_lane_gaps: "{{other_csv}}" # gaps only the other developer's delta plan can close
960
+ contract_impact: "{{impact_csv}}" # operations the service delta changes or removes that the frontend still binds to
696
961
  note: "Gated change {{cr_id}} (wave {{wave}}) against its delta spec, the masters it changes, and the EFFECTIVE model — domain-model.md plus this change's model delta plus the {{predecessor_count}} predecessor change(s) from LOWER waves (each one's model delta and its delta-spec). Same-wave changes are visible but never assumed: {{sibling_count}} open at the same or a higher wave, overlapping this one on — {{overlap_report}}. The masters and the model were READ, never written."
697
962
  next: "/appweaver implement {{cr_id}} → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta (implement is one command for both lanes and both paths — passing the id above skips its 'which change' prompt; the delta plans realise the delta spec; the change stays OPEN through finalize-delta's merge and analyze-slice's verification, and only close-delta archives it and bumps the version)"