aw-spec-cli 0.1.26 → 0.1.28

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 (137) hide show
  1. package/assets/.agents/skills/appweaver/SKILL.md +7 -47
  2. package/assets/.appweaver/ORCH-DIALECT.md +13 -9
  3. package/assets/.appweaver/RULE-PARTITION.md +11 -12
  4. package/assets/.appweaver/STACK-PROFILES.md +260 -108
  5. package/assets/.appweaver/domain.json +1 -1
  6. package/assets/.appweaver/interpreter/run-skill.py +28 -7
  7. package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +38 -10
  8. package/assets/.appweaver/orchestrator/LAUNCHER.md +88 -0
  9. package/assets/.appweaver/orchestrator/README.md +120 -53
  10. package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +1 -1
  11. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +17 -7
  12. package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +6 -4
  13. package/assets/.appweaver/orchestrator/modules/foundation-lane.orch.yaml +237 -0
  14. package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +392 -0
  15. package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +548 -0
  16. package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +4 -4
  17. package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +3 -3
  18. package/assets/.appweaver/orchestrator/orchestrator.py +162 -63
  19. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +5 -4
  20. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +3 -1
  21. package/assets/.appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md +2 -2
  22. package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +2 -2
  23. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +21 -13
  24. package/assets/.appweaver/orchestrator/rev-templates/frontend-rule-file.skeleton.md +1 -1
  25. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +28 -31
  26. package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +2 -2
  27. package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +1 -1
  28. package/assets/.appweaver/orchestrator/rev-templates/ui-constitution.skeleton.md +3 -3
  29. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +4 -1
  30. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +6 -4
  31. package/assets/.appweaver/orchestrator/workflows/_smoke.orch.yaml +41 -3
  32. package/assets/.appweaver/orchestrator/workflows/aw-spec-add-component.orch.yaml +1 -1
  33. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +17 -16
  34. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +7 -1
  35. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +7 -1
  36. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +3 -3
  37. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +3 -3
  38. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
  39. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +2 -2
  40. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +61 -12
  41. package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +1 -1
  42. package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +172 -293
  43. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +253 -44
  44. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +72 -71
  45. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
  46. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +38 -14
  47. package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +4 -4
  48. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +26 -2
  49. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +21 -15
  50. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +14 -14
  51. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +3 -3
  52. package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +3 -3
  53. package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +6 -6
  54. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +371 -131
  55. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +293 -76
  56. package/assets/.appweaver/scripts/python/aw-foundation.py +885 -20
  57. package/assets/.appweaver/scripts/python/aw-inventory.py +28 -21
  58. package/assets/.appweaver/scripts/python/aw-plan-coverage.py +2 -2
  59. package/assets/.appweaver/scripts/python/aw-rev-constitution.py +6 -6
  60. package/assets/.appweaver/scripts/python/aw-stack.py +676 -99
  61. package/assets/.appweaver/stack-profiles/angular.json +2 -1
  62. package/assets/.appweaver/stack-profiles/blazor-wasm.json +2 -1
  63. package/assets/.appweaver/stack-profiles/dotnet-webapi.json +18 -3
  64. package/assets/.appweaver/stack-profiles/go-chi.json +6 -1
  65. package/assets/.appweaver/stack-profiles/java-spring.json +14 -5
  66. package/assets/.appweaver/stack-profiles/nextjs.json +2 -1
  67. package/assets/.appweaver/stack-profiles/node-express.json +8 -2
  68. package/assets/.appweaver/stack-profiles/python-fastapi.json +9 -2
  69. package/assets/.appweaver/stack-profiles/react-vite.json +2 -1
  70. package/assets/.appweaver/stack-profiles/sveltekit.json +2 -1
  71. package/assets/.appweaver/stack-profiles/vue-vite.json +2 -1
  72. package/assets/.appweaver/stacks.json +40 -1
  73. package/assets/.appweaver/templates/constitution-ui-template.md +2 -2
  74. package/assets/.appweaver/templates/delta-design-template.md +7 -5
  75. package/assets/.appweaver/templates/design-template.md +3 -0
  76. package/assets/.appweaver/templates/ui-delta-design-template.md +1 -1
  77. package/assets/.appweaver/templates/ui-design-template.md +6 -4
  78. package/assets/.claude/skills/appweaver/SKILL.md +7 -46
  79. package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.Reference.rules.json +31 -0
  80. package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.lane.json +6 -0
  81. package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.Reference.rules.json +16 -0
  82. package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.lane.json +6 -0
  83. package/assets/foundations/foundation.spec.template.txt +98 -0
  84. package/assets/foundations/java/Hexagonal/Hexagonal.lane.json +6 -0
  85. package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +8 -1
  86. package/assets/foundations/java/Hexagonal/unpack-foundation.sh +4 -5
  87. package/assets/foundations/node/NextAppRouter/NextAppRouter.spec.txt +68 -0
  88. package/assets/guardrails/README.md +31 -17
  89. package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain-rule-bindings.md +25 -13
  90. package/assets/guardrails/project-specific/tech-rule-bindings.md +296 -0
  91. package/assets/guardrails/technology/dotnet-webapi/PACKAGING.md +58 -4
  92. package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +1 -1
  93. package/assets/guardrails/technology/dotnet-webapi/auditing.md +1 -1
  94. package/assets/guardrails/technology/dotnet-webapi/authorization.md +1 -1
  95. package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +1 -1
  96. package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +1 -1
  97. package/assets/guardrails/technology/dotnet-webapi/cqrs.md +1 -1
  98. package/assets/guardrails/technology/dotnet-webapi/data-classification.md +1 -1
  99. package/assets/guardrails/technology/dotnet-webapi/dependency-policy.md +1 -1
  100. package/assets/guardrails/technology/dotnet-webapi/eventing.md +1 -1
  101. package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
  102. package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +1 -1
  103. package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +1 -1
  104. package/assets/guardrails/technology/dotnet-webapi/persistence.md +1 -1
  105. package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
  106. package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +1 -1
  107. package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +1 -1
  108. package/assets/guardrails/technology/nextjs/accessibility-implementation.md +1 -1
  109. package/assets/guardrails/technology/nextjs/api-client.md +1 -1
  110. package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +1 -1
  111. package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +1 -1
  112. package/assets/guardrails/technology/nextjs/auth-session.md +1 -1
  113. package/assets/guardrails/technology/nextjs/client-security.md +1 -1
  114. package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
  115. package/assets/guardrails/technology/nextjs/component-conventions.md +2 -2
  116. package/assets/guardrails/technology/nextjs/design-system.md +1 -1
  117. package/assets/guardrails/technology/nextjs/error-handling.md +1 -1
  118. package/assets/guardrails/technology/nextjs/forms-and-validation.md +1 -1
  119. package/assets/guardrails/technology/nextjs/frontend-testing.md +1 -1
  120. package/assets/guardrails/technology/nextjs/i18n.md +1 -1
  121. package/assets/guardrails/technology/nextjs/observability.md +1 -1
  122. package/assets/guardrails/technology/nextjs/performance-budgets.md +1 -1
  123. package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
  124. package/assets/guardrails/technology/nextjs/state-management.md +1 -1
  125. package/assets/guardrails/technology/nextjs/ui-constitution.md +3 -3
  126. package/package.json +1 -1
  127. package/assets/.agents/skills/appweaver-demo/SKILL.md +0 -59
  128. package/assets/.appweaver/orchestrator/.smoke-out/smoke.md +0 -6
  129. package/assets/.appweaver/orchestrator/workflows/aw-spec-demo.orch.yaml +0 -100
  130. package/assets/.claude/skills/appweaver-demo/SKILL.md +0 -59
  131. package/assets/.kilocode/agent.json +0 -28
  132. package/assets/.kilocode/workflows/appweaver-demo.md +0 -59
  133. package/assets/.kilocode/workflows/appweaver.md +0 -55
  134. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +0 -271
  135. /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain/domain-requirements.md +0 -0
  136. /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/frontend/ui-requirements.md +0 -0
  137. /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/service/technical-requirements.md +0 -0
@@ -28,7 +28,7 @@
28
28
  #
29
29
  # EACH LANE IS GATED ON ITS OWN, AND A LANE'S GAP IS NOT THE RUN'S. A declared lane needs
30
30
  # two things before it can generate: the plan for THIS run (its master plan, or this
31
- # change's delta plan) and a scaffolded project under the source path it declares. A lane
31
+ # change's delta plan) and a scaffolded project under the lane root folder it declares. A lane
32
32
  # that is missing either is REPORTED with the fix named and SKIPPED — the other lane still
33
33
  # runs. Failing the whole command for the frontend's sake is how a backend that was ready
34
34
  # goes unbuilt; failing silently is how a frontend that was never generated reports green.
@@ -38,6 +38,12 @@
38
38
  # pipeline, so each runnable lane confirms first, naming its stack, its plan and the
39
39
  # subtree it will write into. Answering no to one lane leaves the other free to run.
40
40
  #
41
+ # THE FRONTEND BINDS TO THE SERVICE AS BUILT. Between the two lanes, the service's API
42
+ # document is exported from the running code, the frontend's client is generated from it,
43
+ # and the operation ids the frontend plan cites are checked against it; the frontend then
44
+ # generates with the document in context. Each of those steps warns rather than stops —
45
+ # see the API CONTRACT block before the lane swap.
46
+ #
41
47
  # THE PLAN SAYS WHAT TO BUILD, THE MODEL SAYS WHAT THE DOMAIN IS. This phase does NOT read
42
48
  # spec.md — analyze-plan already conformed the plans to it, so the plan is the complete,
43
49
  # authoritative source for WHICH elements exist, where each lives and what surface they
@@ -81,15 +87,15 @@
81
87
  # the constitution halves + this phase's rule sets. Each lane takes its rules from its own
82
88
  # heading of tech-rule-bindings, and the frontend set is loaded only when that lane runs,
83
89
  # so a backend-only project sees exactly the rules it did before:
84
- # • BACKEND — `## /aw-spec-implement` on the bootstrap path, `## /aw-spec-implement-delta`
85
- # on the delta path. Keyed by MODE, which is what the two commands did separately, so
86
- # no project's rule set changes by being merged into one command.
87
- # • FRONTEND — `## /aw-spec-implement-ui`, both paths. There was never a delta heading
88
- # for this lane because the delta half of it was never built; it is built here.
90
+ # • BACKEND — `## /aw-spec-implement-backend`, both paths.
91
+ # • FRONTEND — `## /aw-spec-implement-frontend`, both paths.
92
+ # A change's code is generated against the same rules as the master it changes, so
93
+ # neither lane has a separate delta heading.
89
94
  #
90
- # TWO PATHS PER LANE. Each lane owns the source path stacks.json declares for it and the
91
- # test path lane-verify resolves beside it — `tests/` next to a .NET `src/`, the source
92
- # path itself for a stack that keeps its tests inside — and writes nothing outside them.
95
+ # TWO PATHS PER LANE. Each lane owns the lane root folder stacks.json declares for it and the
96
+ # test path lane-verify resolves for it — `backend/tests/` inside a new lane root folder,
97
+ # `tests/` next to a classic .NET `src/`, the lane root folder itself for a stack that keeps
98
+ # its tests inside — and writes nothing outside them.
93
99
  # The one exception is registering a test project the lane had to create, in whatever
94
100
  # file the lane's test command discovers tests through (a .NET solution file). A file
95
101
  # written elsewhere belongs to the other lane or to no lane at all, and the project gate
@@ -99,35 +105,35 @@
99
105
  #
100
106
  # A FILE LIST IS NOT A FINISHED LANE. Each generating lane is handed its plan's element
101
107
  # list and reports every element back as done or blocked; `plan-coverage` then checks the
102
- # code under the lane's source path for every identifier the plan introduces. Whatever
108
+ # code under the lane root folder for every identifier the plan introduces. Whatever
103
109
  # either check leaves open gets one completion pass, and anything still open after it
104
110
  # stops the run once both lanes have generated — so a pass that stopped at five use-cases
105
111
  # of seven is reported, not shipped.
106
112
  #
107
113
  # A LANE'S GAP DISABLES THAT LANE, NOT THE RUN. Three things a lane needs are checked per
108
- # lane and reported per lane: its source path is on disk, its plan is there, and a project
114
+ # lane and reported per lane: its lane root folder is on disk, its plan is there, and a project
109
115
  # is scaffolded under it. Any of them missing skips that lane with the command that fixes
110
116
  # it named, and every other declared lane still generates; only when NOTHING can run does
111
117
  # the run stop, with all the reasons printed. The whole-run stop is reserved for states
112
118
  # with no correct lane to fall back on — no lane declared at all, a lane declaring no
113
- # source path, and two lanes claiming overlapping subtrees.
119
+ # lane root folder, and two lanes claiming overlapping subtrees.
114
120
  #
115
121
  # WHAT IT DOES NOT DO. It never writes spec.md, a master plan or the domain model, and it
116
122
  # never closes a change — finalize-delta merges the change's spec and model halves,
117
123
  # analyze-slice verifies the slice, and close-delta archives it and bumps the version.
118
124
  #
119
125
  # Prereqs: /appweaver plan (+ analyze-plan, or analyze-plan-delta for a change), and a
120
- # project scaffolded under each lane's source path — this phase GENERATES into that tree,
121
- # so with no project there is nowhere to place the files. /appweaver foundation unpacks one
122
- # where a bundle ships for the stack; a stack with no bundle scaffolds with its own tooling
123
- # instead, and the gate accepts either, since it tests for a project rather than a bundle.
126
+ # project scaffolded under each lane root folder — this phase GENERATES into that tree,
127
+ # so with no project there is nowhere to place the files. /appweaver foundation scaffolds one,
128
+ # from a pack or from a spec; a team may also scaffold with the stack's own tooling, and the
129
+ # gate accepts any of them, since it tests for a project rather than for how it got there.
124
130
  #
125
131
  # Launch: /appweaver implement [guidance]
126
132
  # /appweaver implement CR-20260910-… (implement THAT open change, no prompt)
127
133
  # =============================================================================
128
134
 
129
135
  name: aw-spec-implement
130
- 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 it generates from the master plans; with one or more open it ASKS which change and generates only what that change's delta plans mark ADDED or MODIFIED, leaving the masters untouched. Every lane is gated on its own source path, 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."
136
+ 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 it generates from the master plans; with one or more open it ASKS which change and generates only what that change's delta plans mark ADDED or MODIFIED, 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."
131
137
  version: 3.10.0
132
138
  agent_agnostic: true
133
139
  dialect: aw-spec.orchestrator/v1
@@ -148,7 +154,7 @@ scripts:
148
154
 
149
155
  # Sticky standards seed = the UNIVERSAL always-on rule only (ai-rules). Each lane's
150
156
  # FRAMEWORK / code-generation rule files are CONFIGURATION, loaded per-lane from
151
- # guardrails/projects/<project>/tech-rule-bindings.md by the tech-select module (see the
157
+ # guardrails/<project>/tech-rule-bindings.md by the tech-select module (see the
152
158
  # tech_select steps below). Per the rule-partition this phase loads NO domain rule file —
153
159
  # everything domain reached the code through spec → plan. The architecture and UX bases
154
160
  # arrive via constitution.technical.md / constitution.ui.md (referred to directly, as-is —
@@ -247,15 +253,37 @@ vars:
247
253
  fe_completion_ran: "false"
248
254
  fe_gap_names: ""
249
255
  fe_gaps_text: "none"
256
+ # THE API CONTRACT between the lanes — see the block before the lane swap. Every flag
257
+ # starts lowered, so a run without a frontend lane never touches any of it.
258
+ contract_go: "false"
259
+ contract_ok: ""
260
+ contract_doc: ""
261
+ contract_doc_exists: "false"
262
+ contract_reason: ""
263
+ contract_already: "false"
264
+ contract_fresh: "true"
265
+ contract_log: ""
266
+ contract_placeholder: ""
267
+ contract_export_status: ""
268
+ contract_status: "not exported — no API document is in context, so take each binding from the plan alone and say so in your summary"
269
+ client_go: "false"
270
+ client_ask: "false"
271
+ client_answer: ""
272
+ client_saved: "false"
273
+ client_run: "false"
274
+ client_present: "false"
275
+ client_ok: ""
276
+ client_cmd: ""
277
+ client_log: ""
278
+ client_status: "none was generated — write the calls in the lane's client layer from the API document, exactly as it states them"
279
+ check_ok: ""
280
+ check_reason: ""
250
281
  lanes_complete: "true"
251
282
  generate_any: "false"
252
283
  record_edges: "false"
253
284
  overview_ok: "false"
254
285
  domain_model: ""
255
286
  domain_model_exists: "false"
256
- # The backend lane's rule-set heading, keyed by MODE — see the header. The frontend
257
- # lane's is the same on both paths, so it is written inline at its include.
258
- be_tech_command: "aw-spec-implement"
259
287
  # HOOK PHASES. The merged command fires the phase of every branch it actually takes, so
260
288
  # an extensions.yml wired to any of the three former commands keeps working:
261
289
  # before/after_implement always, the _ui pair when the frontend lane generates, the _cr
@@ -272,15 +300,15 @@ workflow:
272
300
  # kind: a backend-only and a frontend-only project are both correct states, and which
273
301
  # lanes exist is exactly what it is here to find out.
274
302
  #
275
- # --allow-missing-root, because a lane whose source path is not on disk is THAT LANE'S
303
+ # --allow-missing-root, because a lane whose lane root folder is not on disk is THAT LANE'S
276
304
  # gap, not the run's. This phase is per-lane throughout — a missing plan or a missing
277
305
  # project is reported at step 5 and the other lane still generates — and a missing root
278
306
  # is the same kind of gap, most often a declared frontend that `foundation` has not
279
- # unpacked yet. With the check left on it arrives here instead, as a whole-run stop
307
+ # scaffolded yet. With the check left on it arrives here instead, as a whole-run stop
280
308
  # that takes the ready lane down with it. It is re-checked per lane at step 5 from
281
309
  # `*_root_exists`, which lane-verify reports whether or not the on-disk half is armed.
282
310
  #
283
- # What still stops the run: no lane declared at all, a lane declaring no source path,
311
+ # What still stops the run: no lane declared at all, a lane declaring no lane root folder,
284
312
  # and two lanes claiming overlapping subtrees. Those are declaration errors with no
285
313
  # correct lane to fall back to, and none of them is a state any phase can work around.
286
314
  - id: lane_verify
@@ -316,6 +344,8 @@ workflow:
316
344
  - { name: fe_build, from: "$.frontend_build" }
317
345
  - { name: fe_test, from: "$.frontend_test" }
318
346
  - { name: fe_reserved, from: "$.frontend_reserved_csv" }
347
+ - { name: fe_workdir, from: "$.frontend_workdir" }
348
+ - { name: fe_client, from: "$.frontend_client" }
319
349
 
320
350
  - id: require_lanes
321
351
  action: verify_context
@@ -401,7 +431,6 @@ workflow:
401
431
  mode: "delta"
402
432
  pre_cr_phase: "before_implement_cr"
403
433
  post_cr_phase: "after_implement_cr"
404
- be_tech_command: "aw-spec-implement-delta"
405
434
 
406
435
  - id: pick_change
407
436
  condition: "{{mode}} == delta"
@@ -539,8 +568,8 @@ workflow:
539
568
  # detect-project searches the LANE'S OWN subtree for an ecosystem manifest (.sln/
540
569
  # .csproj, pom.xml, build.gradle, package.json, go.mod, Cargo.toml, pyproject.toml),
541
570
  # falls back to the whole repo, and reports which scope answered. How the project got
542
- # there is not asked — `foundation` unpacked it, or the team scaffolded it with the
543
- # stack's own tooling.
571
+ # there is not asked — `foundation` unpacked or built it, or the team scaffolded it with
572
+ # the stack's own tooling.
544
573
  - id: be_detect_project
545
574
  condition: "{{be_declared}} == true"
546
575
  action: execute_script
@@ -555,7 +584,7 @@ workflow:
555
584
  # in a two-lane project is the service lane's manifest, and generating this lane's code
556
585
  # against it is exactly the failure the lane model exists to prevent — a frontend that
557
586
  # was never scaffolded, generated into anyway, reporting green. The service lane accepts
558
- # either scope, because the layout that keeps a manifest beside the lane root rather than
587
+ # either scope, because the layout that keeps a manifest beside the lane root folder rather than
559
588
  # inside it is a normal one for it.
560
589
  - id: fe_detect_project
561
590
  condition: "{{fe_declared}} == true"
@@ -586,13 +615,13 @@ workflow:
586
615
  action: verify_context
587
616
  require_true: be_root_exists
588
617
  on_false: continue
589
- on_false_message: "BACKEND LANE SKIPPED — lane `{{be_lane_id}}` ({{be_title}}) declares source path `{{be_source_path}}` in .appweaver/stacks.json, and that path is not a directory in this repository. A lane owns its subtree and this phase writes into it, so there is nothing to generate into: unpack a project there with /appweaver foundation <FoundationName> <AppName> where a bundle ships for {{be_profile}}, create one with the stack's own tooling, or re-run /appweaver select-stack and point the lane at the directory that really holds its source. Nothing is generated for this lane; any other declared lane still runs."
618
+ on_false_message: "BACKEND LANE SKIPPED — lane `{{be_lane_id}}` ({{be_title}}) declares lane root folder `{{be_source_path}}` in .appweaver/stacks.json, and that path is not a directory in this repository. A lane owns its subtree and this phase writes into it, so there is nothing to generate into: scaffold a project there with /appweaver foundation (a pack or a spec for {{be_profile}}, or a spec you give it), create one with the stack's own tooling, or re-run /appweaver select-stack and point the lane at the directory that really holds its source. Nothing is generated for this lane; any other declared lane still runs."
590
619
  - id: warn_be_project
591
620
  condition: "{{be_declared}} == true"
592
621
  action: verify_context
593
622
  require_true: be_project_ok
594
623
  on_false: continue
595
- on_false_message: "BACKEND LANE SKIPPED — no project found under the {{be_lane_id}} lane's source path `{{be_source_path}}`, nor anywhere else in this repository. This phase GENERATES into an existing project tree, so one has to be scaffolded first: run /appweaver foundation <FoundationName> <AppName> where a foundation bundle ships for {{be_profile}}, or create the project with the stack's own tooling under that path. Any ecosystem manifest marks it — .sln/.csproj, pom.xml, build.gradle(.kts), package.json, go.mod, Cargo.toml or pyproject.toml."
624
+ on_false_message: "BACKEND LANE SKIPPED — no project found under the {{be_lane_id}} lane root folder `{{be_source_path}}`, nor anywhere else in this repository. This phase GENERATES into an existing project tree, so one has to be scaffolded first: run /appweaver foundation, which scaffolds it from a pack or from a spec (one it ships for {{be_profile}}, or one you give it), or create the project with the stack's own tooling under that path. Any ecosystem manifest marks it — .sln/.csproj, pom.xml, build.gradle(.kts), package.json, go.mod, Cargo.toml or pyproject.toml."
596
625
  - id: warn_fe_plan
597
626
  condition: "{{fe_declared}} == true"
598
627
  action: verify_context
@@ -604,15 +633,15 @@ workflow:
604
633
  action: verify_context
605
634
  require_true: fe_root_exists
606
635
  on_false: continue
607
- on_false_message: "FRONTEND LANE SKIPPED — lane `{{fe_lane_id}}` ({{fe_title}}) declares source path `{{fe_source_path}}` in .appweaver/stacks.json, and that path is not a directory in this repository. A lane owns its subtree and this phase writes into it, so there is nothing to generate into: unpack a project there with /appweaver foundation <FoundationName> <AppName> where a bundle ships for {{fe_profile}}, create one with the stack's own tooling, or re-run /appweaver select-stack and point the lane at the directory that really holds its source. Nothing is generated for this lane; any other declared lane still runs."
636
+ on_false_message: "FRONTEND LANE SKIPPED — lane `{{fe_lane_id}}` ({{fe_title}}) declares lane root folder `{{fe_source_path}}` in .appweaver/stacks.json, and that path is not a directory in this repository. A lane owns its subtree and this phase writes into it, so there is nothing to generate into: scaffold a project there with /appweaver foundation (a pack or a spec for {{fe_profile}}, or a spec you give it), create one with the stack's own tooling, or re-run /appweaver select-stack and point the lane at the directory that really holds its source. Nothing is generated for this lane; any other declared lane still runs."
608
637
  - id: warn_fe_project
609
638
  condition: "{{fe_declared}} == true"
610
639
  action: verify_context
611
640
  require_true: fe_project_ok
612
641
  on_false: continue
613
- on_false_message: "FRONTEND LANE SKIPPED — no frontend project found under the {{fe_lane_id}} lane's source path `{{fe_source_path}}`. detect-project searched that subtree and then the whole repository, and answered at scope `{{fe_project_scope}}`; `lane` is the only scope this lane accepts. Scaffold a project there — /appweaver foundation <FoundationName> <AppName> where a bundle ships for {{fe_profile}}, or the stack's own tooling — and re-run implement. A manifest found elsewhere in the repository does NOT count: that one belongs to another lane, and generating this lane's code against it is how a frontend that was never built reports green."
642
+ on_false_message: "FRONTEND LANE SKIPPED — no frontend project found under the {{fe_lane_id}} lane root folder `{{fe_source_path}}`. detect-project searched that subtree and then the whole repository, and answered at scope `{{fe_project_scope}}`; `lane` is the only scope this lane accepts. Scaffold a project there — /appweaver foundation, from a pack or a spec for {{fe_profile}} or a spec you give it, or the stack's own tooling — and re-run implement. A manifest found elsewhere in the repository does NOT count: that one belongs to another lane, and generating this lane's code against it is how a frontend that was never built reports green."
614
643
 
615
- # 6. RUNNABLE = declared AND its source path is on disk AND its plan is there AND its
644
+ # 6. RUNNABLE = declared AND its lane root folder is on disk AND its plan is there AND its
616
645
  # project is there, as one ordered assignment plus a knockdown per condition, since
617
646
  # the engine has no compound conditions. An undeclared lane stays false either way —
618
647
  # its `declared` value is what the raise assigns.
@@ -676,7 +705,7 @@ workflow:
676
705
  action: verify_context
677
706
  require_true: generate_any
678
707
  on_false: stop
679
- on_false_message: "Nothing to implement — no declared lane is both ready and confirmed. Either every declared lane was skipped for a reason printed above (its source path, its plan or its project is missing), or each one that was ready was declined at the prompt. Backend lane declared {{be_declared}}, ready {{be_runnable}}; frontend lane declared {{fe_declared}}, ready {{fe_runnable}}. Fix the reasons named above, or re-run and confirm a lane."
708
+ on_false_message: "Nothing to implement — no declared lane is both ready and confirmed. Either every declared lane was skipped for a reason printed above (its lane root folder, its plan or its project is missing), or each one that was ready was declined at the prompt. Backend lane declared {{be_declared}}, ready {{be_runnable}}; frontend lane declared {{fe_declared}}, ready {{fe_runnable}}. Fix the reasons named above, or re-run and confirm a lane."
680
709
 
681
710
  # 8. EACH GENERATING LANE'S authoring MODE — the run's mode where the lane is going, and
682
711
  # `skip` where it is not, which silences both of that lane's branches without a
@@ -754,15 +783,15 @@ workflow:
754
783
  - { name: fe_existing_count, from: "$.count" }
755
784
 
756
785
  # 9. Each generating lane's code-generation rule files → sticky standards (configuration,
757
- # not hardcoded). Loaded only for a lane that is actually going to generate, and the
758
- # backend's heading is keyed by mode (see the header). No blocking gate: an
786
+ # not hardcoded). Loaded only for a lane that is actually going to generate, under
787
+ # the same heading on both paths (see the header). No blocking gate: an
759
788
  # unconfigured project soft-continues, since the constitution halves still carry each
760
789
  # lane's ratified base.
761
790
  #
762
791
  # EACH LANE'S RULES ARE LOADED BESIDE ITS OWN STEPS, AND THE SECOND LANE REPLACES
763
792
  # THE FIRST'S. Both includes used to run here, before either lane generated — and
764
793
  # sticky standards only accumulate, so on a full-stack run the backend generator was
765
- # shown `## /aw-spec-implement-ui` and the frontend generator the service set. Each
794
+ # shown `## /aw-spec-implement-frontend` and the frontend generator the service set. Each
766
795
  # prompt says which lane it is in, but it said so inside an envelope that contradicted
767
796
  # it, and the envelope is the part the model cannot argue with. The frontend include
768
797
  # now sits immediately before the FRONTEND sections below and passes replace, which
@@ -771,7 +800,7 @@ workflow:
771
800
  condition: "{{be_go}} == true"
772
801
  action: include
773
802
  module: .appweaver/orchestrator/modules/tech-select.orch.yaml
774
- with: { command: "{{be_tech_command}}" }
803
+ with: { command: aw-spec-implement-backend }
775
804
 
776
805
  # 10. Pre-execution hooks — see the hook-phase vars above.
777
806
  - id: resolve_pre_hooks
@@ -891,6 +920,11 @@ workflow:
891
920
  Cross-Cutting Concerns table (audit / retention / data-exposure — the CN-/DC-
892
921
  realisations); and each integration contract (IC-) in the External Integrations
893
922
  table. Implement every port the plan declares.
923
+ - GIVE EVERY ENDPOINT ITS OPERATION ID. Where the plan names an endpoint's
924
+ **Operation id**, set it as this stack's published API document names operations
925
+ (its OpenAPI operationId), spelled exactly as the plan writes it. The frontend lane
926
+ finds the endpoint by that id in the document exported from this code, so an
927
+ endpoint left on a framework-generated name is one it cannot find.
894
928
  - Author a paired test for every plan behaviour / use-case / endpoint; derive the
895
929
  cases from the plan's pre/post/invariant contracts and cite the spec id (FR / BR /
896
930
  CN / DC / IC / US) from its Traceability section. Include tests for the constraints
@@ -1041,6 +1075,10 @@ workflow:
1041
1075
  invariant, handler orchestration, API error contract, the Cross-Cutting Concerns
1042
1076
  realisations (audit / retention / data-exposure — CN-/DC-) and the External
1043
1077
  Integrations table's contracts (IC-) exactly as the delta plan states.
1078
+ - GIVE EVERY ENDPOINT THIS CHANGE ADDS OR CHANGES the operation id the delta plan
1079
+ names, set as this stack's published API document names operations (its OpenAPI
1080
+ operationId) and spelled exactly; an existing endpoint keeps the id it has. The
1081
+ frontend lane finds each endpoint by that id in the exported document.
1044
1082
  - COMPONENTS: if a delta element depends on a framework component, code against its
1045
1083
  ABSTRACTION / port as the delta plan declares — do NOT hand-roll it. Installing the
1046
1084
  component (packages, wiring, config) is /appweaver add-component <Component>; if
@@ -1140,7 +1178,8 @@ workflow:
1140
1178
  under a different name is renamed to the plan's name, with its references — the
1141
1179
  plan's names are the ubiquitous language.
1142
1180
  - An element the plan names by intent (a port, an adapter, an endpoint route) is done
1143
- when the code realises it, whatever the framework calls it; report it `done`.
1181
+ when the code realises it, whatever the framework calls it; report it `done`. An
1182
+ endpoint still carries the operation id the plan names, set exactly.
1144
1183
  - If an element cannot be realised, do not work around it: report it `blocked` and
1145
1184
  say what stops you. A plan that is wrong is fixed in plan / analyze-plan.
1146
1185
  - Report every listed element in an `elements` list in your closing JSON block:
@@ -1165,7 +1204,7 @@ workflow:
1165
1204
  # recorded). Without the union a backend run would erase the frontend lane's source_files
1166
1205
  # and vice-versa, and either lane may have run first. ⌛ The trade-off is that a file this
1167
1206
  # lane DELETED stays listed until something prunes it; a lane-scoped replace needs
1168
- # concept-upsert to learn about lane roots, which is a script change and not built.
1207
+ # concept-upsert to learn about lane root folders, which is a script change and not built.
1169
1208
  - id: xref_concept_backend
1170
1209
  condition: "{{be_go}} == true"
1171
1210
  action: execute_script
@@ -1185,13 +1224,156 @@ workflow:
1185
1224
  outputs:
1186
1225
  - { name: okf_concept, from: "$.concept" }
1187
1226
 
1227
+ # ══════════════ THE API CONTRACT — the built service, as the frontend sees it ═══
1228
+ # The frontend binds to the service through its published API document, and the document
1229
+ # that counts is the one the CODE serves: routes, verbs, parameters, field names and
1230
+ # status codes as built, which the plans only describe. So before the frontend lane
1231
+ # generates, three steps, each of which WARNS and carries on — the frontend still
1232
+ # generates, from its plan, when one of them cannot run:
1233
+ # 1. EXPORT — start the service with its lane's `contract_run`, save what it serves at
1234
+ # `contract` under its lane root folder, stop it (contract-export). The two values
1235
+ # come from the lane in stacks.json — where foundation records a pack's own — or
1236
+ # else from the stack profile.
1237
+ # 2. CLIENT — run the frontend lane's `client` command, which generates the client from
1238
+ # that document. A lane with none yet asks the developer once and records the
1239
+ # answer on the lane; an empty answer carries on without a generated client.
1240
+ # 3. CHECK — every operation id the frontend plan cites must be in the document
1241
+ # (contract-check, by script).
1242
+ # It runs whenever the frontend lane does, including when the service was built in an
1243
+ # earlier run, and only where a service lane is declared.
1244
+ - id: contract_arm
1245
+ condition: "{{fe_go}} == true"
1246
+ action: set
1247
+ vars: { contract_go: "{{be_declared}}" }
1248
+ - id: contract_export
1249
+ condition: "{{contract_go}} == true"
1250
+ action: execute_script
1251
+ on_error: continue
1252
+ script: { ref: actions }
1253
+ subcommand: contract-export
1254
+ outputs:
1255
+ - { name: contract_ok, from: "$.ok" }
1256
+ - { name: contract_doc, from: "$.doc" }
1257
+ - { name: contract_doc_exists, from: "$.doc_exists" }
1258
+ - { name: contract_reason, from: "$.reason" }
1259
+ - { name: contract_already, from: "$.already_running" }
1260
+ - { name: contract_ops, from: "$.operations" }
1261
+ - { name: contract_log, from: "$.log_tail" }
1262
+ - { name: contract_placeholder, from: "$.placeholder" }
1263
+ - { name: contract_export_status, from: "$.status" }
1264
+ - id: warn_contract_export
1265
+ condition: "{{contract_go}} == true"
1266
+ action: verify_context
1267
+ require_true: contract_ok
1268
+ on_false: continue
1269
+ 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. Until then the frontend lane binds from its plan alone. Service output: {{contract_log}}"
1270
+ - id: contract_stale_flag
1271
+ condition: "{{contract_already}} == true"
1272
+ action: set
1273
+ vars: { contract_fresh: "false" }
1274
+ - id: warn_contract_stale
1275
+ condition: "{{contract_go}} == true"
1276
+ action: verify_context
1277
+ require_true: contract_fresh
1278
+ on_false: continue
1279
+ 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."
1280
+ - id: contract_status_set
1281
+ condition: "{{contract_doc_exists}} == true"
1282
+ action: set
1283
+ vars: { contract_status: "{{contract_export_status}}; it is in context" }
1284
+
1285
+ # 2. THE CLIENT, generated from the document — so only once there is one.
1286
+ - id: client_arm
1287
+ condition: "{{contract_doc_exists}} == true"
1288
+ action: set
1289
+ vars: { client_go: "{{fe_go}}" }
1290
+ - id: client_ask_arm
1291
+ condition: "{{fe_client}} == "
1292
+ action: set
1293
+ vars: { client_ask: "{{client_go}}" }
1294
+ - id: ask_client
1295
+ condition: "{{client_ask}} == true"
1296
+ action: ask
1297
+ prompt: |
1298
+ The frontend lane `{{fe_lane_id}}` ({{fe_title}}) has no `client` command, so its API
1299
+ client cannot be generated from the service's document ({{contract_doc}}).
1300
+ Type the command that generates it. It runs in `{{fe_workdir}}`, and
1301
+ {{contract_placeholder}} in it stands for the document's path — for example:
1302
+ npx openapi-typescript {{contract_placeholder}} -o lib/api/schema.d.ts
1303
+ It is saved on the lane in .appweaver/stacks.json and used on every run after this one.
1304
+ Leave it empty to carry on without a generated client.
1305
+ answer_var: client_answer
1306
+ - id: client_save
1307
+ condition: "{{client_ask}} == true"
1308
+ action: execute_script
1309
+ on_error: continue
1310
+ script: { ref: actions }
1311
+ subcommand: lane-set
1312
+ args: ["--id", "{{fe_lane_id}}", "--set", "client={{client_answer}}"]
1313
+ outputs:
1314
+ - { name: client_saved, from: "$.written" }
1315
+ - id: client_saved_set
1316
+ condition: "{{client_saved}} == true"
1317
+ action: set
1318
+ vars: { fe_client: "{{client_answer}}" }
1319
+ - id: client_run_arm
1320
+ condition: "{{fe_client}} != "
1321
+ action: set
1322
+ vars: { client_run: "{{client_go}}" }
1323
+ - id: contract_client
1324
+ condition: "{{client_run}} == true"
1325
+ action: execute_script
1326
+ on_error: continue
1327
+ script: { ref: actions }
1328
+ subcommand: contract-client
1329
+ outputs:
1330
+ - { name: client_ok, from: "$.ok" }
1331
+ - { name: client_cmd, from: "$.cmd" }
1332
+ - { name: client_log, from: "$.failing_excerpt" }
1333
+ - id: warn_client_failed
1334
+ condition: "{{client_run}} == true"
1335
+ action: verify_context
1336
+ require_true: client_ok
1337
+ on_false: continue
1338
+ on_false_message: "API CLIENT NOT GENERATED — the frontend lane's `client` command failed: {{client_log}}. Correct `client` on the `{{fe_lane_id}}` lane in .appweaver/stacks.json and re-run implement. Until then the frontend lane writes its calls from the API document."
1339
+ - id: client_status_set
1340
+ condition: "{{client_ok}} == true"
1341
+ action: set
1342
+ vars:
1343
+ client_present: "true"
1344
+ client_status: "generated just now by `{{client_cmd}}` from the API document — call the service THROUGH it, and never edit a generated file"
1345
+ - id: warn_no_client
1346
+ condition: "{{client_go}} == true"
1347
+ action: verify_context
1348
+ require_true: client_present
1349
+ on_false: continue
1350
+ 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."
1351
+
1352
+ # 3. THE CHECK — the operation ids the frontend plan cites, against the document.
1353
+ - id: contract_check
1354
+ condition: "{{contract_doc_exists}} == true"
1355
+ action: execute_script
1356
+ on_error: continue
1357
+ script: { ref: actions }
1358
+ subcommand: contract-check
1359
+ args: ["--plan", "{{fe_plan}}", "--doc", "{{contract_doc}}"]
1360
+ outputs:
1361
+ - { name: check_ok, from: "$.ok" }
1362
+ - { name: check_reason, from: "$.reason" }
1363
+ - id: warn_contract_check
1364
+ condition: "{{contract_doc_exists}} == true"
1365
+ action: verify_context
1366
+ require_true: check_ok
1367
+ on_false: continue
1368
+ 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, and the built service must serve that id. Either the plan cites the wrong id (fix it with /appweaver plan, then analyze-plan) or the service never gave the endpoint that id (re-run implement for the backend lane). The frontend lane still generates; these bindings are where a call will fail."
1369
+
1188
1370
  # ══════════════ THE LANE SWAP — the frontend's rules replace the backend's ═══
1189
1371
  # build_context already scoped the two lanes' PAYLOADS apart; this is the same boundary
1190
1372
  # for their STANDARDS, which the envelope carries and which nothing used to reset. It
1191
1373
  # runs here rather than up with the backend's include because standards accumulate:
1192
1374
  # loading both before either lane generated is precisely what showed each generator the
1193
1375
  # other lane's rule set. `replace` resets to the declared seed (ai-rules stays) and then
1194
- # loads `## /aw-spec-implement-ui`, so from here down the envelope is the frontend's.
1376
+ # loads `## /aw-spec-implement-frontend`, so from here down the envelope is the frontend's.
1195
1377
  #
1196
1378
  # Nothing after the frontend sections regenerates backend code — the OKF steps that
1197
1379
  # follow run include_standards: false — so the swap needs no undo.
@@ -1199,12 +1381,13 @@ workflow:
1199
1381
  condition: "{{fe_go}} == true"
1200
1382
  action: include
1201
1383
  module: .appweaver/orchestrator/modules/tech-select.orch.yaml
1202
- with: { command: aw-spec-implement-ui, replace: "true" }
1384
+ with: { command: aw-spec-implement-frontend, replace: "true" }
1203
1385
 
1204
1386
  # ═══════════ FRONTEND LANE — bootstrap, generate from the MASTER plan ════════
1205
1387
  # The experience twin of the service generation, one lane over. Its own build_context,
1206
1388
  # because the envelope persists across steps and the two lanes must not see each other's
1207
1389
  # payload — the frontend binds to the service's PUBLISHED surface, never its internals.
1390
+ # That surface is the API document the contract block exported, when there is one.
1208
1391
  - id: ctx_frontend
1209
1392
  condition: "{{fe_mode}} == bootstrap"
1210
1393
  action: build_context
@@ -1212,6 +1395,7 @@ workflow:
1212
1395
  - { path: "{{fe_plan}}" }
1213
1396
  - { path: "{{domain_model}}", optional: true } # the UBIQUITOUS LANGUAGE this lane's labels, states and messages use — and what a cited BR-nnn requires
1214
1397
  - { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's ratified UX-* base — conventions, forbidden patterns, gates
1398
+ - { path: "{{contract_doc}}", optional: true } # the service's API document as BUILT — what every §5 binding calls
1215
1399
  - id: gen_frontend
1216
1400
  condition: "{{fe_mode}} == bootstrap"
1217
1401
  action: agent_generate
@@ -1305,6 +1489,17 @@ workflow:
1305
1489
  semantics and rollback each row states.
1306
1490
  • §6 — the cross-cutting decisions as code: what is hidden vs disabled,
1307
1491
  accessibility obligations, localization, performance and offline behaviour.
1492
+ - THE SERVICE'S API DOCUMENT IS THE CONTRACT. API document: {{contract_status}}.
1493
+ Each §5 row names its endpoint by Operation id; find that operation in the
1494
+ document and take EVERYTHING about the call from it — route, verb, path and query
1495
+ parameters, request and response field names and their casing, status codes and
1496
+ error codes. Never take a route, a field name or a paging convention from memory,
1497
+ from convention or from the loaded standards' examples: the document is what the
1498
+ built service actually answers. A §5 row whose id the document does not have is a
1499
+ broken binding — realise the screen, leave that call unbound, report the element
1500
+ `blocked` naming the id, and never guess a substitute.
1501
+ - THE CLIENT: {{client_status}}. The client lives in this lane's client layer; a
1502
+ generated client is called, never edited.
1308
1503
  - THE CLIENT IS NOT AN AUTHORITY. A client-side validation or permission check
1309
1504
  restates a server rule for feedback; it never replaces it. Generate no server-side
1310
1505
  behaviour, no persistence and no business rule evaluation here.
@@ -1346,6 +1541,7 @@ workflow:
1346
1541
  - { path: "{{frontend_plan}}", optional: true } # the master plan — READ ONLY, never amended here
1347
1542
  - { path: "{{domain_model}}", optional: true } # the master model — the ubiquitous language as it stands BEFORE this change
1348
1543
  - { path: .appweaver/memory/constitution.ui.md, optional: true }
1544
+ - { path: "{{contract_doc}}", optional: true } # the service's API document as BUILT — what every binding row calls
1349
1545
  - id: gen_frontend_delta
1350
1546
  condition: "{{fe_mode}} == delta"
1351
1547
  action: agent_generate
@@ -1425,6 +1621,15 @@ workflow:
1425
1621
  service delta's "Contract deltas for the frontend lane" table as the delta plan
1426
1622
  cites them. Generate no server-side behaviour, no persistence and no business rule
1427
1623
  evaluation.
1624
+ - THE SERVICE'S API DOCUMENT IS THE CONTRACT. API document: {{contract_status}}.
1625
+ Each binding row leads with its endpoint's operation id; find that operation in
1626
+ the document and take EVERYTHING about the call from it — route, verb, path and
1627
+ query parameters, request and response field names and their casing, status
1628
+ codes and error codes — never from memory, convention or the standards' examples.
1629
+ A binding whose id the document does not have is broken: leave that call unbound,
1630
+ report the element `blocked` naming the id, and never guess a substitute.
1631
+ - THE CLIENT: {{client_status}}. The client lives in this lane's client layer; a
1632
+ generated client is called, never edited.
1428
1633
  - Author a paired test for every ADDED/MODIFIED screen, interaction and state,
1429
1634
  including the unwanted-behaviour states and an accessibility assertion per
1430
1635
  interactive surface, citing the requirement id THE DELTA PLAN NAMES for that row
@@ -1499,6 +1704,10 @@ workflow:
1499
1704
  plan's names are the ubiquitous language.
1500
1705
  - A screen or a binding is done when the code realises it, whatever its route or
1501
1706
  file is called; report it `done`.
1707
+ - A binding calls the operation its id names in the service's API document
1708
+ ({{contract_status}}), taking route, verb, parameters, field names and status codes
1709
+ from there, through the client ({{client_status}}). An id the document does not
1710
+ have stays `blocked`; never guess a substitute.
1502
1711
  - If an element cannot be realised, do not work around it: report it `blocked` and
1503
1712
  say what stops you. A plan that is wrong is fixed in plan / analyze-plan.
1504
1713
  - Report every listed element in an `elements` list in your closing JSON block:
@@ -1554,7 +1763,7 @@ workflow:
1554
1763
  action: verify_context
1555
1764
  require_true: lanes_complete
1556
1765
  on_false: stop
1557
- on_false_message: "PARTIAL GENERATION — implement did not realise every plan element, even after one completion pass. Backend lane: {{be_gaps_text}}. Frontend lane: {{fe_gaps_text}}. Everything written so far is kept and recorded in the OKF concept. An element reported `blocked` names what stops it — a plan that is wrong is fixed with /appweaver plan (then analyze-plan or analyze-plan-delta). An element with no code under the lane's source path is still to be written, or was written under a name that is not the plan's: re-run /appweaver implement to finish, which extends existing files rather than regenerating them."
1766
+ on_false_message: "PARTIAL GENERATION — implement did not realise every plan element, even after one completion pass. Backend lane: {{be_gaps_text}}. Frontend lane: {{fe_gaps_text}}. Everything written so far is kept and recorded in the OKF concept. An element reported `blocked` names what stops it — a plan that is wrong is fixed with /appweaver plan (then analyze-plan or analyze-plan-delta). An element with no code under the lane root folder is still to be written, or was written under a name that is not the plan's: re-run /appweaver implement to finish, which extends existing files rather than regenerating them."
1558
1767
 
1559
1768
  # 11. CROSS-SLICE EDGES — a Cross-References row written into every SIBLING aggregate
1560
1769
  # this one uses, read off the master backend plan (§1 References-by-id + its External
@@ -1691,5 +1900,5 @@ outputs:
1691
1900
  plan_gate_at: "{{gate_at}}"
1692
1901
  plan_gate_verdict: "{{gate_verdict}}"
1693
1902
  plan_gate_routed_to_specify: "{{gate_routed_csv}}" # open spec/model gaps this code does not cover
1694
- note: "Lanes come from .appweaver/stacks.json — backend declared {{be_declared}} (ready {{be_runnable}}, implemented {{be_go}}), frontend declared {{fe_declared}} (ready {{fe_runnable}}, implemented {{fe_go}}). 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 source path, which is the correct result of re-running implement on a finished feature and not a failure. On the bootstrap path the plans were checked against analyze-plan's receipt before anything generated (tracked={{gate_tracked}}, fresh={{gate_fresh}}, gated {{gate_at}}) — tracked=false means this feature 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."
1903
+ note: "Lanes come from .appweaver/stacks.json — backend declared {{be_declared}} (ready {{be_runnable}}, implemented {{be_go}}), frontend declared {{fe_declared}} (ready {{fe_runnable}}, implemented {{fe_go}}). 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 the plans were checked against analyze-plan's receipt before anything generated (tracked={{gate_tracked}}, fresh={{gate_fresh}}, gated {{gate_at}}) — tracked=false means this feature 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."
1695
1904
  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."