aw-spec-cli 0.1.33 → 0.1.34

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 (75) hide show
  1. package/README.md +76 -81
  2. package/assets/.appweaver/DESIGN-FORMAT.md +57 -25
  3. package/assets/.appweaver/STACK-PROFILES.md +95 -60
  4. package/assets/.appweaver/orchestrator/LAUNCHER.md +1 -1
  5. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +173 -32
  6. package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +29 -1
  7. package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +7 -4
  8. package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +49 -60
  9. package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +58 -0
  10. package/assets/.appweaver/orchestrator/orchestrator.py +26 -8
  11. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +20 -20
  12. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +22 -48
  13. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +9 -7
  14. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +5 -5
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +239 -40
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +254 -43
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +248 -68
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +251 -20
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +316 -10
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +6 -47
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +62 -16
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +132 -9
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-fix-test-coverage.orch.yaml +965 -0
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +222 -169
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +21 -22
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +453 -120
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +2 -2
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +83 -6
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +45 -2
  30. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +324 -74
  31. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +275 -44
  32. package/assets/.appweaver/scripts/python/aw-foundation.py +16 -13
  33. package/assets/.appweaver/scripts/python/aw-gaps.py +11 -3
  34. package/assets/.appweaver/scripts/python/aw-model-guard.py +295 -0
  35. package/assets/.appweaver/scripts/python/aw-plan-coverage.py +412 -3
  36. package/assets/.appweaver/scripts/python/aw-plangate.py +23 -16
  37. package/assets/.appweaver/scripts/python/aw-stack.py +1794 -265
  38. package/assets/.appweaver/stack-profiles/dotnet-webapi.json +0 -2
  39. package/assets/.appweaver/stack-profiles/java-spring.json +0 -2
  40. package/assets/.appweaver/templates/api-plan-template.md +107 -0
  41. package/assets/.appweaver/templates/delta-api-plan-template.md +87 -0
  42. package/assets/.appweaver/templates/delta-design-template.md +25 -23
  43. package/assets/.appweaver/templates/delta-spec-template.md +3 -3
  44. package/assets/.appweaver/templates/design-template.md +20 -42
  45. package/assets/.appweaver/templates/domain-model-template.md +3 -0
  46. package/assets/.appweaver/templates/ui-delta-design-template.md +11 -8
  47. package/assets/.appweaver/templates/ui-design-template.md +4 -4
  48. package/assets/foundations/foundation.spec.template.txt +25 -18
  49. package/assets/guardrails/README.md +61 -25
  50. package/assets/guardrails/methodology/ux/accessibility-standards.md +38 -0
  51. package/assets/guardrails/methodology/ux/content-design.md +34 -0
  52. package/assets/guardrails/methodology/ux/interaction-states.md +45 -0
  53. package/assets/guardrails/methodology/ux/journey-mapping.md +41 -0
  54. package/assets/guardrails/project-specific/domain/domain-requirements.md +25 -38
  55. package/assets/guardrails/project-specific/domain-rule-bindings.md +33 -69
  56. package/assets/guardrails/project-specific/frontend/ui-requirements.md +2 -2
  57. package/assets/guardrails/project-specific/service/technical-requirements.md +2 -2
  58. package/assets/guardrails/project-specific/tech-rule-bindings.md +16 -13
  59. package/assets/guardrails/sample-healthcare/domain/domain-requirements.md +58 -0
  60. package/assets/guardrails/sample-healthcare/domain-rule-bindings.md +138 -0
  61. package/assets/guardrails/sample-healthcare/frontend/ui-requirements.md +43 -0
  62. package/assets/guardrails/sample-healthcare/service/technical-requirements.md +40 -0
  63. package/assets/guardrails/sample-healthcare/tech-rule-bindings.md +303 -0
  64. package/dist/commands/init.d.ts.map +1 -1
  65. package/dist/commands/init.js +216 -7
  66. package/dist/commands/init.js.map +1 -1
  67. package/dist/utils/config.js +1 -1
  68. package/dist/utils/config.js.map +1 -1
  69. package/dist/utils/foundation.d.ts +36 -0
  70. package/dist/utils/foundation.d.ts.map +1 -0
  71. package/dist/utils/foundation.js +109 -0
  72. package/dist/utils/foundation.js.map +1 -0
  73. package/dist/utils/paths.js +1 -1
  74. package/dist/utils/paths.js.map +1 -1
  75. package/package.json +1 -1
@@ -3,7 +3,8 @@
3
3
  #
4
4
  # GENERATE the source files + paired tests for the aggregate, STRICTLY from its lane
5
5
  # plans — the mediators between the spec and the code:
6
- # • specs/<Context>/<Feature>/backend-plan.md when a BACKEND lane is declared
6
+ # • specs/<Context>/<Feature>/backend-plan.md when a BACKEND lane is declared, with the
7
+ # api-plan.md beside it — the endpoints that plan realises
7
8
  # • specs/<Context>/<Feature>/frontend-plan.md when a FRONTEND lane is declared
8
9
  #
9
10
  # THE LANES COME FROM stacks.json, NOT FROM A FLAG — select-stack is the sole writer of
@@ -54,15 +55,18 @@
54
55
  # pipeline, so each runnable lane confirms first, naming its stack, its plan and the
55
56
  # subtree it will write into. Answering no to one lane leaves the other free to run.
56
57
  #
57
- # THE BACKEND PUBLISHES THE API DOCUMENT, THE FRONTEND READS IT. When the service lane
58
- # generates, its API document is exported from the running code and saved beside it, to be
59
- # committed. When the frontend lane generates, it never starts the service: it reads that
60
- # committed document plus the operations the backend plan has drafted and the service does
61
- # not serve yet (api-draft.json), combined into one, generates its client from it and
62
- # checks the operation ids its plan cites against it. Contract requests the backend plan has
63
- # not answered are named, and the bindings that wait on them are left unbound while the
64
- # rest of the lane generates. Every one of those steps warns rather than stops — see the
65
- # API CONTRACT block before the lane swap.
58
+ # THE API PLAN IS THE CONTRACT, AND BOTH LANES BUILD TO IT. The service lane serves every
59
+ # endpoint of the feature's api-plan.md — a change's delta-api-plan.md on the delta path —
60
+ # at the route, verb and operation id it states; analyze-slice checks the built service
61
+ # serves it, and this phase never starts the service. A feature or change planned before
62
+ # API plans existed keeps its endpoints in its backend plan, which is read in their place.
63
+ # When the frontend lane generates, it reads the operations the API plans draft
64
+ # (api-draft.json, with each open change's draft applied), combined into one document,
65
+ # generates its client from it and checks the operation ids its plan cites against it; the
66
+ # feature's api-plan.md is in its context too, and the backend plan never is. Contract
67
+ # requests the API plan has not answered are named, and the bindings that wait on them are
68
+ # left unbound while the rest of the lane generates. Every one of those steps warns rather
69
+ # than stops — see the API CONTRACT block before the lane swap.
66
70
  #
67
71
  # THE PLAN SAYS WHAT TO BUILD, THE MODEL SAYS WHAT THE DOMAIN IS. This phase does NOT read
68
72
  # spec.md — analyze-plan already conformed the plans to it, so the plan is the complete,
@@ -154,8 +158,8 @@
154
158
  # =============================================================================
155
159
 
156
160
  name: aw-spec-implement
157
- description: "Generate the aggregate's source files and paired tests from its lane plans — the backend lane from backend-plan.md when a backend stack is declared, the frontend lane from frontend-plan.md when a frontend stack is, both when both are. One command covers backend-only, frontend-only and full-stack projects and absorbs the former implement-ui and implement-delta. Self-detecting off the changes on disk — with none open that names a lane you work on (its **Lanes** line) it generates from the master plans; with one or more it ASKS which change and generates only what that change's delta plans mark ADDED or MODIFIED, for the lanes the change names, leaving the masters untouched. Every lane is gated on its own lane root folder, its own plan and its own scaffolded project, and a lane missing any of them is reported and skipped rather than failing the other lane; each runnable lane confirms before it writes. Model-driven, and it does not read the spec — analyze-plan already conformed the plans to it. The plan says WHAT to build; the bounded context's domain-model.md, in context alongside it, says what the domain IS — the entity names, the boundary and the text of every invariant the plan cites by id — joined on the delta path by that change's own model delta, since the master is not merged until finalize-delta. The stack binds here from the lane declaration and the constitution halves; the plans stay technology-agnostic."
158
- version: 3.13.0
161
+ description: "Generate the aggregate's source files and paired tests from its lane plans — the backend lane from backend-plan.md, serving the endpoints of the api-plan.md beside it, when a backend stack is declared, the frontend lane from frontend-plan.md when a frontend stack is, both when both are. One command covers backend-only, frontend-only and full-stack projects and absorbs the former implement-ui and implement-delta. Self-detecting off the changes on disk — with none open that names a lane you work on (its **Lanes** line) it generates from the master plans; with one or more it ASKS which change and generates only what that change's delta plans mark ADDED or MODIFIED, for the lanes the change names, leaving the masters untouched. Every lane is gated on its own lane root folder, its own plan and its own scaffolded project, and a lane missing any of them is reported and skipped rather than failing the other lane; each runnable lane confirms before it writes. Model-driven, and it does not read the spec — analyze-plan already conformed the plans to it. The plan says WHAT to build; the bounded context's domain-model.md, in context alongside it, says what the domain IS — the entity names, the boundary and the text of every invariant the plan cites by id — joined on the delta path by that change's own model delta, since the master is not merged until finalize-delta. The stack binds here from the lane declaration and the constitution halves; the plans stay technology-agnostic."
162
+ version: 3.16.0
159
163
  agent_agnostic: true
160
164
  dialect: aw-spec.orchestrator/v1
161
165
 
@@ -200,7 +204,17 @@ vars:
200
204
  delta_backend_plan_exists: "false"
201
205
  delta_frontend_plan: ""
202
206
  delta_frontend_plan_exists: "false"
207
+ delta_api_plan: ""
208
+ delta_api_plan_exists: "false"
203
209
  wave: ""
210
+ # THE FEATURE'S API PLAN, and the one the backend lane serves this run (be_api_doc) —
211
+ # api-plan.md on the bootstrap path, the change's delta-api-plan.md on the delta path;
212
+ # be_api_doc is empty where there is none, because the (delta) backend plan still carries
213
+ # its own API.
214
+ api_plan: ""
215
+ api_plan_exists: "false"
216
+ be_api_doc: ""
217
+ be_api_ok: "false"
204
218
  # THE GATE RECEIPT — was this feature's plan pair checked against this spec, with what
205
219
  # verdict, and has anything moved since. analyze-plan writes it on every master run; this
206
220
  # phase is the reader. Bootstrap path only: a delta generates from delta plans, which
@@ -304,18 +318,8 @@ vars:
304
318
  fe_gap_names: ""
305
319
  fe_gaps_text: "none"
306
320
  # THE API CONTRACT between the lanes — see the block before the lane swap. Every flag
307
- # starts lowered: the backend half publishes only when the service lane generates, and
308
- # the frontend half reads only when the frontend lane does.
309
- contract_go: "false" # PUBLISH — the service lane generated in this run
310
- contract_ok: ""
311
- export_doc: "" # the service's exported document (contracts/openapi.json)
312
- export_doc_exists: "false"
313
- contract_reason: ""
314
- contract_already: "false"
315
- contract_fresh: "true"
316
- contract_log: ""
321
+ # starts lowered: the frontend half reads only when the frontend lane generates.
317
322
  contract_placeholder: ""
318
- contract_export_status: ""
319
323
  combine_go: "false" # READ — the frontend lane generates and a service lane is declared
320
324
  combined_ok: ""
321
325
  combined_status: ""
@@ -325,13 +329,15 @@ vars:
325
329
  clashes_clear: "true"
326
330
  contract_doc: "" # what the frontend binds to: the combined document
327
331
  contract_doc_exists: "false"
328
- contract_status: "none — no API document is in context, so take each binding from the plan alone and say so in your summary"
329
- req_open_csv: "" # the frontend plan's contract requests the backend plan has not answered
332
+ contract_status: "none — no API document is in context, so take each binding from the feature's API plan where it is in context, and from the plan alone otherwise, and say so in your summary"
333
+ req_open_csv: "" # the frontend plan's contract requests the API plan has not answered
330
334
  req_backend_text: "none"
331
335
  req_summary: ""
332
336
  req_clear: "true"
333
- check_drafted: ""
334
- drafted_clear: "true"
337
+ check_unserved: ""
338
+ unserved_clear: "true"
339
+ check_removed: ""
340
+ removed_clear: "true"
335
341
  client_go: "false"
336
342
  client_ask: "false"
337
343
  client_answer: ""
@@ -441,6 +447,8 @@ workflow:
441
447
  - { name: backend_plan_exists, from: "$.backend_plan_exists" }
442
448
  - { name: frontend_plan, from: "$.frontend_plan" }
443
449
  - { name: frontend_plan_exists, from: "$.frontend_plan_exists" }
450
+ - { name: api_plan, from: "$.api_plan" }
451
+ - { name: api_plan_exists, from: "$.api_plan_exists" }
444
452
 
445
453
  - id: require_feature
446
454
  action: verify_context
@@ -574,6 +582,10 @@ workflow:
574
582
  - { name: delta_backend_plan_exists, from: "$.delta_backend_plan_exists" }
575
583
  - { name: delta_frontend_plan, from: "$.delta_frontend_plan" }
576
584
  - { name: delta_frontend_plan_exists, from: "$.delta_frontend_plan_exists" }
585
+ # THIS CHANGE'S API — the endpoints it adds, changes or removes. Absent for a change
586
+ # planned before delta API plans existed, whose delta backend plan carries them.
587
+ - { name: delta_api_plan, from: "$.delta_api_plan" }
588
+ - { name: delta_api_plan_exists, from: "$.delta_api_plan_exists" }
577
589
  - { name: wave, from: "$.wave" }
578
590
  # The lanes the change names — its delta-spec's **Lanes** line, every lane when it
579
591
  # records none.
@@ -584,14 +596,28 @@ workflow:
584
596
  # 3. WHICH PLAN EACH LANE GENERATES FROM, resolved once per lane from the mode — the
585
597
  # master on the bootstrap path, this change's delta plan on the delta path. Every gate
586
598
  # and prompt below reads be_plan / fe_plan, so neither has to re-branch on the mode.
599
+ # The backend lane's API plan resolves beside its plan, and is emptied where it is not
600
+ # on disk: that plan still carries its own API section.
587
601
  - id: be_plan_bootstrap
588
602
  condition: "{{mode}} == bootstrap"
589
603
  action: set
590
- vars: { be_plan: "{{backend_plan}}", be_plan_ok: "{{backend_plan_exists}}" }
604
+ vars:
605
+ be_plan: "{{backend_plan}}"
606
+ be_plan_ok: "{{backend_plan_exists}}"
607
+ be_api_doc: "{{api_plan}}"
608
+ be_api_ok: "{{api_plan_exists}}"
591
609
  - id: be_plan_delta
592
610
  condition: "{{mode}} == delta"
593
611
  action: set
594
- vars: { be_plan: "{{delta_backend_plan}}", be_plan_ok: "{{delta_backend_plan_exists}}" }
612
+ vars:
613
+ be_plan: "{{delta_backend_plan}}"
614
+ be_plan_ok: "{{delta_backend_plan_exists}}"
615
+ be_api_doc: "{{delta_api_plan}}"
616
+ be_api_ok: "{{delta_api_plan_exists}}"
617
+ - id: be_api_absent
618
+ condition: "{{be_api_ok}} != true"
619
+ action: set
620
+ vars: { be_api_doc: "" }
595
621
  - id: fe_plan_bootstrap
596
622
  condition: "{{mode}} == bootstrap"
597
623
  action: set
@@ -906,15 +932,17 @@ workflow:
906
932
  action: set
907
933
  vars: { fe_mode: "{{mode}}" }
908
934
 
909
- # 8b. EACH GENERATING LANE'S ELEMENT LIST, read off its plan by plan-coverage. The
910
- # generation prompt carries it, and the model reports every name on it — so an
911
- # element it did not get to is declared rather than silently absent.
935
+ # 8b. EACH GENERATING LANE'S ELEMENT LIST, read off its plan by plan-coverage — and, for
936
+ # the backend lane, the endpoints of the API plan it serves (`--api-plan`; an empty
937
+ # path reads the endpoints from the plan's own API section). The generation prompt
938
+ # carries it, and the model reports every name on it — so an element it did not get to
939
+ # is declared rather than silently absent.
912
940
  - id: be_plan_elements
913
941
  condition: "{{be_go}} == true"
914
942
  action: execute_script
915
943
  script: { ref: actions }
916
944
  subcommand: plan-coverage
917
- args: ["--plan", "{{be_plan}}", "--kind", "backend", "--mode", "{{be_mode}}", "--list-only", "--reserved", "{{be_reserved}}"]
945
+ args: ["--plan", "{{be_plan}}", "--kind", "backend", "--mode", "{{be_mode}}", "--api-plan", "{{be_api_doc}}", "--list-only", "--reserved", "{{be_reserved}}"]
918
946
  outputs:
919
947
  - { name: be_element_names, from: "$.names_text" }
920
948
  - { name: be_collisions, from: "$.collisions_csv" }
@@ -1011,6 +1039,7 @@ workflow:
1011
1039
  action: build_context
1012
1040
  context:
1013
1041
  - { path: "{{be_plan}}" }
1042
+ - { path: "{{api_plan}}", optional: true } # the endpoints the plan realises — absent where the plan still carries its own API section
1014
1043
  - { path: "{{domain_model}}", optional: true } # the CONTEXT'S canonical model — what each cited BR-nnn REQUIRES
1015
1044
  - { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain rules
1016
1045
  - id: gen_backend
@@ -1020,10 +1049,11 @@ workflow:
1020
1049
  permission_mode: acceptEdits
1021
1050
  task: |
1022
1051
  Generate the production code AND paired tests for this aggregate STRICTLY from its
1023
- backend-plan.md (in context). The plan is the complete, authoritative source of WHAT
1024
- to build — do NOT consult or infer from the spec; analyze-plan already conformed the
1025
- plan to it. This phase is the HOME of the technology-specific, solution-wide
1026
- code-generation rules. Guidance: {{USER_INPUT}}
1052
+ backend-plan.md (in context), serving the endpoints of the api-plan.md beside it (in
1053
+ context where the feature has one). The plans are the complete, authoritative source
1054
+ of WHAT to build — do NOT consult or infer from the spec; analyze-plan already
1055
+ conformed them to it. This phase is the HOME of the technology-specific,
1056
+ solution-wide code-generation rules. Guidance: {{USER_INPUT}}
1027
1057
  Rules:
1028
1058
  - TWO DOMAIN INPUTS, WITH DIFFERENT JOBS. The PLAN is the only authority on WHAT TO
1029
1059
  BUILD — which elements exist, where each one lives, what surface they publish. The
@@ -1102,21 +1132,30 @@ workflow:
1102
1132
  - Realise EVERY plan element as code, changing nothing about WHAT it specifies: one
1103
1133
  file per Domain type / Application use-case / API endpoint / Infrastructure
1104
1134
  adapter, placed by layer. Preserve the ubiquitous-language names verbatim.
1105
- - Enforce EXACTLY as the plan states: every Domain invariant, Application handler
1106
- orchestration and API error contract; the cross-cutting constraints in the
1135
+ - Enforce EXACTLY as the plans state: every Domain invariant, Application handler
1136
+ orchestration and endpoint error contract; the cross-cutting constraints in the
1107
1137
  Cross-Cutting Concerns table (audit / retention / data-exposure — the CN-/DC-
1108
1138
  realisations); and each integration contract (IC-) in the External Integrations
1109
1139
  table. Implement every port the plan declares.
1110
- - GIVE EVERY ENDPOINT ITS OPERATION ID. Where the plan names an endpoint's
1111
- **Operation id**, set it as this stack's published API document names operations
1112
- (its OpenAPI operationId), spelled exactly as the plan writes it. The frontend lane
1113
- finds the endpoint by that id in the document exported from this code, so an
1114
- endpoint left on a framework-generated name is one it cannot find.
1140
+ - SERVE EVERY ENDPOINT OF THE API PLAN EXACTLY AS IT IS WRITTEN. api-plan.md is the
1141
+ API contract: each endpoint block gives the route, verb, **Operation id**, request
1142
+ and response field shapes and the error contract, and **Maps to** names the
1143
+ backend plan's use-case that serves it. Where api-plan.md is not in context, the
1144
+ backend plan's own API section carries the same blocks. Serve each endpoint at the
1145
+ route and verb it gives, with its field names and status codes as stated, and set
1146
+ its operation id as this stack's published API document names operations (its
1147
+ OpenAPI operationId), spelled exactly as the block writes it. The frontend lane
1148
+ binds to the endpoint by that id, and analyze-slice checks that the built service
1149
+ serves it there, so an endpoint left on a framework-generated name is one the
1150
+ frontend calls and cannot reach.
1115
1151
  - Author a paired test for every plan behaviour / use-case / endpoint; derive the
1116
1152
  cases from the plan's pre/post/invariant contracts and cite the spec id (FR / BR /
1117
1153
  CN / DC / IC / US) from its Traceability section. Include tests for the constraints
1118
1154
  — e.g. an audit record is emitted, a DC-restricted field is NOT exposed on the
1119
- public surface, an IC- projection round-trips.
1155
+ public surface, an IC- projection round-trips. THE CITATION IS CHECKED: analyze-slice
1156
+ fails a slice in which some FR- or BR- of that section is named by no test. Write the
1157
+ id in the test's name, a comment, an attribute or a tag, in whichever form this
1158
+ language allows — `FR-004`, `FR_004` or `FR004`.
1120
1159
  - WIRE WHAT YOU BUILD, in this same pass. Every port, adapter, handler and endpoint
1121
1160
  the plan introduces is registered in the lane's composition root as the plan's §4.3
1122
1161
  Wiring states. A dependency that is injected and never bound COMPILES GREEN — a
@@ -1136,21 +1175,22 @@ workflow:
1136
1175
  one changed, STOP and surface it.
1137
1176
  - Where generated code depends on a plan Assumption (DA-n) or Workaround (WA-n), mark
1138
1177
  it with a comment citing that id so it is easy to revisit later.
1139
- - REPORT EVERY PLAN ELEMENT. The plan's elements, by name: {{be_element_names}}.
1140
- Add an `elements` list to your closing JSON block with one entry per name, spelled
1141
- exactly as listed: {"element": "<name>", "status": "done" | "blocked", "reason":
1142
- "<what stops it, when blocked>"}. `done` means it is realised in code now,
1143
- including one that already existed; `blocked` means you could not realise it. An
1144
- element missing from the list counts as not done, and the code is checked for
1145
- each name after this step.
1178
+ - REPORT EVERY PLAN ELEMENT. The elements of the plan and its API plan, by name:
1179
+ {{be_element_names}}. Add an `elements` list to your closing JSON block with one
1180
+ entry per name, spelled exactly as listed: {"element": "<name>", "status": "done"
1181
+ | "blocked", "reason": "<what stops it, when blocked>"}. `done` means it is
1182
+ realised in code now, including one that already existed; `blocked` means you
1183
+ could not realise it. An element missing from the list counts as not done, and the
1184
+ code is checked for each name after this step.
1146
1185
  outputs:
1147
1186
  - { name: be_files, from: "$.files" }
1148
1187
  - { name: be_elements, from: "$.elements" }
1149
1188
 
1150
1189
  # ═══════════ BACKEND LANE — delta, generate ONLY what the change adds ════════
1151
1190
  # The delta plan is the authoritative source of WHAT changed — analyze-plan-delta already
1152
- # conformed it to the delta spec. The MASTER plan is the BASE (the unchanged elements the
1153
- # delta depends on).
1191
+ # conformed it to the delta spec — and the change's delta-api-plan.md of the endpoints it
1192
+ # adds, changes or removes. The MASTER plan and API plan are the BASE (the unchanged
1193
+ # elements and endpoints the delta depends on).
1154
1194
  #
1155
1195
  # NO DELTA-SPEC HERE, exactly as the bootstrap path carries no spec. It used to be
1156
1196
  # inlined "only so the paired tests can cite traceability ids", but a document in the
@@ -1166,8 +1206,10 @@ workflow:
1166
1206
  action: build_context
1167
1207
  context:
1168
1208
  - { path: "{{be_plan}}" } # this change's delta-backend-plan.md
1209
+ - { path: "{{delta_api_plan}}", optional: true } # this change's API — absent for a change whose delta plan carries it
1169
1210
  - { path: "{{delta_model}}", optional: true } # this change's MODEL half — what it ADDS to / CHANGES in the model
1170
1211
  - { path: "{{backend_plan}}", optional: true } # the master plan — READ ONLY, never amended here
1212
+ - { path: "{{api_plan}}", optional: true } # the master API plan — READ ONLY, the endpoints as they stand BEFORE this change
1171
1213
  - { path: "{{domain_model}}", optional: true } # the master model — the domain as it stands BEFORE this change
1172
1214
  - { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain rules
1173
1215
  - id: gen_backend_delta
@@ -1177,14 +1219,16 @@ workflow:
1177
1219
  permission_mode: acceptEdits
1178
1220
  task: |
1179
1221
  Implement ONLY change {{cr_id}} (wave {{wave}}) into the existing project tree,
1180
- STRICTLY from its delta-backend-plan.md (in context). The delta plan is the
1181
- authoritative source of WHAT changed — analyze-plan-delta already conformed it to the
1182
- delta spec. The master backend-plan.md (in context) is the BASE: the unchanged
1183
- elements the delta depends on; do NOT regenerate them. Guidance: {{USER_INPUT}}
1222
+ STRICTLY from its delta-backend-plan.md and its delta-api-plan.md (in context where
1223
+ the change has one). The delta plans are the authoritative source of WHAT changed —
1224
+ analyze-plan-delta already conformed them to the delta spec. The master
1225
+ backend-plan.md and api-plan.md (in context) are the BASE: the unchanged elements and
1226
+ endpoints the delta depends on; do NOT regenerate them. Guidance: {{USER_INPUT}}
1184
1227
  Rules:
1185
1228
  - SCOPE = THE DELTA ONLY. Realise exactly the elements the delta plan marks ADDED
1186
- (create the new file) or MODIFIED (edit the existing file in place). Touch NOTHING
1187
- the delta does not list, except the regression re-tests below.
1229
+ (create the new file) or MODIFIED (edit the existing file in place), and the
1230
+ endpoints its API delta adds, changes or removes. Touch NOTHING the delta does not
1231
+ list, except the regression re-tests below.
1188
1232
  - TWO DOMAIN INPUTS, WITH DIFFERENT JOBS. The DELTA PLAN is the only authority on
1189
1233
  WHAT TO BUILD in this change — which elements are added or edited, where each one
1190
1234
  lives, what surface they publish. The DOMAIN MODEL is the authority on WHAT THE
@@ -1262,10 +1306,16 @@ workflow:
1262
1306
  invariant, handler orchestration, API error contract, the Cross-Cutting Concerns
1263
1307
  realisations (audit / retention / data-exposure — CN-/DC-) and the External
1264
1308
  Integrations table's contracts (IC-) exactly as the delta plan states.
1265
- - GIVE EVERY ENDPOINT THIS CHANGE ADDS OR CHANGES the operation id the delta plan
1266
- names, set as this stack's published API document names operations (its OpenAPI
1267
- operationId) and spelled exactly; an existing endpoint keeps the id it has. The
1268
- frontend lane finds each endpoint by that id in the exported document.
1309
+ - SERVE THE CHANGE'S API EXACTLY AS IT IS WRITTEN. delta-api-plan.md lists every
1310
+ endpoint the change adds, changes or removes in its Contract deltas table, and
1311
+ writes each added or changed endpoint whole below it; a change planned before delta
1312
+ API plans existed carries the same in its delta backend plan. Serve each added or
1313
+ changed endpoint at the route and verb its block gives, with its field names,
1314
+ status codes and errors as stated, and set the operation id it names as this
1315
+ stack's published API document names operations (its OpenAPI operationId), spelled
1316
+ exactly; an existing endpoint keeps the id it has, and one the change removes is no
1317
+ longer served. The frontend lane binds to each endpoint by that id, and
1318
+ analyze-slice-delta checks the built service against the change's API draft.
1269
1319
  - COMPONENTS: if a delta element depends on a framework component, code against its
1270
1320
  ABSTRACTION / port as the delta plan declares — do NOT hand-roll it. Installing the
1271
1321
  component (packages, wiring, config) is /appweaver add-component <Component>; if
@@ -1275,7 +1325,10 @@ workflow:
1275
1325
  requirement id THE DELTA PLAN NAMES for that element (FR / BR / CN / DC / IC / US).
1276
1326
  The spec is not in context: take the id from the plan and cite it verbatim, never
1277
1327
  guess one from the element's shape. A delta-plan element that names no id is a
1278
- planning gap — surface it rather than inventing the traceability.
1328
+ planning gap — surface it rather than inventing the traceability. THE CITATION IS
1329
+ CHECKED: analyze-slice-delta fails a lane in which some FR- or BR- its delta plan
1330
+ rows serve is named by no test — written as `FR-004`, `FR_004` or `FR004` in the
1331
+ test's name, a comment, an attribute or a tag, whichever this language allows.
1279
1332
  - WIRE WHAT YOU BUILD, in this same pass: every port, adapter, handler and endpoint
1280
1333
  this change introduces is registered in the lane's composition root, as the delta
1281
1334
  plan states. A dependency injected and never bound compiles green and fails on the
@@ -1286,11 +1339,12 @@ workflow:
1286
1339
  shared element changed — but do NOT rewrite that existing behaviour.
1287
1340
  - Where generated code depends on a delta Assumption (DA-n) or Workaround (WA-n),
1288
1341
  mark it with a comment citing that id.
1289
- - REPORT EVERY DELTA ELEMENT. The delta plan's ADDED and MODIFIED elements, by name:
1290
- {{be_element_names}}. Add an `elements` list to your closing JSON block with one
1291
- entry per name, spelled exactly as listed: {"element": "<name>", "status": "done"
1292
- | "blocked", "reason": "<what stops it, when blocked>"}. An element missing from
1293
- the list counts as not done, and the code is checked for each name after this step.
1342
+ - REPORT EVERY DELTA ELEMENT. The delta plan's ADDED and MODIFIED elements and the
1343
+ endpoints its API delta adds or changes, by name: {{be_element_names}}. Add an
1344
+ `elements` list to your closing JSON block with one entry per name, spelled exactly
1345
+ as listed: {"element": "<name>", "status": "done" | "blocked", "reason": "<what
1346
+ stops it, when blocked>"}. An element missing from the list counts as not done, and
1347
+ the code is checked for each name after this step.
1294
1348
  outputs:
1295
1349
  - { name: be_files, from: "$.files" }
1296
1350
  - { name: be_elements, from: "$.elements" }
@@ -1313,7 +1367,7 @@ workflow:
1313
1367
  action: execute_script
1314
1368
  script: { ref: actions }
1315
1369
  subcommand: plan-coverage
1316
- args: ["--plan", "{{be_plan}}", "--kind", "backend", "--mode", "{{be_mode}}", "--source-path", "{{be_source_path}}", "--test-path", "{{be_test_path}}", "--reported", "{{be_elements}}", "--generated", "{{be_files}}"]
1370
+ args: ["--plan", "{{be_plan}}", "--kind", "backend", "--mode", "{{be_mode}}", "--api-plan", "{{be_api_doc}}", "--source-path", "{{be_source_path}}", "--test-path", "{{be_test_path}}", "--reported", "{{be_elements}}", "--generated", "{{be_files}}"]
1317
1371
  outputs:
1318
1372
  - { name: be_complete, from: "$.ok" }
1319
1373
  - { name: be_outcome, from: "$.outcome" }
@@ -1353,8 +1407,8 @@ workflow:
1353
1407
  COMPLETE the backend lane. The generation pass before this one left these plan
1354
1408
  elements unrealised: {{be_gap_names}}.
1355
1409
  Why each was flagged: {{be_gaps_text}}.
1356
- The same plan, domain model and constitution are in context, and every rule of that
1357
- pass still holds: realise each element STRICTLY as the plan states it; production
1410
+ The same plans, domain model and constitution are in context, and every rule of that
1411
+ pass still holds: realise each element STRICTLY as the plans state it; production
1358
1412
  code only under `{{be_source_path}}` and paired tests only under `{{be_test_path}}`;
1359
1413
  the `{{be_lane_id}}` lane's stack ({{be_title}}, {{be_language}}) and no other;
1360
1414
  shared aggregate files EXTENDED, never regenerated; nothing the plan does not state.
@@ -1366,7 +1420,7 @@ workflow:
1366
1420
  plan's names are the ubiquitous language.
1367
1421
  - An element the plan names by intent (a port, an adapter, an endpoint route) is done
1368
1422
  when the code realises it, whatever the framework calls it; report it `done`. An
1369
- endpoint still carries the operation id the plan names, set exactly.
1423
+ endpoint still carries the operation id its API plan names, set exactly.
1370
1424
  - If an element cannot be realised, do not work around it: report it `blocked` and
1371
1425
  say what stops you. A plan that is wrong is fixed in plan / analyze-plan.
1372
1426
  - Report every listed element in an `elements` list in your closing JSON block:
@@ -1379,7 +1433,7 @@ workflow:
1379
1433
  action: execute_script
1380
1434
  script: { ref: actions }
1381
1435
  subcommand: plan-coverage
1382
- args: ["--plan", "{{be_plan}}", "--kind", "backend", "--mode", "{{be_mode}}", "--source-path", "{{be_source_path}}", "--test-path", "{{be_test_path}}", "--reported", "{{be_elements}}", "--reported", "{{be_elements_more}}"]
1436
+ args: ["--plan", "{{be_plan}}", "--kind", "backend", "--mode", "{{be_mode}}", "--api-plan", "{{be_api_doc}}", "--source-path", "{{be_source_path}}", "--test-path", "{{be_test_path}}", "--reported", "{{be_elements}}", "--reported", "{{be_elements_more}}"]
1383
1437
  outputs:
1384
1438
  - { name: be_complete, from: "$.ok" }
1385
1439
  - { name: be_outcome, from: "$.outcome" }
@@ -1411,68 +1465,32 @@ workflow:
1411
1465
  outputs:
1412
1466
  - { name: okf_concept, from: "$.concept" }
1413
1467
 
1414
- # ══════════════ THE API CONTRACT — published by the backend, read by the frontend ═══
1415
- # The frontend binds to the service through its API document, and the document that
1416
- # counts is the one the CODE serves: routes, verbs, parameters, field names and status
1417
- # codes as built, which the plans only describe. THE BACKEND PUBLISHES IT AND THE FRONTEND
1418
- # READS IT, so the two lanes can be built by different developers at different times.
1419
- # Every step below WARNS and carries on — the frontend still generates, from its plan,
1420
- # when one of them cannot run.
1421
- # 1. PUBLISH (when the service lane generated in this run) — start the service with
1422
- # its lane's `contract_run`, save what it serves at `contract` under its lane root
1423
- # folder, stop it (contract-export). The two values come from the lane in
1424
- # stacks.json — where foundation records a pack's own — or else from the stack
1425
- # profile. The document is committed with the service, and `build` refreshes it
1426
- # after a green compile. A frontend run never starts the service: its developer may
1427
- # have neither the service built nor its runtime installed.
1428
- # 2. REQUESTS — the frontend plan's contract requests the backend plan has not answered
1429
- # yet (contract-requests), named to whichever lane runs.
1430
- # 3. COMBINE (when the frontend lane generates) — the committed document plus every
1431
- # operation a backend plan has drafted (api-draft.json) and the service does not
1432
- # serve yet, written to .appweaver/contracts/openapi.combined.json (contract-combine).
1433
- # That is how a frontend binds to an endpoint the backend has planned and not built.
1434
- # 4. CLIENT — run the frontend lane's `client` command against the combined document. A
1468
+ # ══════════════ THE API CONTRACT — planned by the backend, bound to by the frontend ═══
1469
+ # The frontend binds to the service through an API document, and the document that counts
1470
+ # is the API plan's: every endpoint api-plan.md names — its backend plan's API section,
1471
+ # for a feature planned before API plans existed — with route, verb, parameters, field
1472
+ # names and status codes, drafted by script into api-draft.json beside it, and, while a
1473
+ # change is open, the change's own draft over it. The code is held to that document
1474
+ # (analyze-slice and analyze-slice-delta check the built service against it), so the two
1475
+ # lanes can be built by different developers at different times and in either order, and
1476
+ # nothing here starts the service. Every step below WARNS and carries on — the frontend
1477
+ # still generates, from its plan, when one of them cannot run.
1478
+ # 1. REQUESTS — the frontend plan's contract requests the API plan has not answered yet
1479
+ # (contract-requests), named to whichever lane runs.
1480
+ # 2. COMBINE (when the frontend lane generates) — every operation the API plans draft,
1481
+ # with the open changes applied, written to
1482
+ # .appweaver/contracts/openapi.combined.json (contract-combine). The service lane's
1483
+ # as-built document, when one is committed, adds the operations no plan describes and
1484
+ # marks the planned ones it does not serve yet.
1485
+ # 3. CLIENT — run the frontend lane's `client` command against the combined document. A
1435
1486
  # lane with none yet asks the developer once and records the answer on the lane; an
1436
1487
  # empty answer carries on without a generated client.
1437
- # 5. CHECK — every operation id the frontend plan cites must be in the combined
1438
- # document (contract-check, by script); a cited draft operation is named apart.
1439
- - id: contract_arm
1440
- condition: "{{be_go}} == true"
1441
- action: set
1442
- vars: { contract_go: "true" }
1443
- - id: contract_export
1444
- condition: "{{contract_go}} == true"
1445
- action: execute_script
1446
- on_error: continue
1447
- script: { ref: actions }
1448
- subcommand: contract-export
1449
- outputs:
1450
- - { name: contract_ok, from: "$.ok" }
1451
- - { name: export_doc, from: "$.doc" }
1452
- - { name: export_doc_exists, from: "$.doc_exists" }
1453
- - { name: contract_reason, from: "$.reason" }
1454
- - { name: contract_already, from: "$.already_running" }
1455
- - { name: contract_ops, from: "$.operations" }
1456
- - { name: contract_log, from: "$.log_tail" }
1457
- - { name: contract_export_status, from: "$.status" }
1458
- - id: warn_contract_export
1459
- condition: "{{contract_go}} == true"
1460
- action: verify_context
1461
- require_true: contract_ok
1462
- on_false: continue
1463
- on_false_message: "API DOCUMENT NOT EXPORTED — {{contract_reason}}. The service lane `{{be_lane_id}}` is started with its `contract_run` command and its document fetched from its `contract` URL, both read from .appweaver/stacks.json or else its stack profile; correct them on the lane there if they do not fit this project, start anything the service needs, and re-run implement or build. Until it is exported and committed, the frontend lane binds to the last committed document plus the backend plan's draft. Service output: {{contract_log}}"
1464
- - id: contract_stale_flag
1465
- condition: "{{contract_already}} == true"
1466
- action: set
1467
- vars: { contract_fresh: "false" }
1468
- - id: warn_contract_stale
1469
- condition: "{{contract_go}} == true"
1470
- action: verify_context
1471
- require_true: contract_fresh
1472
- on_false: continue
1473
- on_false_message: "WARNING — the API document was {{contract_reason}}. Stop that service and re-run implement to export the code as it is now."
1488
+ # 4. CHECK — every operation id the frontend plan cites must be in the combined
1489
+ # document (contract-check, by script); one an open change removes, and one the
1490
+ # service does not serve yet, are named apart.
1474
1491
 
1475
- # 2. THE REQUEST LEDGER — read from both plans, for whichever lane runs.
1492
+ # 1. THE REQUEST LEDGER — read from the frontend plan and the API plan, for whichever lane
1493
+ # runs.
1476
1494
  - id: contract_requests
1477
1495
  action: execute_script
1478
1496
  on_error: continue
@@ -1492,15 +1510,15 @@ workflow:
1492
1510
  action: verify_context
1493
1511
  require_true: req_clear
1494
1512
  on_false: continue
1495
- on_false_message: "FRONTEND CONTRACT REQUESTS OPEN — the frontend plan asks the service for {{req_open_csv}}, and backend-plan.md has not answered them: {{req_backend_text}}. This run generated the backend plan as it stands. Answer each request — publish it under an operation id, or decline it with the reason — with /appweaver plan or /appweaver analyze-plan for the backend lane, then re-run implement."
1513
+ on_false_message: "FRONTEND CONTRACT REQUESTS OPEN — the frontend plan asks the service for {{req_open_csv}}, and the API plan has not answered them: {{req_backend_text}}. This run generated the backend lane from its plans as they stand. Answer each request in the API plan's requests table — publish it under an operation id, or decline it with the reason — with /appweaver plan or /appweaver analyze-plan for the backend lane, then re-run implement."
1496
1514
  - id: warn_requests_frontend
1497
1515
  condition: "{{fe_go}} == true"
1498
1516
  action: verify_context
1499
1517
  require_true: req_clear
1500
1518
  on_false: continue
1501
- on_false_message: "CONTRACT REQUESTS STILL OPEN — {{req_open_csv}}: the frontend plan asks the service for these and the backend plan has not answered them. The bindings that wait on them are left unbound and marked in this run, and the rest of the frontend lane generates. The backend lane's developer answers them in backend-plan.md; re-run implement once they are published."
1519
+ on_false_message: "CONTRACT REQUESTS STILL OPEN — {{req_open_csv}}: the frontend plan asks the service for these and the API plan has not answered them. The bindings that wait on them are left unbound and marked in this run, and the rest of the frontend lane generates. The backend lane's developer answers them in the API plan (api-plan.md, or the backend plan of a feature planned before API plans existed); re-run implement once they are published."
1502
1520
 
1503
- # 3. THE COMBINED DOCUMENT — what the frontend reads, never the running service.
1521
+ # 2. THE COMBINED DOCUMENT — what the frontend reads, never the running service.
1504
1522
  - id: combine_arm
1505
1523
  condition: "{{fe_go}} == true"
1506
1524
  action: set
@@ -1524,7 +1542,7 @@ workflow:
1524
1542
  action: verify_context
1525
1543
  require_true: combined_ok
1526
1544
  on_false: continue
1527
- on_false_message: "NO API DOCUMENT — {{combined_reason}}. The backend lane publishes {{combined_export}} when its implement or build runs, and drafts its plan's endpoints into api-draft.json when plan or analyze-plan runs; once they are committed and pulled here, re-run implement. Until then the frontend lane binds from its plan alone."
1545
+ on_false_message: "NO API DOCUMENT — {{combined_reason}}. The backend lane drafts its API plan's endpoints into api-draft.json beside it when plan or analyze-plan runs and when finalize-delta merges a change, and a change's into its change folder; once they are committed and pulled here, re-run implement. Until then the frontend lane binds from its plan and the feature's API plan."
1528
1546
  - id: contract_doc_set
1529
1547
  condition: "{{combined_ok}} == true"
1530
1548
  action: set
@@ -1540,9 +1558,9 @@ workflow:
1540
1558
  action: verify_context
1541
1559
  require_true: clashes_clear
1542
1560
  on_false: continue
1543
- on_false_message: "DRAFT LEFT OUT — {{combined_clashes}}. The backend plan drafts an endpoint the service already serves under a different operation id, so the frontend binds to what the service serves. Correct the id in backend-plan.md (/appweaver plan or analyze-plan for the backend lane)."
1561
+ on_false_message: "API DOCUMENT CLASH — {{combined_clashes}}. Each is left as the plan states it: an endpoint drafted twice keeps its first draft, and a route the service answers under another operation id keeps the plan's id, which the frontend binds by. The backend lane corrects the plan (/appweaver plan or analyze-plan) or the code (analyze-slice reports the served id that differs)."
1544
1562
 
1545
- # 4. THE CLIENT, generated from the combined document — so only once there is one.
1563
+ # 3. THE CLIENT, generated from the combined document — so only once there is one.
1546
1564
  - id: client_arm
1547
1565
  condition: "{{contract_doc_exists}} == true"
1548
1566
  action: set
@@ -1610,7 +1628,7 @@ workflow:
1610
1628
  on_false: continue
1611
1629
  on_false_message: "NO API CLIENT GENERATED — the frontend lane `{{fe_lane_id}}` has no working `client` command, so this run writes its calls from the API document {{contract_doc}} instead. Set `client` on the lane in .appweaver/stacks.json (`{{contract_placeholder}}` stands for the document) to generate the client on the next run."
1612
1630
 
1613
- # 5. THE CHECK — the operation ids the frontend plan cites, against the combined document.
1631
+ # 4. THE CHECK — the operation ids the frontend plan cites, against the combined document.
1614
1632
  - id: contract_check
1615
1633
  condition: "{{client_go}} == true"
1616
1634
  action: execute_script
@@ -1619,25 +1637,36 @@ workflow:
1619
1637
  subcommand: contract-check
1620
1638
  args: ["--plan", "{{fe_plan}}", "--doc", "{{contract_doc}}"]
1621
1639
  outputs:
1622
- - { name: check_ok, from: "$.ok" }
1623
- - { name: check_reason, from: "$.reason" }
1624
- - { name: check_drafted, from: "$.drafted_csv" }
1640
+ - { name: check_ok, from: "$.ok" }
1641
+ - { name: check_reason, from: "$.reason" }
1642
+ - { name: check_unserved, from: "$.unserved_csv" }
1643
+ - { name: check_removed, from: "$.removed_csv" }
1625
1644
  - id: warn_contract_check
1626
1645
  condition: "{{client_go}} == true"
1627
1646
  action: verify_context
1628
1647
  require_true: check_ok
1629
1648
  on_false: continue
1630
- on_false_message: "CONTRACT CHECK — {{check_reason}}. Each binding in {{fe_plan}} names the endpoint it calls by the operation id the backend plan gives it, or the id its contract request proposes. An id in no document belongs to a contract request the backend has not answered (open now: [{{req_open_csv}}]), or the plan cites the wrong id (fix it with /appweaver plan, then analyze-plan), or an endpoint the service never gave that id (re-run implement for the backend lane). The frontend lane still generates; these bindings are where a call will fail."
1631
- - id: drafted_flag
1632
- condition: "{{check_drafted}} != "
1649
+ on_false_message: "CONTRACT CHECK — {{check_reason}}. Each binding in {{fe_plan}} names the endpoint it calls by the operation id the API plan gives it, or the id its contract request proposes. An id in no document belongs to a contract request the backend has not answered (open now: [{{req_open_csv}}]), or the frontend plan cites the wrong id (fix it with /appweaver plan, then analyze-plan), or the API plan has not published the endpoint yet. The frontend lane still generates; these bindings are where a call will fail."
1650
+ - id: unserved_flag
1651
+ condition: "{{check_unserved}} != "
1652
+ action: set
1653
+ vars: { unserved_clear: "false" }
1654
+ - id: warn_unserved
1655
+ condition: "{{client_go}} == true"
1656
+ action: verify_context
1657
+ require_true: unserved_clear
1658
+ on_false: continue
1659
+ on_false_message: "BOUND TO OPERATIONS NOT SERVED YET — {{check_unserved}}: the API plan publishes these, and the service's as-built document does not serve them yet. The frontend binds to them through the client as to any other operation; the calls fail at run time until the backend lane implements them."
1660
+ - id: removed_flag
1661
+ condition: "{{check_removed}} != "
1633
1662
  action: set
1634
- vars: { drafted_clear: "false" }
1635
- - id: warn_drafted
1663
+ vars: { removed_clear: "false" }
1664
+ - id: warn_removed
1636
1665
  condition: "{{client_go}} == true"
1637
1666
  action: verify_context
1638
- require_true: drafted_clear
1667
+ require_true: removed_clear
1639
1668
  on_false: continue
1640
- on_false_message: "BOUND TO DRAFT OPERATIONS — {{check_drafted}}: the backend plan describes these and the service does not serve them yet. The frontend binds to them through the client as to any other operation; the calls fail at run time until the backend lane implements them and publishes its API document."
1669
+ on_false_message: "BOUND TO OPERATIONS AN OPEN CHANGE REMOVES — {{check_removed}}: each is still in the API document, marked deprecated, until the service stops serving it once its change is implemented — or, where the change serves another operation on its route, is already replaced by that one, and the client no longer has it. Re-bind those elements — in the change's frontend delta plan (/appweaver plan <CR-id>) or a follow-up change — before then."
1641
1670
 
1642
1671
  # ══════════════ THE LANE SWAP — the frontend's rules replace the backend's ═══
1643
1672
  # build_context already scoped the two lanes' PAYLOADS apart; this is the same boundary
@@ -1659,7 +1688,8 @@ workflow:
1659
1688
  # The experience twin of the service generation, one lane over. Its own build_context,
1660
1689
  # because the envelope persists across steps and the two lanes must not see each other's
1661
1690
  # payload — the frontend binds to the service's PUBLISHED surface, never its internals.
1662
- # That surface is the API document the contract block exported, when there is one.
1691
+ # That surface is the API document the contract block combined, when there is one, and
1692
+ # the feature's API plan it was drafted from; the backend plan is never in context here.
1663
1693
  - id: ctx_frontend
1664
1694
  condition: "{{fe_mode}} == bootstrap"
1665
1695
  action: build_context
@@ -1667,7 +1697,8 @@ workflow:
1667
1697
  - { path: "{{fe_plan}}" }
1668
1698
  - { path: "{{domain_model}}", optional: true } # the UBIQUITOUS LANGUAGE this lane's labels, states and messages use — and what a cited BR-nnn requires
1669
1699
  - { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's ratified UX-* base — conventions, forbidden patterns, gates
1670
- - { path: "{{contract_doc}}", optional: true } # the combined API document — the service as built, plus drafted operations it does not serve yet
1700
+ - { path: "{{contract_doc}}", optional: true } # the combined API document — every operation the API plans publish, with the open changes applied
1701
+ - { path: "{{api_plan}}", optional: true } # the feature's API plan — the endpoint blocks the document is drafted from, and the answers to this plan's requests
1671
1702
  - id: gen_frontend
1672
1703
  condition: "{{fe_mode}} == bootstrap"
1673
1704
  action: agent_generate
@@ -1761,15 +1792,24 @@ workflow:
1761
1792
  semantics and rollback each row states.
1762
1793
  • §6 — the cross-cutting decisions as code: what is hidden vs disabled,
1763
1794
  accessibility obligations, localization, performance and offline behaviour.
1764
- - THE SERVICE'S API DOCUMENT IS THE CONTRACT. API document: {{contract_status}}.
1795
+ - THE API DOCUMENT IS THE CONTRACT. API document: {{contract_status}}.
1765
1796
  Each §5 row names its endpoint by Operation id; find that operation in the
1766
1797
  document and take EVERYTHING about the call from it — route, verb, path and query
1767
1798
  parameters, request and response field names and their casing, status codes and
1768
1799
  error codes. Never take a route, a field name or a paging convention from memory,
1769
- from convention or from the loaded standards' examples: the document is what the
1770
- service answers. An operation marked `x-aw-draft` is one the backend plan
1771
- describes and the service does not serve yet: bind to it exactly like any other,
1772
- through the client, and never stub, mock or fake its response in production code.
1800
+ from convention or from the loaded standards' examples: the document is drafted
1801
+ from the API plans, and the service is checked against it. This feature's
1802
+ api-plan.md is in context too where it has one — the endpoint blocks the document
1803
+ was drafted from, and the answers to this plan's contract requests. Read it for
1804
+ what a block states that the document cannot carry, such as when each error is
1805
+ returned; where the two differ on a route, verb, field or status, follow the
1806
+ document, which the client was generated from. Bind to every
1807
+ operation in it the same way, through the client, and never stub, mock or fake a
1808
+ response in production code — also one marked `x-aw-unserved` (planned, not served
1809
+ yet) or `deprecated` (an open change removes it: bind to it only where the plan
1810
+ says to, and say so in your summary). An id in the document's `x-aw-removed` list
1811
+ is one an open change removes and has replaced on its route: it is not in the
1812
+ client, so its row is a broken binding (below) — name that change when you report it.
1773
1813
  - A BINDING THAT WAITS ON AN OPEN CONTRACT REQUEST (open now: [{{req_open_csv}}]) is
1774
1814
  not broken: its operation is in no document because the backend has not answered. Realise
1775
1815
  the screen or component in full, write nothing that pretends to call the service
@@ -1785,7 +1825,10 @@ workflow:
1785
1825
  - Author a paired test for every screen, interaction and state the plan names —
1786
1826
  including the unwanted-behaviour states from §4 and an accessibility assertion per
1787
1827
  interactive surface — and cite the spec id (UX-/US-/FR-/CN-/DC-/IC-/SC-) from the
1788
- plan's §7 Traceability in each test. A snapshot alone is not a test.
1828
+ plan's §7 Traceability in each test. A snapshot alone is not a test. THE CITATION IS
1829
+ CHECKED: analyze-slice fails a slice in which some FR- or BR- of §7 is named by no
1830
+ test — written as `FR-004`, `FR_004` or `FR004` in the test's name, a comment, an
1831
+ attribute or a tag.
1789
1832
  - Do NOT add behaviour the plan does not state, and do NOT omit any it does. If the
1790
1833
  plan looks wrong or incomplete, STOP and surface it (it is fixed in plan /
1791
1834
  analyze-plan) — never compensate from the spec or from memory.
@@ -1820,7 +1863,9 @@ workflow:
1820
1863
  - { path: "{{frontend_plan}}", optional: true } # the master plan — READ ONLY, never amended here
1821
1864
  - { path: "{{domain_model}}", optional: true } # the master model — the ubiquitous language as it stands BEFORE this change
1822
1865
  - { path: .appweaver/memory/constitution.ui.md, optional: true }
1823
- - { path: "{{contract_doc}}", optional: true } # the combined API document — the service as built, plus drafted operations it does not serve yet
1866
+ - { path: "{{contract_doc}}", optional: true } # the combined API document — every operation the API plans publish, with the open changes applied
1867
+ - { path: "{{delta_api_plan}}", optional: true } # this change's API delta — its Contract deltas table and the endpoints it writes whole
1868
+ - { path: "{{api_plan}}", optional: true } # the feature's API plan — the endpoints as they stand BEFORE this change
1824
1869
  - id: gen_frontend_delta
1825
1870
  condition: "{{fe_mode}} == delta"
1826
1871
  action: agent_generate
@@ -1897,16 +1942,21 @@ workflow:
1897
1942
  A new interaction without them is an incomplete implementation, not a later
1898
1943
  refinement.
1899
1944
  - BIND AGAINST THE PUBLISHED SURFACE ONLY: the delta plan's binding rows and the
1900
- service delta's "Contract deltas for the frontend lane" table as the delta plan
1901
- cites them. Generate no server-side behaviour, no persistence and no business rule
1945
+ change's API delta — the "Contract deltas for the frontend lane" table of
1946
+ delta-api-plan.md, in context where the change has one — as the delta plan cites
1947
+ them. Generate no server-side behaviour, no persistence and no business rule
1902
1948
  evaluation.
1903
- - THE SERVICE'S API DOCUMENT IS THE CONTRACT. API document: {{contract_status}}.
1949
+ - THE API DOCUMENT IS THE CONTRACT. API document: {{contract_status}}.
1904
1950
  Each binding row leads with its endpoint's operation id; find that operation in
1905
1951
  the document and take EVERYTHING about the call from it — route, verb, path and
1906
1952
  query parameters, request and response field names and their casing, status
1907
1953
  codes and error codes — never from memory, convention or the standards' examples.
1908
- An operation marked `x-aw-draft` is planned and not served yet: bind to it through
1909
- the client like any other, and never stub, mock or fake its response.
1954
+ It carries this change's own API changes over the master's. The feature's
1955
+ api-plan.md and this change's delta-api-plan.md, in context where they exist, are
1956
+ what it was drafted from: read them for what a block states that the document
1957
+ cannot carry, and follow the document where they differ. Bind to every
1958
+ operation through the client the same way — also one marked `x-aw-unserved`
1959
+ (planned, not served yet) — and never stub, mock or fake its response.
1910
1960
  A binding whose id the document does not have is broken: leave that call unbound,
1911
1961
  report the element `blocked` naming the id, and never guess a substitute.
1912
1962
  - THE CLIENT: {{client_status}}. The client lives in this lane's client layer; a
@@ -1916,6 +1966,9 @@ workflow:
1916
1966
  interactive surface, citing the requirement id THE DELTA PLAN NAMES for that row
1917
1967
  (UX-/US-/FR-/CN-/DC-/IC-/SC-). The spec is not in context: take the id from the
1918
1968
  plan and cite it verbatim. A row that names no id is a planning gap — surface it.
1969
+ THE CITATION IS CHECKED: analyze-slice-delta fails the lane when some FR- or BR- the
1970
+ delta plan rows serve is named by no test (`FR-004`, `FR_004` or `FR004`, in the
1971
+ test's name, a comment, an attribute or a tag).
1919
1972
  - REGRESSION: for each element in the delta plan's regression targets, verify (add or
1920
1973
  adjust a test where needed) that the existing behaviour still holds after a shared
1921
1974
  component or state container changed — but do NOT rewrite that behaviour.
@@ -2150,13 +2203,13 @@ outputs:
2150
2203
  cr_id: "{{active_change}}"
2151
2204
  backend_implemented: "{{be_go}}"
2152
2205
  backend_plan: "{{be_plan}}" # the document the backend lane generated FROM
2206
+ api_plan: "{{be_api_doc}}" # the API plan it served — empty where that plan carries its own API section
2153
2207
  backend_files: "{{be_files}}"
2154
2208
  frontend_implemented: "{{fe_go}}"
2155
2209
  frontend_plan: "{{fe_plan}}" # the document the frontend lane generated FROM
2156
2210
  frontend_files: "{{fe_files}}"
2157
- api_document: "{{export_doc}}" # exported from the service in this run (backend lane) — commit it, it is how the frontend developer receives the surface
2158
- frontend_api_document: "{{contract_doc}}" # what the frontend bound to: the export plus the drafted operations it does not serve yet (not committed)
2159
- contract_requests: "{{req_summary}}" # the frontend plan's RQ-n requests and the backend plan's answers
2211
+ frontend_api_document: "{{contract_doc}}" # what the frontend bound to: every operation the API plans publish, with the open changes applied (not committed)
2212
+ contract_requests: "{{req_summary}}" # the frontend plan's RQ-n requests and the API plan's answers
2160
2213
  # Every plan element realised, per lane — `true` for a lane that did not generate.
2161
2214
  # `*_completion_pass` says whether the first pass left gaps a second one had to close.
2162
2215
  # `*_outcome` is the one word for what the lane did: complete (it generated and every
@@ -2192,5 +2245,5 @@ outputs:
2192
2245
  frontend_plan_gate_at: "{{fe_gate_at}}"
2193
2246
  frontend_plan_gate_verdict: "{{fe_gate_verdict}}"
2194
2247
  frontend_plan_gate_routed_to_specify: "{{fe_gate_routed_csv}}"
2195
- note: "Lanes come from .appweaver/stacks.json, and the ones you work on from .appweaver/lane.json (`{{working_lane}}`) — backend declared {{be_declared}} (worked {{be_working}}, ready {{be_runnable}}, implemented {{be_go}}), frontend declared {{fe_declared}} (worked {{fe_working}}, ready {{fe_runnable}}, implemented {{fe_go}}). A declared lane you do not work on is left to its developer and is not reported here. A declared lane that was skipped printed its reason above and did not stop the other one; an undeclared lane produces nothing and that is not a failure. Each generating lane read its PLAN for what to build and {{domain_model}} for what the domain is — plus {{delta_model}} on the delta path, since the master model does not carry the change until finalize-delta merges it. Every one of those documents was READ and none was written: this phase generates code and nothing else. Each lane's outcome is backend={{be_outcome}}, frontend={{fe_outcome}} — `already-implemented` means the lane wrote no files because every plan element was already realised in its lane root folder, which is the correct result of re-running implement on a finished feature and not a failure. On the bootstrap path each working lane's plan was checked against that lane's analyze-plan receipt before anything generated (backend tracked={{be_gate_tracked}}, fresh={{be_gate_fresh}}, gated {{be_gate_at}}; frontend tracked={{fe_gate_tracked}}, fresh={{fe_gate_fresh}}, gated {{fe_gate_at}}) — tracked=false means that lane has never passed analyze-plan since the gate began recording what it proved, which is an unknown rather than a stale plan and blocks nothing; run /appweaver analyze-plan once to start the record."
2248
+ note: "Lanes come from .appweaver/stacks.json, and the ones you work on from .appweaver/lane.json (`{{working_lane}}`) — backend declared {{be_declared}} (worked {{be_working}}, ready {{be_runnable}}, implemented {{be_go}}), frontend declared {{fe_declared}} (worked {{fe_working}}, ready {{fe_runnable}}, implemented {{fe_go}}). A declared lane you do not work on is left to its developer and is not reported here. A declared lane that was skipped printed its reason above and did not stop the other one; an undeclared lane produces nothing and that is not a failure. Each generating lane read its PLAN for what to build — the backend lane with the API plan beside it, where there is one ({{be_api_doc}}) — and {{domain_model}} for what the domain is — plus {{delta_model}} on the delta path, since the master model does not carry the change until finalize-delta merges it. Every one of those documents was READ and none was written: this phase generates code and nothing else. Each lane's outcome is backend={{be_outcome}}, frontend={{fe_outcome}} — `already-implemented` means the lane wrote no files because every plan element was already realised in its lane root folder, which is the correct result of re-running implement on a finished feature and not a failure. On the bootstrap path each working lane's plan was checked against that lane's analyze-plan receipt before anything generated (backend tracked={{be_gate_tracked}}, fresh={{be_gate_fresh}}, gated {{be_gate_at}}; frontend tracked={{fe_gate_tracked}}, fresh={{fe_gate_fresh}}, gated {{fe_gate_at}}) — tracked=false means that lane has never passed analyze-plan since the gate began recording what it proved, which is an unknown rather than a stale plan and blocks nothing; run /appweaver analyze-plan once to start the record."
2196
2249
  next: "This run was mode `{{mode}}`, change `{{active_change}}` (empty on the bootstrap path). BOOTSTRAP → /appweaver build → analyze-slice → run. DELTA → /appweaver build → analyze-slice-delta {{active_change}} → finalize-delta → analyze-slice → close-delta, with the change still OPEN throughout — compile first, then analyze-slice-delta verifies that every element the delta plans marked ADDED or MODIFIED is actually in the code with a paired test (the gate that catches a lane you declined at the prompt above), then finalize-delta merges it into spec.md + the context's domain-model.md while leaving it open, analyze-slice verifies the slice against those merged masters while a red result is still recoverable, and close-delta LAST archives the change and bumps the version."