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
@@ -53,7 +53,8 @@
53
53
  # lane rule set, no constitution, no stack binding — which is the same reason analyze-plan
54
54
  # stops naming `/appweaver plan` instead of filling an empty template itself. So every
55
55
  # finding is REPORTED with the command that closes it (implement for missing code, plan for
56
- # a delta plan that is wrong, specify for a spec or model gap) and the gate stops. The
56
+ # a delta plan that is wrong, specify for a spec or model gap, fix-test-coverage for a
57
+ # requirement no test names) and the gate stops. The
57
58
  # repair loops stay in analyze-slice, where the merged master is the anchor and the job is
58
59
  # fixing what is BROKEN rather than building what is ABSENT.
59
60
  #
@@ -73,6 +74,29 @@
73
74
  # way: a developer working one lane of two writes delta-slice-analyze-report.<lane>.md, and
74
75
  # a developer on every lane writes delta-slice-analyze-report.md.
75
76
  #
77
+ # EVERY REQUIREMENT THE CHANGE'S PLAN SERVES IS NAMED BY A TEST — checked by a script as
78
+ # well as by the model. Check 2 asks the model whether each ADDED or MODIFIED item has a
79
+ # test citing its id; `test-citations` answers the same question deterministically for the
80
+ # FR- and BR- ids each gated lane's delta plan rows serve (ADDED, MODIFIED, model changes,
81
+ # interaction states — removed rows aside), over the slice's test files. An id no test
82
+ # names fails that lane's requirement vote and joins its findings, so it is stamped and
83
+ # reported like the model's own, and routed to /appweaver fix-test-coverage <CR-id> — the
84
+ # workflow that writes the missing tests; one the model already reported as untested is not
85
+ # reported twice. analyze-slice runs the master-plan half of this after the merge, but the
86
+ # change's new ids reach the master plans only when plan re-derives them, so this is where
87
+ # they are checked.
88
+ #
89
+ # THE SERVICE SERVES THE CHANGE'S API AS ITS DELTA API PLAN DRAFTS IT — checked by a script
90
+ # too. The frontend lane binds to the change's API draft (changes/<CR-id>/api-draft.json,
91
+ # drafted from delta-api-plan.md, or from the delta backend plan of a change planned before
92
+ # delta API plans existed) while the change is open, so for a gated service lane the
93
+ # service's as-built document is written the way the lane says and compared with it
94
+ # (contract-verify --cr): an endpoint the change adds or changes that is not served as
95
+ # drafted, or one it removes that is still served, fails that lane's requirement vote and
96
+ # joins its findings, routed to implement. A field that differs is reported. A lane that sets
97
+ # no way to write its document is not checked, and the run says so. A change that drafts no
98
+ # API change is not checked either, and its document is not written.
99
+ #
76
100
  # NO NEW RULE-MAP HEADING. It loads `## /aw-spec-analyze-slice` — the same testing +
77
101
  # coding-standards set its master twin reads — so no project has to add configuration to
78
102
  # get this gate. The DOMAIN gate overlays are deliberately NOT loaded: full domain
@@ -86,8 +110,8 @@
86
110
  # =============================================================================
87
111
 
88
112
  name: aw-spec-analyze-slice-delta
89
- version: 1.3.0
90
- description: "The delta-spec↔delta-code gate — verify that the open change DOES WHAT IT WAS ASKED TO DO, before it reaches the master documents. delta-spec.md is the anchor, exactly as it is for analyze-plan-delta. Asks which open change it is gating (only the changes naming a lane you work on are listed) and gates only the lanes the change names that you work on. Each gated lane runs its own tests (the first execution on the change path, since build only compiles), then its code and paired tests are evaluated against the change's REQUIREMENTS — every ADDED or MODIFIED delta-spec item realised in code, every one covered by a test that cites its id, every invariant the model delta adds actually enforced — with the delta plan in context as the map from requirement to code and cross-checked in its own right, so an element the plan promised and nobody built is caught too. Each gated lane's pass or fail is recorded in the change as .aw-slice-gate.<lane>.json. The dynamic twin of analyze-plan-delta and the delta twin of analyze-slice, which cannot see a missing implementation because a behaviour that was never built leaves no failing test. Verify-only — it reports each finding with the command that closes it and stops; it generates no code and repairs nothing."
113
+ version: 1.7.0
114
+ description: "The delta-spec↔delta-code gate — verify that the open change DOES WHAT IT WAS ASKED TO DO, before it reaches the master documents. delta-spec.md is the anchor, exactly as it is for analyze-plan-delta. Asks which open change it is gating (only the changes naming a lane you work on are listed) and gates only the lanes the change names that you work on. Each gated lane runs its own tests (the first execution on the change path, since build only compiles), then its code and paired tests are evaluated against the change's REQUIREMENTS — every ADDED or MODIFIED delta-spec item realised in code, every one covered by a test that cites its id (and, by script, every FR- and BR- the lane's delta plan rows serve named by a test of the slice), the service lane serving the API the change's delta API plan drafts (by script, against its as-built document), every invariant the model delta adds actually enforced — with the delta plan in context as the map from requirement to code and cross-checked in its own right, so an element the plan promised and nobody built is caught too. Each gated lane's pass or fail is recorded in the change as .aw-slice-gate.<lane>.json. The dynamic twin of analyze-plan-delta and the delta twin of analyze-slice, which cannot see a missing implementation because a behaviour that was never built leaves no failing test. Verify-only — it reports each finding with the command that closes it and stops; it generates no code and repairs nothing."
91
115
  agent_agnostic: true
92
116
  dialect: aw-spec.orchestrator/v1
93
117
 
@@ -124,6 +148,8 @@ vars:
124
148
  delta_backend_plan_exists: "false"
125
149
  delta_frontend_plan: ""
126
150
  delta_frontend_plan_exists: "false"
151
+ delta_api_plan: "" # the change's API — absent for a change whose delta backend plan carries it
152
+ api_plan: "" # the feature's API plan — absent for a feature whose backend plan carries it
127
153
  domain_model: ""
128
154
  # WHICH LANES THIS GATE CHECKS — a lane is gated when it is DECLARED for this feature, the
129
155
  # change's **Lanes** line NAMES it, this developer WORKS on it (.appweaver/lane.json) and
@@ -191,6 +217,39 @@ vars:
191
217
  be_req_vote: "true"
192
218
  fe_req_vote: "true"
193
219
  requirements_ok: "false"
220
+ # The test-citation check, per gated lane. Its `ok` votes into that lane's requirement
221
+ # vote only when the lane is gated, and a check that never answered votes not met.
222
+ be_cite_ok: "false"
223
+ be_cite_clean: "false"
224
+ be_cite_warning: ""
225
+ be_uncited: "not checked — the lane is not gated"
226
+ be_cite_findings: []
227
+ be_cite_vote: "true"
228
+ fe_cite_ok: "false"
229
+ fe_cite_clean: "false"
230
+ fe_cite_warning: ""
231
+ fe_uncited: "not checked — the lane is not gated"
232
+ fe_cite_findings: []
233
+ fe_cite_vote: "true"
234
+ # The API check of a gated service lane — the same refusing default as the citation
235
+ # check, voting only when its document was written, and only for a change that drafts an
236
+ # API change.
237
+ be_api_needed: "false" # contract-verify --cr --check: the change drafts something to check
238
+ be_api_plan_note: "" # why it drafts nothing, when it does not
239
+ be_api_way: ""
240
+ be_api_placeholder: ""
241
+ be_api_export_ok: "false"
242
+ be_api_export_reason: ""
243
+ be_api_export_log: ""
244
+ be_api_has_way: "true"
245
+ be_api_export_vote: "true"
246
+ be_api_export_clean: "true"
247
+ be_api_ok: ""
248
+ be_api_issues: "not checked — no as-built API document was written in this run"
249
+ be_api_warning: ""
250
+ be_api_clean: "true"
251
+ be_api_findings: []
252
+ be_api_vote: "true"
194
253
  # Findings split by the command that closes them (gap-route). Defaults read as
195
254
  # "nothing found", so a run where the router could not execute still prints.
196
255
  gap_summary: "no findings"
@@ -201,6 +260,8 @@ vars:
201
260
  fix_plan_n: "0"
202
261
  fix_spec: "none"
203
262
  fix_spec_n: "0"
263
+ fix_tests: "none"
264
+ fix_tests_n: "0"
204
265
  fix_review: "none"
205
266
  fix_review_n: "0"
206
267
  gate_ok: "false"
@@ -253,6 +314,7 @@ workflow:
253
314
  - { name: spec_file, from: "$.spec_file" }
254
315
  - { name: backend_plan, from: "$.backend_plan" }
255
316
  - { name: frontend_plan, from: "$.frontend_plan" }
317
+ - { name: api_plan, from: "$.api_plan" }
256
318
 
257
319
  # THIS FEATURE'S LANES. lane_resolve above read the REPO's declaration; one feature may
258
320
  # own no part of a lane's surface — adopt records `lanes: [backend]` in
@@ -354,6 +416,7 @@ workflow:
354
416
  - { name: delta_backend_plan_exists, from: "$.delta_backend_plan_exists" }
355
417
  - { name: delta_frontend_plan, from: "$.delta_frontend_plan" }
356
418
  - { name: delta_frontend_plan_exists, from: "$.delta_frontend_plan_exists" }
419
+ - { name: delta_api_plan, from: "$.delta_api_plan" }
357
420
  - { name: wave, from: "$.wave" }
358
421
  - { name: domain_model, from: "$.domain_model" }
359
422
  # The lanes this change touches — its delta-spec's **Lanes** line, every lane when it
@@ -557,8 +620,10 @@ workflow:
557
620
  - { path: "{{delta_spec}}", optional: true } # THE ANCHOR — what this change was asked to do
558
621
  - { path: "{{delta_model}}", optional: true } # the invariants it adds or changes — requirements too
559
622
  - { path: "{{delta_backend_plan}}" } # THE MAP — which element serves which requirement
623
+ - { path: "{{delta_api_plan}}", optional: true } # the change's API — the endpoints it adds, changes or removes, and their error contracts
560
624
  - { path: "{{spec_file}}", optional: true } # the master spec — the EXISTING ids a plan-only change still serves
561
625
  - { path: "{{backend_plan}}", optional: true } # the master plan — what was already there
626
+ - { path: "{{api_plan}}", optional: true } # the master API plan — the endpoints as they were
562
627
  - { path: "{{domain_model}}", optional: true } # the model as it stands before the merge
563
628
  - id: check_backend
564
629
  condition: "{{be_gated}} == true"
@@ -573,14 +638,15 @@ workflow:
573
638
  not "does it match the plan": it is DOES THIS CHANGE DO WHAT IT WAS ASKED TO DO?
574
639
  {{source_count}} file(s) realise this slice: {{source_files}} — READ the ones this
575
640
  change should have touched.
576
- The delta plan is in context as the MAP from a requirement to the element that serves
577
- it — use it to know WHERE to look, and to write a finding that names the place the
641
+ The delta plan — with the change's delta-api-plan.md, in context where it has one —
642
+ is in context as the MAP from a requirement to the element or endpoint that serves
643
+ it: use it to know WHERE to look, and to write a finding that names the place the
578
644
  behaviour was supposed to land. It is not the authority: a requirement the plan never
579
645
  planned is still this gate's finding, because the delta spec is the only document
580
646
  that knows it was asked for.
581
- The master spec, master plan and master domain model are what was ALREADY there; they
582
- do not yet carry this change (finalize-delta merges it AFTER this gate), so never
583
- report an element as missing from them.
647
+ The master spec, master plan, master API plan and master domain model are what was
648
+ ALREADY there; they do not yet carry this change (finalize-delta merges it AFTER this
649
+ gate), so never report an element as missing from them.
584
650
 
585
651
  CHECK 1 — REQUIREMENTS → CODE (the reason this gate exists). For EVERY ADDED or
586
652
  MODIFIED item in the delta-spec — FR-, BR-, CN-, DC-, IC-, user story — the code must
@@ -608,7 +674,9 @@ workflow:
608
674
 
609
675
  CHECK 3 — THE PLAN'S OWN PROMISES (cross-check, not the anchor). Every element the
610
676
  delta plan marks ADDED exists, every MODIFIED one carries its change, every REMOVED
611
- one is gone and unreferenced. A promised element nobody built is a HIGH `plan` gap
677
+ one is gone and unreferenced; every endpoint the API delta adds or changes is served
678
+ at the route and verb its block gives, with its error contract, and every one it
679
+ removes is no longer served. A promised element nobody built is a HIGH `plan` gap
612
680
  even when the requirement it served looks satisfied elsewhere — the two usually mean
613
681
  the change was implemented in a different shape than was planned, which is worth
614
682
  knowing before the masters are re-derived from it. Where the code satisfies the
@@ -747,6 +815,131 @@ workflow:
747
815
  - { name: fe_ok, from: "$.ok" }
748
816
  - { name: fe_gaps, from: "$.gaps" }
749
817
 
818
+ # ══════ PASS 3 — TEST CITATIONS. Is every requirement the change serves named by a test? ══════
819
+ # The deterministic half of check 2: the FR- and BR- ids each gated lane's delta plan rows
820
+ # serve, looked for in the slice's test files. An id the lane's check already reported as
821
+ # untested is not reported again (`--known-gaps`).
822
+ - id: cite_backend
823
+ condition: "{{be_gated}} == true"
824
+ action: execute_script
825
+ on_error: continue
826
+ script: { ref: actions }
827
+ subcommand: test-citations
828
+ args: ["--plan", "{{delta_backend_plan}}", "--kind", "backend", "--mode", "delta", "--paths", "{{source_files}}", "--source-path", "{{be_source_path}}", "--test-path", "{{be_test_path}}", "--known-gaps", "{{be_gaps}}"]
829
+ outputs:
830
+ - { name: be_cite_ok, from: "$.ok" }
831
+ - { name: be_cite_clean, from: "$.clean" }
832
+ - { name: be_cite_warning, from: "$.warning" }
833
+ - { name: be_uncited, from: "$.uncited_text" }
834
+ - { name: be_cite_findings, from: "$.findings" }
835
+ - id: warn_cite_backend
836
+ condition: "{{be_gated}} == true"
837
+ action: verify_context
838
+ require_true: be_cite_clean
839
+ on_false: continue
840
+ on_false_message: "{{be_cite_warning}}"
841
+ - id: cite_frontend
842
+ condition: "{{fe_gated}} == true"
843
+ action: execute_script
844
+ on_error: continue
845
+ script: { ref: actions }
846
+ subcommand: test-citations
847
+ args: ["--plan", "{{delta_frontend_plan}}", "--kind", "frontend", "--mode", "delta", "--paths", "{{source_files}}", "--source-path", "{{fe_source_path}}", "--test-path", "{{fe_test_path}}", "--known-gaps", "{{fe_gaps}}"]
848
+ outputs:
849
+ - { name: fe_cite_ok, from: "$.ok" }
850
+ - { name: fe_cite_clean, from: "$.clean" }
851
+ - { name: fe_cite_warning, from: "$.warning" }
852
+ - { name: fe_uncited, from: "$.uncited_text" }
853
+ - { name: fe_cite_findings, from: "$.findings" }
854
+ - id: warn_cite_frontend
855
+ condition: "{{fe_gated}} == true"
856
+ action: verify_context
857
+ require_true: fe_cite_clean
858
+ on_false: continue
859
+ on_false_message: "{{fe_cite_warning}}"
860
+
861
+ # ══════ PASS 4 — API. Does the built service serve the change's API as its delta API plan drafts it? ══════
862
+ # Only for a change that drafts an API change: contract-verify --cr --check reads the
863
+ # change's draft without the as-built document, so a behaviour-only change never runs the
864
+ # lane's export or its warnings. Only an explicit `needed: false` skips: a check that does
865
+ # not answer leaves the export armed.
866
+ - id: api_plan_check
867
+ condition: "{{be_gated}} == true"
868
+ action: execute_script
869
+ on_error: continue
870
+ script: { ref: actions }
871
+ subcommand: contract-verify
872
+ args: ["--feature-dir", "{{feature_dir}}", "--cr", "{{cr_id}}", "--check"]
873
+ outputs:
874
+ - { name: be_api_needed, from: "$.needed" }
875
+ - { name: be_api_plan_note, from: "$.note" }
876
+ - id: api_nothing_to_check
877
+ condition: "{{be_api_plan_note}} != "
878
+ action: set
879
+ vars: { be_api_issues: "{{be_api_plan_note}}" }
880
+ - id: api_export
881
+ condition: "{{be_api_needed}} != false"
882
+ action: execute_script
883
+ on_error: continue
884
+ script: { ref: actions }
885
+ subcommand: contract-export
886
+ outputs:
887
+ - { name: be_api_export_ok, from: "$.ok" }
888
+ - { name: be_api_way, from: "$.way" }
889
+ - { name: be_api_export_reason, from: "$.reason" }
890
+ - { name: be_api_export_log, from: "$.log_tail" }
891
+ - { name: be_api_placeholder, from: "$.placeholder" }
892
+ - id: api_no_way_flag
893
+ condition: "{{be_api_way}} == none"
894
+ action: set
895
+ vars: { be_api_has_way: "false" }
896
+ - id: warn_api_no_way
897
+ condition: "{{be_api_needed}} != false"
898
+ action: verify_context
899
+ require_true: be_api_has_way
900
+ on_false: continue
901
+ on_false_message: "API NOT CHECKED — {{be_api_export_reason}}. The frontend lane binds to the API this change's delta API plan drafts (changes/{{cr_id}}/api-draft.json); without the service's as-built document nothing checks that the code serves it. `contract_write` is any command that writes the service's OpenAPI document to the path {{be_api_placeholder}} stands for, with no server started."
902
+ - id: api_export_vote_set
903
+ condition: "{{be_api_needed}} != false"
904
+ action: set
905
+ vars: { be_api_export_vote: "{{be_api_export_ok}}" }
906
+ - id: api_export_failed
907
+ condition: "{{be_api_export_vote}} != true"
908
+ action: set
909
+ vars: { be_api_export_clean: "false" }
910
+ - id: api_export_no_way
911
+ condition: "{{be_api_has_way}} == false"
912
+ action: set
913
+ vars: { be_api_export_clean: "true" }
914
+ - id: warn_api_export
915
+ condition: "{{be_api_needed}} != false"
916
+ action: verify_context
917
+ require_true: be_api_export_clean
918
+ on_false: continue
919
+ on_false_message: "API NOT CHECKED — the service's as-built API document could not be written: {{be_api_export_reason}}. Correct `contract_write` (or `contract_run` / `contract`) on the service lane in .appweaver/stacks.json, start anything it needs, and re-run analyze-slice-delta {{cr_id}}. Output: {{be_api_export_log}}"
920
+ - id: api_verify
921
+ condition: "{{be_api_export_ok}} == true"
922
+ action: execute_script
923
+ on_error: continue
924
+ script: { ref: actions }
925
+ subcommand: contract-verify
926
+ args: ["--feature-dir", "{{feature_dir}}", "--cr", "{{cr_id}}"]
927
+ outputs:
928
+ - { name: be_api_ok, from: "$.ok" }
929
+ - { name: be_api_issues, from: "$.issues_text" }
930
+ - { name: be_api_warning, from: "$.warning" }
931
+ - { name: be_api_findings, from: "$.findings" }
932
+ - id: api_warning_flag
933
+ condition: "{{be_api_warning}} != "
934
+ action: set
935
+ vars: { be_api_clean: "false" }
936
+ - id: warn_api_fields
937
+ condition: "{{be_api_export_ok}} == true"
938
+ action: verify_context
939
+ require_true: be_api_clean
940
+ on_false: continue
941
+ on_false_message: "{{be_api_warning}}"
942
+
750
943
  # ═══════════════════════════ THE VERDICT ═══════════════════════════
751
944
  # requirements_ok = every GATED lane passed. Expressed as a raise-then-knock-down chain,
752
945
  # because the engine has no compound conditions and an ungated lane must not vote. A gated
@@ -768,6 +961,32 @@ workflow:
768
961
  condition: "{{fe_ok}} == true"
769
962
  action: set
770
963
  vars: { fe_req_vote: "true" }
964
+ # A requirement no test names knocks the lane's vote down, whatever its check said.
965
+ - id: be_cite_vote_set
966
+ condition: "{{be_gated}} == true"
967
+ action: set
968
+ vars: { be_cite_vote: "{{be_cite_ok}}" }
969
+ - id: be_req_vote_uncited
970
+ condition: "{{be_cite_vote}} != true"
971
+ action: set
972
+ vars: { be_req_vote: "false" }
973
+ # So does an endpoint the change drafts that the service does not serve as drafted.
974
+ - id: be_api_vote_set
975
+ condition: "{{be_api_export_ok}} == true"
976
+ action: set
977
+ vars: { be_api_vote: "{{be_api_ok}}" }
978
+ - id: be_req_vote_api
979
+ condition: "{{be_api_vote}} != true"
980
+ action: set
981
+ vars: { be_req_vote: "false" }
982
+ - id: fe_cite_vote_set
983
+ condition: "{{fe_gated}} == true"
984
+ action: set
985
+ vars: { fe_cite_vote: "{{fe_cite_ok}}" }
986
+ - id: fe_req_vote_uncited
987
+ condition: "{{fe_cite_vote}} != true"
988
+ action: set
989
+ vars: { fe_req_vote: "false" }
771
990
  - id: requirements_seed
772
991
  action: set
773
992
  vars: { requirements_ok: "true" }
@@ -791,7 +1010,7 @@ workflow:
791
1010
  on_error: continue
792
1011
  script: { ref: actions }
793
1012
  subcommand: gap-route
794
- args: ["--cr", "{{cr_id}}", "--gaps", "{{be_gaps}}", "--gaps", "{{fe_gaps}}"]
1013
+ args: ["--cr", "{{cr_id}}", "--gaps", "{{be_gaps}}", "--gaps", "{{fe_gaps}}", "--gaps", "{{be_cite_findings}}", "--gaps", "{{fe_cite_findings}}", "--gaps", "{{be_api_findings}}"]
795
1014
  outputs:
796
1015
  - { name: gap_summary, from: "$.summary" }
797
1016
  - { name: gap_total, from: "$.total" }
@@ -801,6 +1020,8 @@ workflow:
801
1020
  - { name: fix_plan_n, from: "$.plan_count" }
802
1021
  - { name: fix_spec, from: "$.specify_text" }
803
1022
  - { name: fix_spec_n, from: "$.specify_count" }
1023
+ - { name: fix_tests, from: "$.tests_text" }
1024
+ - { name: fix_tests_n, from: "$.tests_count" }
804
1025
  - { name: fix_review, from: "$.review_text" }
805
1026
  - { name: fix_review_n, from: "$.review_count" }
806
1027
 
@@ -820,7 +1041,7 @@ workflow:
820
1041
  on_error: continue
821
1042
  script: { ref: actions }
822
1043
  subcommand: slice-gate-stamp
823
- args: ["--cr-id", "{{cr_id}}", "--feature-dir", "{{feature_dir}}", "--tests-green", "{{be_test_ok}}", "--tests-ran", "{{be_test_ran}}", "--requirements-ok", "{{be_req_vote}}", "--findings", "{{be_gaps}}", "--lane", "backend"]
1044
+ args: ["--cr-id", "{{cr_id}}", "--feature-dir", "{{feature_dir}}", "--tests-green", "{{be_test_ok}}", "--tests-ran", "{{be_test_ran}}", "--requirements-ok", "{{be_req_vote}}", "--findings", "{{be_gaps}}", "--findings", "{{be_cite_findings}}", "--findings", "{{be_api_findings}}", "--lane", "backend"]
824
1045
  outputs:
825
1046
  - { name: be_verdict, from: "$.verdict" }
826
1047
  - id: stamp_frontend
@@ -829,7 +1050,7 @@ workflow:
829
1050
  on_error: continue
830
1051
  script: { ref: actions }
831
1052
  subcommand: slice-gate-stamp
832
- args: ["--cr-id", "{{cr_id}}", "--feature-dir", "{{feature_dir}}", "--tests-green", "{{fe_test_ok}}", "--tests-ran", "{{fe_test_ran}}", "--requirements-ok", "{{fe_req_vote}}", "--findings", "{{fe_gaps}}", "--lane", "frontend"]
1053
+ args: ["--cr-id", "{{cr_id}}", "--feature-dir", "{{feature_dir}}", "--tests-green", "{{fe_test_ok}}", "--tests-ran", "{{fe_test_ran}}", "--requirements-ok", "{{fe_req_vote}}", "--findings", "{{fe_gaps}}", "--findings", "{{fe_cite_findings}}", "--lane", "frontend"]
833
1054
  outputs:
834
1055
  - { name: fe_verdict, from: "$.verdict" }
835
1056
 
@@ -850,7 +1071,7 @@ workflow:
850
1071
  - id: be_state_gated
851
1072
  condition: "{{be_gated}} == true"
852
1073
  action: set
853
- vars: { be_state: "gated — tests green={{be_test_ok}} (tool `{{be_test_tool}}`, actually ran={{be_test_ran}}), requirements met={{be_req_vote}}, recorded `{{be_verdict}}` in .aw-slice-gate.backend.json" }
1074
+ vars: { be_state: "gated — tests green={{be_test_ok}} (tool `{{be_test_tool}}`, actually ran={{be_test_ran}}), requirements met={{be_req_vote}} (requirement ids no test names: {{be_uncited}}; the change's API as served: {{be_api_issues}}), recorded `{{be_verdict}}` in .aw-slice-gate.backend.json" }
854
1075
  - id: fe_state_unnamed
855
1076
  condition: "{{fe_declared}} == true"
856
1077
  action: set
@@ -866,7 +1087,7 @@ workflow:
866
1087
  - id: fe_state_gated
867
1088
  condition: "{{fe_gated}} == true"
868
1089
  action: set
869
- vars: { fe_state: "gated — tests green={{fe_test_ok}} (tool `{{fe_test_tool}}`, actually ran={{fe_test_ran}}), requirements met={{fe_req_vote}}, recorded `{{fe_verdict}}` in .aw-slice-gate.frontend.json" }
1090
+ vars: { fe_state: "gated — tests green={{fe_test_ok}} (tool `{{fe_test_tool}}`, actually ran={{fe_test_ran}}), requirements met={{fe_req_vote}} (requirement ids no test names: {{fe_uncited}}), recorded `{{fe_verdict}}` in .aw-slice-gate.frontend.json" }
870
1091
 
871
1092
  # THE REPORT IS ALWAYS WRITTEN — green or red. A green one is the record that this change
872
1093
  # was verified built before it was merged, which is what makes the merge auditable; a red
@@ -903,10 +1124,14 @@ workflow:
903
1124
  `{{working_lane}}`. Each gated lane ran its own tests and was checked on its own:
904
1125
  - service lane: {{be_state}}. Matched a slice filter={{be_test_matched}},
905
1126
  slice-scoped={{be_test_scoped}}. Failing lines: {{be_test_excerpt}} · Log tail:
906
- {{be_test_log}} · gaps: {{be_gaps}}
1127
+ {{be_test_log}} · gaps: {{be_gaps}} · test-citation findings (by script — an FR- or
1128
+ BR- the delta plan serves that no test names): {{be_cite_findings}} · API findings
1129
+ (by script — an endpoint the change adds or changes that the service does not serve
1130
+ as its draft states, or one it removes that is still served): {{be_api_findings}}
907
1131
  - experience lane: {{fe_state}}. Matched a slice filter={{fe_test_matched}},
908
1132
  slice-scoped={{fe_test_scoped}}. Failing lines: {{fe_test_excerpt}} · Log tail:
909
- {{fe_test_log}} · gaps: {{fe_gaps}}
1133
+ {{fe_test_log}} · gaps: {{fe_gaps}} · test-citation findings (by script):
1134
+ {{fe_cite_findings}}
910
1135
  Markdown format:
911
1136
  - "# Delta Slice Analyze Report — {{cr_id}}" plus today's date.
912
1137
  - A verdict line: "verdict: REQUIREMENTS MET ✓" when every gated lane's tests are
@@ -921,7 +1146,7 @@ workflow:
921
1146
  requirement findings below, not the fence, are what answer whether the change does
922
1147
  what it was asked to.
923
1148
  - "## Findings" as a table — | # | check | lane | severity | delta-spec ref | where |
924
- finding | fixed by | — ordered high severity first, and within that the
1149
+ finding | fixed by | — the gaps, the test-citation findings AND the API findings, ordered high severity first, and within that the
925
1150
  `requirement` check first, because an unrealised requirement is the finding this
926
1151
  gate exists for. Omit the section entirely when there are none, and say so under
927
1152
  the verdict.
@@ -933,8 +1158,10 @@ workflow:
933
1158
  /appweaver plan {{cr_id}}, then analyze-plan-delta. `specify` — the delta spec or
934
1159
  the model delta is what is missing (a requirement realised in code that nothing
935
1160
  asked for lands here too, as does a plan-only change that moved behaviour); re-run
936
- /appweaver specify, which is the only phase that authors either. Then re-run
937
- /appweaver analyze-slice-delta {{cr_id}}.
1161
+ /appweaver specify, which is the only phase that authors either.
1162
+ `fix-test-coverage` — a requirement no test names (the test-citation findings); run
1163
+ /appweaver fix-test-coverage {{cr_id}}, which writes the tests and never code. Then
1164
+ re-run /appweaver analyze-slice-delta {{cr_id}}.
938
1165
  - Close with one line stating that this gate verifies only; it generated no code and
939
1166
  repaired nothing.
940
1167
  Return the complete report markdown only.
@@ -959,10 +1186,12 @@ workflow:
959
1186
 
960
1187
  CODE ({{fix_code_n}}): {{fix_code}}
961
1188
 
1189
+ TESTS ({{fix_tests_n}}): {{fix_tests}}
1190
+
962
1191
  UNROUTED ({{fix_review_n}}): {{fix_review}}
963
1192
 
964
1193
  Work them in that order — the spec before the plan that realises it, the plan before
965
- the code that maps to it. Re-running /appweaver plan {{cr_id}} re-derives the delta
1194
+ the code that maps to it, the code before its tests. Re-running /appweaver plan {{cr_id}} re-derives the delta
966
1195
  plans with this report in context, so a plan finding is fixed by re-deriving rather
967
1196
  than by hand-editing a generated document. The full report is at
968
1197
  {{report_file}}. DO NOT run finalize-delta yet: merging now
@@ -999,6 +1228,7 @@ outputs:
999
1228
  backend_ok: "{{be_ok}}"
1000
1229
  backend_gaps: "{{be_gaps}}"
1001
1230
  backend_verdict: "{{be_verdict}}" # as recorded in .aw-slice-gate.backend.json
1231
+ backend_untraced: "{{be_uncited}}" # FR-/BR- ids the backend delta plan serves that no test names
1002
1232
  frontend_gated: "{{fe_gated}}"
1003
1233
  frontend_state: "{{fe_state}}"
1004
1234
  frontend_tests_green: "{{fe_test_ok}}"
@@ -1007,6 +1237,7 @@ outputs:
1007
1237
  frontend_ok: "{{fe_ok}}"
1008
1238
  frontend_gaps: "{{fe_gaps}}"
1009
1239
  frontend_verdict: "{{fe_verdict}}" # as recorded in .aw-slice-gate.frontend.json
1240
+ frontend_untraced: "{{fe_uncited}}"
1010
1241
  report: "{{report_file}}" # delta-slice-analyze-report.<lane>.md when one lane of two is worked
1011
1242
  source_count: "{{source_count}}"
1012
1243
  note: "Gated change {{cr_id}} (wave {{wave}}) — delta-spec.md is the ANCHOR, so the question answered here is whether the CODE does what the change asked for, not whether it matches the plan. The delta plan is the MAP from a requirement to the element that serves it, and is cross-checked in its own right; the change's model delta supplies the invariants the code must enforce, and they are requirements too. The masters are read-only context for what was already there — they do not carry this change until finalize-delta merges it. Only a lane the change names, that you work on and that has a delta plan for THIS change is gated: backend — {{be_state}}; frontend — {{fe_state}}. An ungated lane is reported as not gated, never as a pass, and a lane somebody else works on is gated in their own run. Each gated lane ran its own tests, here for the first time on the change path since build only compiles, and its pass or fail is recorded in {{cr_dir}}/.aw-slice-gate.<lane>.json. This gate verifies: it generated no code and repaired nothing, and every finding names the command that closes it."