aw-spec-cli 0.1.33 → 0.1.35

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
@@ -24,6 +24,18 @@
24
24
  # builds green, passes unit tests that construct their subject directly,
25
25
  # and fails on the first request. Fixed up to 2×.
26
26
  # 3. KNOWLEDGE refresh + review the slice's OKF concept (advisory).
27
+ # 4. TRACEABILITY does a test of the slice NAME every FR- and BR- its plans trace? Green
28
+ # tests cannot show a requirement whose test was never written, so each
29
+ # working lane's plan is read for its ids and the slice's test files for
30
+ # citations. It warns, and the gate fails at the END, like the no-tests case.
31
+ # 5. API does the built service serve the endpoints its API plan publishes?
32
+ # The frontend lane is bound to that API (api-draft.json), so the
33
+ # service's as-built document is written the way the lane says and
34
+ # compared with it. A planned endpoint not served, or served under another
35
+ # route, verb or operation id, fails the gate at the END; a field or status
36
+ # code that differs is reported. A lane that sets no way to write the
37
+ # document is not checked, and the run says so. A feature whose plan
38
+ # names no endpoint is not checked either, and the document is not written.
27
39
  # Every fix made here is recorded in that concept, which is also where the slice's
28
40
  # file list is READ from (2.3.0 — `aw-xref concept-paths`, replacing the retired
29
41
  # `design-paths` scan for ```lang path=… fences that the tech-agnostic backend-plan.md no
@@ -61,9 +73,11 @@
61
73
  # gate asks of the whole spec, narrowed to one change's ids and answered while the delta is
62
74
  # still the only document that carries them. This gate cannot substitute for it: it is
63
75
  # test-driven, and a requirement that was never implemented leaves NO test rather than a
64
- # failing one, so nothing here fails on it. What this gate does that its twin cannot is
65
- # score the WHOLE slice, against masters that carry the change, with the fix loops and the
66
- # referrer blast radius.
76
+ # failing one. Pass 4 catches the part of that a script can see — an FR- or BR- the master
77
+ # plans trace that no test names — but a change's NEW ids reach the master plans only when
78
+ # plan re-derives them, after this gate, so its twin checks those. What this gate does that
79
+ # its twin cannot is score the WHOLE slice, against masters that carry the change, with the
80
+ # fix loops and the referrer blast radius.
67
81
  #
68
82
  # THE WORKING LANE. Only the lanes the developer on this machine works on (.appweaver/
69
83
  # lane.json — select-stack writes the default, select-lane narrows it) are tested, fixed and
@@ -77,8 +91,8 @@
77
91
  # =============================================================================
78
92
 
79
93
  name: aw-spec-analyze-slice
80
- description: The design↔code conformance gate — run the slice's tests against its acceptance criteria, check the code against the domain gate overlays AND the bounded context's domain-model.md (every invariant the slice's own aggregate section states must be enforced as written; one another aggregate owns is reported, not blocking), verify the change did not break any referring aggregate (the OKF referrer blast radius) and write a triage report before stopping if it did, refresh + review its OKF concept, and record every fix in it. Sits after finalize-delta on the change path, so the two masters it scores against already carry the change, and before close-delta, so a red gate leaves the change open and recoverable.
81
- version: 2.7.0
94
+ description: The design↔code conformance gate — run the slice's tests against its acceptance criteria, check the code against the domain gate overlays AND the bounded context's domain-model.md (every invariant the slice's own aggregate section states must be enforced as written; one another aggregate owns is reported, not blocking), verify the change did not break any referring aggregate (the OKF referrer blast radius) and write a triage report before stopping if it did, refresh + review its OKF concept, record every fix in it, and check that a test of the slice names every FR- and BR- its lane plans trace (a requirement whose test was never written leaves the suite green; a reverse-engineered plan's gaps are reported, not blocking), and check that the built service serves every endpoint its API plan publishes — the frontend lane is bound to the plan, never to the running service — by writing the service's as-built API document the way the lane says and comparing the two (a missing or moved endpoint fails; a differing field is reported). Sits after finalize-delta on the change path, so the two masters it scores against already carry the change, and before close-delta, so a red gate leaves the change open and recoverable.
95
+ version: 2.11.0
82
96
  agent_agnostic: true
83
97
  dialect: aw-spec.orchestrator/v1
84
98
 
@@ -157,6 +171,46 @@ vars:
157
171
  # readable in the outputs even when a step is skipped or errors out.
158
172
  okf_ok: "unknown"
159
173
  okf_orphans: "0"
174
+ # Pass 4 (traceability). A lane votes into trace_ok only when it is worked, and its vote
175
+ # is its check's own `ok` — a check that never answered votes not met.
176
+ fe_working: "false"
177
+ be_test_path: ""
178
+ fe_source_path: ""
179
+ fe_test_path: ""
180
+ frontend_plan: ""
181
+ be_trace_ok: "false"
182
+ be_trace_clean: "false"
183
+ be_trace_warning: ""
184
+ be_untraced: "not checked — no backend lane is worked here"
185
+ be_trace_vote: "true"
186
+ fe_trace_ok: "false"
187
+ fe_trace_clean: "false"
188
+ fe_trace_warning: ""
189
+ fe_untraced: "not checked — no frontend lane is worked here"
190
+ fe_trace_vote: "true"
191
+ trace_ok: "false"
192
+ # Pass 5 — the API check of the service lane. A refusing default for the verdict: it is
193
+ # raised to the check's `ok` only when the check ran, and stays "true" when this
194
+ # developer does not work on the service lane or the lane sets no way to write its
195
+ # document — both of which warn instead — or the plan names no endpoint to check.
196
+ api_needed: "false" # contract-verify --check: the plan names something to check
197
+ api_plan_note: "" # why it names nothing, when it does not
198
+ api_way: ""
199
+ api_doc: ""
200
+ api_placeholder: ""
201
+ api_export_ok: "false"
202
+ api_export_reason: ""
203
+ api_export_log: ""
204
+ api_has_way: "true"
205
+ api_export_vote: "true"
206
+ api_export_clean: "true"
207
+ api_ok: ""
208
+ api_checked: "false"
209
+ api_issues: "not checked — no as-built API document was written in this run"
210
+ api_warning: ""
211
+ api_note: ""
212
+ api_clean: "true"
213
+ api_vote: "true"
160
214
 
161
215
  workflow:
162
216
 
@@ -181,6 +235,7 @@ workflow:
181
235
  - { name: feature_dir, from: "$.feature_dir" }
182
236
  - { name: spec_file, from: "$.spec_file" }
183
237
  - { name: backend_plan, from: "$.backend_plan" } # the ports + §4.3 wiring the composition pass reads
238
+ - { name: frontend_plan, from: "$.frontend_plan" } # its §7 Traceability, read by pass 4
184
239
 
185
240
  - id: require_feature
186
241
  action: verify_context
@@ -270,6 +325,10 @@ workflow:
270
325
  - { name: working_root, from: "$.working_root" }
271
326
  - { name: working_test_path, from: "$.working_test_path" }
272
327
  - { name: be_working, from: "$.backend_working" }
328
+ - { name: fe_working, from: "$.frontend_working" }
329
+ - { name: be_test_path, from: "$.backend_test_path" }
330
+ - { name: fe_source_path, from: "$.frontend_source_path" }
331
+ - { name: fe_test_path, from: "$.frontend_test_path" }
273
332
 
274
333
  # The slice's source files, from its OKF concept — the context all three passes below
275
334
  # work over (test fix loop, integrity triage, conformance). `--under` keeps the working
@@ -444,13 +503,38 @@ workflow:
444
503
  # forbidden-patterns / refusal conditions / CI gates). The overlays load as the
445
504
  # checklist — VALIDATION only, never an authoring input to feature code. No-op for a
446
505
  # non-domain project. Fix violations (bounded 2×); the gate stops if any remain.
506
+ # A non-domain project's map holds the heading with a `_None_` line; a map with no such
507
+ # heading, or one naming a file that is not on disk, warns and carries on.
447
508
  - id: resolve_gate_overlays
448
509
  action: execute_script
449
510
  script: { ref: actions }
450
511
  subcommand: resolve-domain
451
512
  args: ["--command", "aw-spec-gate"]
452
513
  outputs:
453
- - { name: gate_overlays, from: "$.candidates" }
514
+ - { name: gate_overlays, from: "$.candidates" }
515
+ - { name: gate_domain, from: "$.domain" }
516
+ - { name: gate_map_exists, from: "$.rulemap_exists" }
517
+ - { name: gate_heading_found, from: "$.heading_found" }
518
+ - { name: gate_missing, from: "$.missing" }
519
+ - id: gate_flags
520
+ action: set
521
+ vars: { gate_all_on_disk: "true" }
522
+ - id: gate_flag_missing
523
+ condition: "{{gate_missing}} != []"
524
+ action: set
525
+ vars: { gate_all_on_disk: "false" }
526
+ - id: warn_gate_heading
527
+ condition: "{{gate_map_exists}} == true"
528
+ action: verify_context
529
+ require_true: gate_heading_found
530
+ on_false: continue
531
+ on_false_message: "WARNING — guardrails/{{gate_domain}}/domain-rule-bindings.md has no `## /aw-spec-gate` heading, so the code is checked against no domain checklist. If that is not intended, add the heading and its overlay files — it must read exactly `## /aw-spec-gate`; a project with no checklist keeps the heading with a `_None_` line."
532
+ - id: warn_gate_missing
533
+ condition: "{{gate_map_exists}} == true"
534
+ action: verify_context
535
+ require_true: gate_all_on_disk
536
+ on_false: continue
537
+ on_false_message: "WARNING — guardrails/{{gate_domain}}/domain-rule-bindings.md maps these checklist files under `## /aw-spec-gate`, but they are not on disk, so the code was not checked against them: {{gate_missing}}. Correct the paths, or add the files."
454
538
  - id: load_gate_overlays
455
539
  action: load_standards
456
540
  files_from_variable: gate_overlays
@@ -806,10 +890,187 @@ workflow:
806
890
  - { name: okf_ok, from: "$.ok" }
807
891
  - { name: okf_orphans, from: "$.orphan_count" }
808
892
 
809
- # THE LAST GATE — a slice with no tests fails HERE, not at PASS 1, so everything the
810
- # other passes found has already been reported and any fix they made is on disk. It sits
811
- # before the after-hooks, because an extension wired to `after_analyze_slice` runs on a
812
- # slice this gate has passed.
893
+ # ═══════ PASS 4 — TRACEABILITY: does a test name every requirement the plans trace? ═══════
894
+ # Green tests prove that what was written works; they cannot show what was never written.
895
+ # A requirement whose paired test was never generated leaves the suite green, so each
896
+ # working lane's plan is read for the FR- and BR- ids its Traceability section traces, and
897
+ # the slice's TEST files for each one — the concept's list plus every file the fix loops
898
+ # above wrote, which is why this pass runs last: a test the conformance pass added counts.
899
+ # A citation is the id in a test's name, a comment, an attribute or a tag (`FR-004`,
900
+ # `FR_004`, `FR004`). FR numbers restart in every feature, so only this slice's tests are
901
+ # read; a concept that lists none falls back to every test under the lane's test path.
902
+ # Two readings are kept apart from a gap: a plan adopt reverse-engineered is REPORTED and
903
+ # never blocks (its legacy tests predate the spec ids), and an id the plan still traces
904
+ # that spec.md or the model no longer defines — a change removed it and plan has not
905
+ # re-derived the master plan yet — is stale, not required. Like the no-tests case it warns
906
+ # here and fails at the end, after everything else has reported. What it proves is that
907
+ # each requirement has a test that NAMES it, not that the test asserts what it says.
908
+ - id: trace_backend
909
+ condition: "{{be_working}} == true"
910
+ action: execute_script
911
+ on_error: continue
912
+ script: { ref: actions }
913
+ subcommand: test-citations
914
+ args: ["--plan", "{{backend_plan}}", "--kind", "backend", "--paths", "{{all_paths}}", "--paths", "{{fixed_files}}", "--paths", "{{conformance_fixes}}", "--paths", "{{wiring_fixes}}", "--source-path", "{{be_source_path}}", "--test-path", "{{be_test_path}}", "--spec", "{{spec_file}}", "--model", "{{domain_model}}"]
915
+ outputs:
916
+ - { name: be_trace_ok, from: "$.ok" }
917
+ - { name: be_trace_clean, from: "$.clean" }
918
+ - { name: be_trace_warning, from: "$.warning" }
919
+ - { name: be_untraced, from: "$.uncited_text" }
920
+ - id: warn_trace_backend
921
+ condition: "{{be_working}} == true"
922
+ action: verify_context
923
+ require_true: be_trace_clean
924
+ on_false: continue
925
+ on_false_message: "{{be_trace_warning}}"
926
+ - id: trace_frontend
927
+ condition: "{{fe_working}} == true"
928
+ action: execute_script
929
+ on_error: continue
930
+ script: { ref: actions }
931
+ subcommand: test-citations
932
+ args: ["--plan", "{{frontend_plan}}", "--kind", "frontend", "--paths", "{{all_paths}}", "--paths", "{{fixed_files}}", "--paths", "{{conformance_fixes}}", "--source-path", "{{fe_source_path}}", "--test-path", "{{fe_test_path}}", "--spec", "{{spec_file}}", "--model", "{{domain_model}}"]
933
+ outputs:
934
+ - { name: fe_trace_ok, from: "$.ok" }
935
+ - { name: fe_trace_clean, from: "$.clean" }
936
+ - { name: fe_trace_warning, from: "$.warning" }
937
+ - { name: fe_untraced, from: "$.uncited_text" }
938
+ - id: warn_trace_frontend
939
+ condition: "{{fe_working}} == true"
940
+ action: verify_context
941
+ require_true: fe_trace_clean
942
+ on_false: continue
943
+ on_false_message: "{{fe_trace_warning}}"
944
+
945
+ # trace_ok = every worked lane's check passed — a raise and two knock-downs, each lane
946
+ # voting only when it is worked.
947
+ - id: be_trace_vote_set
948
+ condition: "{{be_working}} == true"
949
+ action: set
950
+ vars: { be_trace_vote: "{{be_trace_ok}}" }
951
+ - id: fe_trace_vote_set
952
+ condition: "{{fe_working}} == true"
953
+ action: set
954
+ vars: { fe_trace_vote: "{{fe_trace_ok}}" }
955
+ - id: trace_seed
956
+ action: set
957
+ vars: { trace_ok: "true" }
958
+ - id: trace_clear_backend
959
+ condition: "{{be_trace_vote}} != true"
960
+ action: set
961
+ vars: { trace_ok: "false" }
962
+ - id: trace_clear_frontend
963
+ condition: "{{fe_trace_vote}} != true"
964
+ action: set
965
+ vars: { trace_ok: "false" }
966
+
967
+ # ═══════ PASS 5 — API: does the built service serve what its API plan publishes? ═══════
968
+ # The frontend lane binds to the API plan — api-draft.json, drafted by script from
969
+ # api-plan.md, or from the backend plan's API section for a feature planned before API
970
+ # plans existed — and never to the running service, so the code is held to the plan here.
971
+ # The service's as-built document is written the way the lane says (contract-export:
972
+ # `contract_write`, or `contract_run` + `contract`), after every fix loop above, to the
973
+ # lane's contracts/openapi.json, and compared with the operations THIS feature's API plan
974
+ # names, plus a change merged into spec.md and not closed yet (contract-verify).
975
+ # finalize-delta merges such a change into api-plan.md as well, and applying it again over
976
+ # the merged plan changes nothing. Runs for the developer who works on the service lane;
977
+ # the frontend developer's run never starts it.
978
+ #
979
+ # ONLY WHEN THERE IS SOMETHING TO CHECK. contract-verify --check reads what the check
980
+ # would (the draft and the merged, open changes) without the as-built document; a
981
+ # feature that plans no endpoint — a worker, a batch job, domain logic only — never runs
982
+ # the lane's export or its warnings. Only an explicit `needed: false` skips: a check that
983
+ # does not answer leaves the export armed.
984
+ - id: api_plan_check
985
+ condition: "{{be_working}} == true"
986
+ action: execute_script
987
+ on_error: continue
988
+ script: { ref: actions }
989
+ subcommand: contract-verify
990
+ args: ["--feature-dir", "{{feature_dir}}", "--check"]
991
+ outputs:
992
+ - { name: api_needed, from: "$.needed" }
993
+ - { name: api_plan_note, from: "$.note" }
994
+ - id: api_nothing_to_check
995
+ condition: "{{api_plan_note}} != "
996
+ action: set
997
+ vars: { api_issues: "{{api_plan_note}}" }
998
+ - id: api_export
999
+ condition: "{{api_needed}} != false"
1000
+ action: execute_script
1001
+ on_error: continue
1002
+ script: { ref: actions }
1003
+ subcommand: contract-export
1004
+ outputs:
1005
+ - { name: api_export_ok, from: "$.ok" }
1006
+ - { name: api_way, from: "$.way" }
1007
+ - { name: api_export_reason, from: "$.reason" }
1008
+ - { name: api_export_log, from: "$.log_tail" }
1009
+ - { name: api_doc, from: "$.doc" }
1010
+ - { name: api_placeholder, from: "$.placeholder" }
1011
+ - id: api_no_way_flag
1012
+ condition: "{{api_way}} == none"
1013
+ action: set
1014
+ vars: { api_has_way: "false" }
1015
+ - id: warn_api_no_way
1016
+ condition: "{{api_needed}} != false"
1017
+ action: verify_context
1018
+ require_true: api_has_way
1019
+ on_false: continue
1020
+ on_false_message: "API NOT CHECKED — {{api_export_reason}}. The frontend lane binds to the endpoints the API plan publishes (api-draft.json); without the service's as-built document nothing checks that the code serves them. `contract_write` is any command that writes the service's OpenAPI document to the path {{api_placeholder}} stands for — the stack's own generator, or a script that loads the application and dumps its document — with no server started."
1021
+ # An export that was attempted and failed — a no-way lane has already been told.
1022
+ - id: api_export_vote_set
1023
+ condition: "{{api_needed}} != false"
1024
+ action: set
1025
+ vars: { api_export_vote: "{{api_export_ok}}" }
1026
+ - id: api_export_failed
1027
+ condition: "{{api_export_vote}} != true"
1028
+ action: set
1029
+ vars: { api_export_clean: "false" }
1030
+ - id: api_export_no_way
1031
+ condition: "{{api_has_way}} == false"
1032
+ action: set
1033
+ vars: { api_export_clean: "true" }
1034
+ - id: warn_api_export
1035
+ condition: "{{api_needed}} != false"
1036
+ action: verify_context
1037
+ require_true: api_export_clean
1038
+ on_false: continue
1039
+ on_false_message: "API NOT CHECKED — the service's as-built API document could not be written: {{api_export_reason}}. The code compiled and its tests ran; the way the lane writes its document is what failed. Correct `contract_write` (or `contract_run` / `contract`) on the service lane in .appweaver/stacks.json, start anything it needs, and re-run analyze-slice. Output: {{api_export_log}}"
1040
+ - id: api_verify
1041
+ condition: "{{api_export_ok}} == true"
1042
+ action: execute_script
1043
+ on_error: continue
1044
+ script: { ref: actions }
1045
+ subcommand: contract-verify
1046
+ args: ["--feature-dir", "{{feature_dir}}"]
1047
+ outputs:
1048
+ - { name: api_ok, from: "$.ok" }
1049
+ - { name: api_checked, from: "$.checked" }
1050
+ - { name: api_issues, from: "$.issues_text" }
1051
+ - { name: api_warning, from: "$.warning" }
1052
+ - { name: api_note, from: "$.note" }
1053
+ - id: api_warning_flag
1054
+ condition: "{{api_warning}} != "
1055
+ action: set
1056
+ vars: { api_clean: "false" }
1057
+ - id: warn_api_fields
1058
+ condition: "{{api_export_ok}} == true"
1059
+ action: verify_context
1060
+ require_true: api_clean
1061
+ on_false: continue
1062
+ on_false_message: "{{api_warning}}"
1063
+ - id: api_vote_set
1064
+ condition: "{{api_export_ok}} == true"
1065
+ action: set
1066
+ vars: { api_vote: "{{api_ok}}" }
1067
+
1068
+ # THE LAST THREE GATES — a slice with no tests fails HERE, not at PASS 1, and one whose
1069
+ # tests leave a requirement unnamed fails at the next, and one whose service does not serve
1070
+ # its plan's API at the last, so everything the other passes
1071
+ # found has already been reported and any fix they made is on disk. They sit before the
1072
+ # after-hooks, because an extension wired to `after_analyze_slice` runs on a slice these
1073
+ # gates have passed.
813
1074
  - id: untested_gate
814
1075
  action: verify_context
815
1076
  require_true: tests_present
@@ -826,6 +1087,38 @@ workflow:
826
1087
  lane declares — or, if the tests exist, name them so each carries its aggregate's
827
1088
  name, which is what the slice filter selects on. Then re-run /appweaver analyze-slice.
828
1089
 
1090
+ # THE TRACEABILITY GATE — pass 4's verdict, last for the same reason as the one above.
1091
+ - id: untraced_gate
1092
+ action: verify_context
1093
+ require_true: trace_ok
1094
+ on_false: stop
1095
+ on_false_message: >
1096
+ The slice's tests are green, but they do not name every requirement its plans trace —
1097
+ so a requirement whose test was never written passes here unchecked. Named by no test:
1098
+ backend — {{be_untraced}}; frontend — {{fe_untraced}}. Everything above ran and
1099
+ passed (domain conformance={{conformance_ok}}, composition={{wiring_ok}}). Run
1100
+ /appweaver fix-test-coverage: it writes a test for each id, or names the id in the
1101
+ test that already exercises it (`FR-004`, `FR_004` or `FR004` in its name, a comment,
1102
+ an attribute or a tag), and never touches production code. Then re-run
1103
+ /appweaver analyze-slice.
1104
+
1105
+ # THE API GATE — pass 5's verdict.
1106
+ - id: api_gate
1107
+ action: verify_context
1108
+ require_true: api_vote
1109
+ on_false: stop
1110
+ on_false_message: >
1111
+ The slice's tests are green, but the built service does not serve what the API plan
1112
+ publishes — and the frontend lane is bound to it: {{api_issues}}. Everything
1113
+ above ran and passed (domain conformance={{conformance_ok}},
1114
+ composition={{wiring_ok}}, traceability={{trace_ok}}). Make the code serve each
1115
+ endpoint at the route, verb and operation id the API plan states — /appweaver
1116
+ implement generates what it names — then /appweaver build and re-run /appweaver
1117
+ analyze-slice. Where the API plan is what is wrong, correct it with /appweaver plan,
1118
+ then analyze-plan: that redraws the contract, and the frontend lane re-checks against
1119
+ it. On a change that is open and not merged yet, run /appweaver analyze-slice-delta
1120
+ <CR-id> instead — this gate checks the master API plan, plus a change already merged.
1121
+
829
1122
  - id: resolve_post_hooks
830
1123
  action: execute_script
831
1124
  script: { ref: actions }
@@ -861,6 +1154,19 @@ outputs:
861
1154
  # nothing on this run, and the gate failed at the end saying so.
862
1155
  tests_present: "{{tests_present}}"
863
1156
  tests_matched: "{{test_matched}}"
1157
+ # Pass 4: the FR-/BR- ids each worked lane's plan traces that no test of the slice names —
1158
+ # `none` when every one is named. A reverse-engineered plan's are reported, never blocking.
1159
+ traceability_ok: "{{trace_ok}}"
1160
+ backend_untraced: "{{be_untraced}}"
1161
+ frontend_untraced: "{{fe_untraced}}"
1162
+ # Pass 5: what the built service serves vs what the API plan publishes. `api_checked`
1163
+ # false means no as-built document was written — the lane sets no way, or it failed, and
1164
+ # the run warned — or the plan names no endpoint, which `backend_api_issues` then says;
1165
+ # `api_issues` is `none` when every planned endpoint is served as planned.
1166
+ api_ok: "{{api_vote}}"
1167
+ api_checked: "{{api_checked}}"
1168
+ backend_api_issues: "{{api_issues}}"
1169
+ api_document: "{{api_doc}}" # the as-built document, when api_checked — commit it with the service
864
1170
  referrer_count: "{{referrer_count}}"
865
1171
  referrer_blast_radius: "{{referrer_names}}"
866
1172
  okf_concept: "{{okf_concept}}"
@@ -36,18 +36,17 @@
36
36
  # and its fix loop is never handed the frontend's files. With the default (every declared
37
37
  # lane) every lane is compiled, as before.
38
38
  #
39
- # 1.5.0 — THE BACKEND PUBLISHES ITS API DOCUMENT. After a green compile, a developer who
40
- # works on the service lane exports its API document again (contract-export: start the
41
- # service, save what it serves under contracts/ in its lane root folder, stop it), so the
42
- # committed document follows the fixes this loop made. The frontend lane reads that
43
- # document and never starts the service. A failed export warns and the build stays green.
39
+ # NO API DOCUMENT IS WRITTEN HERE. The contract between the lanes is the API plan's draft
40
+ # (api-draft.json), and the service's as-built document is written and checked
41
+ # against it by analyze-slice, after its own fix loops — so it reflects the code that gate
42
+ # passes, not the code this loop left.
44
43
  #
45
44
  # Launch: /appweaver build [strategy]
46
45
  # =============================================================================
47
46
 
48
47
  name: aw-spec-build
49
- description: Compile the feature by running the build command of every lane the developer works on (.appweaver/lane.json — every declared lane by default), fix compile errors against the design intent, then record the touched files in the slice's knowledge concept; a developer who works on the service lane then re-exports its API document (contracts/openapi.json), which the frontend lane reads from the commit. Backend-only, frontend-only and full-stack projects all compile here — the gate requires a lane declaration, not a lane of a particular kind. Loads TECHNICAL rules only (tech-rule-bindings) — no domain rules reach this stage.
50
- version: 1.5.0
48
+ description: Compile the feature by running the build command of every lane the developer works on (.appweaver/lane.json — every declared lane by default), fix compile errors against the design intent, then record the touched files in the slice's knowledge concept. Backend-only, frontend-only and full-stack projects all compile here — the gate requires a lane declaration, not a lane of a particular kind. Loads TECHNICAL rules only (tech-rule-bindings) — no domain rules reach this stage.
49
+ version: 1.6.0
51
50
  agent_agnostic: true
52
51
  dialect: aw-spec.orchestrator/v1
53
52
 
@@ -73,13 +72,6 @@ vars:
73
72
  working_kind: "" # one kind when the developer works on one lane of two, else empty — every declared lane
74
73
  working_root: ""
75
74
  working_test_path: ""
76
- be_working: "false" # the service lane is declared and this developer works on it — it publishes the API document
77
- contract_ok: ""
78
- export_doc: ""
79
- contract_reason: ""
80
- contract_log: ""
81
- contract_already: "false"
82
- contract_fresh: "true"
83
75
 
84
76
  workflow:
85
77
 
@@ -192,38 +184,6 @@ workflow:
192
184
  on_false: stop
193
185
  on_false_message: "Build still failing after 3 fix attempts. Last log: {{build_log}}"
194
186
 
195
- # THE API DOCUMENT, published from the code that just compiled — by the developer who
196
- # works on the service lane, so the frontend developer reads it from the commit instead of
197
- # starting the service. Advisory: a failed export warns and the build stays green.
198
- - id: contract_export
199
- condition: "{{be_working}} == true"
200
- action: execute_script
201
- on_error: continue
202
- script: { ref: actions }
203
- subcommand: contract-export
204
- outputs:
205
- - { name: contract_ok, from: "$.ok" }
206
- - { name: export_doc, from: "$.doc" }
207
- - { name: contract_reason, from: "$.reason" }
208
- - { name: contract_already, from: "$.already_running" }
209
- - { name: contract_log, from: "$.log_tail" }
210
- - id: warn_contract_export
211
- condition: "{{be_working}} == true"
212
- action: verify_context
213
- require_true: contract_ok
214
- on_false: continue
215
- on_false_message: "API DOCUMENT NOT EXPORTED — {{contract_reason}}. The build is green; only the published document is out of date. The service lane 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 there, start anything the service needs, and re-run build. Service output: {{contract_log}}"
216
- - id: contract_stale_flag
217
- condition: "{{contract_already}} == true"
218
- action: set
219
- vars: { contract_fresh: "false" }
220
- - id: warn_contract_stale
221
- condition: "{{be_working}} == true"
222
- action: verify_context
223
- require_true: contract_fresh
224
- on_false: continue
225
- on_false_message: "WARNING — the API document was {{contract_reason}}. Stop that service and re-run build to export the code as it is now."
226
-
227
187
  # Record what the fix loop touched in the slice's OKF concept. This replaces the retired
228
188
  # `design-reconcile` step, which copied on-disk file bodies back into the design doc's
229
189
  # path-tagged fences — meaningless now that backend-plan.md describes contracts, not code (and
@@ -263,5 +223,4 @@ outputs:
263
223
  # (a file moved or deleted without re-running the owning slice). Advisory, never gated.
264
224
  stale_source_files: "{{paths_missing}}"
265
225
  working_lane: "{{working_lane}}" # the lane(s) compiled — .appweaver/lane.json
266
- api_document: "{{export_doc}}" # the service's API document, exported after the green compile — commit it for the frontend lane
267
226
  next: "/appweaver analyze-slice"