aw-spec-cli 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +76 -81
  2. package/assets/.appweaver/DESIGN-FORMAT.md +56 -17
  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 -16
  12. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +30 -6
  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 +341 -36
  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 +28 -12
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +453 -116
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +7 -4
  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
@@ -0,0 +1,965 @@
1
+ # =============================================================================
2
+ # aw-spec-fix-test-coverage (orchestrator) — write the tests no requirement has
3
+ #
4
+ # The repair step for the gap the two slice gates report. analyze-slice reads each worked
5
+ # lane's master plan for the FR- and BR- ids its Traceability section traces, and
6
+ # analyze-slice-delta reads the ids a change's delta plan rows serve; both look for each id
7
+ # in the feature's test files (`test-citations`) and fail when a test names none. Green
8
+ # tests cannot show a requirement whose test was never written, which is why those gates
9
+ # exist — and this is what closes what they find.
10
+ #
11
+ # /appweaver fix-test-coverage the master plans' ids → then analyze-slice
12
+ # /appweaver fix-test-coverage <CR-id> that change's delta-plan ids → then build →
13
+ # analyze-slice-delta
14
+ # With no id it works from the master plans even when changes are open; an id that names
15
+ # no open change stops and lists the open ones.
16
+ #
17
+ # TESTS ONLY. For each id no test names, the model reads what the requirement or invariant
18
+ # states, finds the code that realises it, and writes a test of that behaviour naming the
19
+ # id — or, where a test of the feature already exercises it, adds the id to that test.
20
+ # Production code is never written here: an id whose behaviour the code does not realise is
21
+ # reported `not-implemented` and left for /appweaver implement, because a test written
22
+ # against missing behaviour either passes without checking anything or fails for a reason
23
+ # no test edit can fix.
24
+ #
25
+ # THE REQUIREMENT TEXT IS IN CONTEXT, unlike implement. implement realises the PLAN and is
26
+ # deliberately kept from the spec so a gap in the plan is found rather than compensated for.
27
+ # A test checks the REQUIREMENT, so the spec (or the change's delta spec) and the domain
28
+ # model are what it is written from; the plan is the map from an id to the element that
29
+ # realises it.
30
+ #
31
+ # THE SCRIPT DECIDES, NOT THE MODEL. After the tests are written the lane's tests run, and
32
+ # `test-citations` runs again: an id is closed when a test names it and the suite is green,
33
+ # whatever the model reported.
34
+ #
35
+ # A NEW TEST THAT FAILS gets at most two repair passes, and those may change only the files
36
+ # this run wrote, only for what is mechanical — a test that does not compile, a missing
37
+ # import, the wrong fixture or setup. Never production code, never another test, never
38
+ # what an assertion expects. A test that runs and fails its assertion has found a place
39
+ # where the code does not do what the requirement says: it is reported as a suspected
40
+ # defect and the run stops, with the test kept as the evidence.
41
+ #
42
+ # AN ADOPTED FEATURE IS NOT EXEMPT. Its plans carry `origin: reverse-engineered`, and the
43
+ # gates only report its gaps. Its spec was reversed from the code, so a test of what the spec
44
+ # states should pass against it; one that fails is a mismatch between the adopted spec and
45
+ # the code — reported, never fixed in the code.
46
+ #
47
+ # ONLY THE LANES YOU WORK ON (.appweaver/lane.json), and on a change only those it names
48
+ # that have its delta plan. Each lane is confirmed before anything is written, and loads the
49
+ # rule set its tests were first generated under (`## /aw-spec-implement-<lane>`), so no
50
+ # project needs new configuration for this command.
51
+ #
52
+ # Launch: /appweaver fix-test-coverage [CR-id]
53
+ # Prereq: analyze-slice (or analyze-slice-delta <CR-id>) reported requirements no test names.
54
+ # =============================================================================
55
+
56
+ name: aw-spec-fix-test-coverage
57
+ version: 1.0.0
58
+ description: "Write the tests the slice gates found missing — for every FR- and BR- id a lane's plan traces (no argument: the master plans' Traceability, the ids analyze-slice checks) or a change's delta plan rows serve (a CR id: the ids analyze-slice-delta checks) that no test of the feature names. For each id the model reads the requirement or invariant, finds the code that realises it, and writes a test of that behaviour naming the id, or adds the id to the test that already exercises it; it never writes production code, and an id the code does not realise is reported not-implemented for /appweaver implement. Only the lanes you work on, each confirmed first. The lane's tests then run — a failing new test gets at most two mechanical repairs, limited to the files this run wrote, and a failing assertion is reported as a suspected defect and stops the run — and test-citations runs again, so the script, not the model, says whether each id is now named."
59
+ agent_agnostic: true
60
+ dialect: aw-spec.orchestrator/v1
61
+
62
+ inputs:
63
+ user_input:
64
+ description: "Optional — a CR id fills the test gaps of that open change's delta plans; with none, the master plans' gaps are filled."
65
+ required: false
66
+ bind: USER_INPUT
67
+
68
+ scripts:
69
+ actions:
70
+ sh: .appweaver/scripts/bash/aw-spec-actions.sh
71
+ ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
72
+ xref:
73
+ sh: .appweaver/scripts/bash/aw-xref.sh
74
+ ps1: .appweaver/scripts/powershell/aw-xref.ps1
75
+
76
+ # Sticky standards seed = the universal always-on rule only. Each lane's rule set is loaded
77
+ # beside its own steps by tech-select, under implement's heading for that lane.
78
+ standards:
79
+ - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
80
+
81
+ vars:
82
+ # master = the master plans' Traceability; delta = one open change's delta plans.
83
+ mode: "master"
84
+ cite_mode: "bootstrap" # test-citations --mode
85
+ where_text: "the Traceability section"
86
+ spec_text: "spec.md, in context"
87
+ cr_id: ""
88
+ cr_arg: "" # " <CR-id>" on the delta path, for the commands this run names
89
+ cr_dir: ""
90
+ cr_lanes_csv: ""
91
+ matched_label: ""
92
+ open_ids: ""
93
+ preselect_hidden: ""
94
+ preselect_visible: "true"
95
+ delta_spec: ""
96
+ delta_model: ""
97
+ delta_backend_plan: ""
98
+ delta_backend_plan_exists: "false"
99
+ delta_frontend_plan: ""
100
+ delta_frontend_plan_exists: "false"
101
+ domain_model: ""
102
+ # THE LANES. A lane is a TARGET when it is worked here, has the plan this run reads, and —
103
+ # on a change — is one the change names. Each AND is an ordered assignment.
104
+ lanes_declared: "false"
105
+ be_working: "false"
106
+ fe_working: "false"
107
+ working_lane: ""
108
+ be_in_change: "true" # every lane until cr-paths answers (and always on the master path)
109
+ fe_in_change: "true"
110
+ be_plan: ""
111
+ be_plan_ok: "false"
112
+ fe_plan: ""
113
+ fe_plan_ok: "false"
114
+ be_plan_needed: "false"
115
+ fe_plan_needed: "false"
116
+ be_target: "false"
117
+ fe_target: "false"
118
+ any_target: "false"
119
+ # WHAT THE SCRIPT FOUND, per lane, before this run wrote anything. Defaults read as "nothing
120
+ # to fill", so a lane that is not a target never asks and never generates.
121
+ be_all_cited: "true"
122
+ be_cite_checked: "true"
123
+ be_cite_clean: "true"
124
+ be_cite_warning: ""
125
+ be_advisory: "false"
126
+ be_uncited_before: "not checked — not a lane this run targets"
127
+ be_test_files: []
128
+ be_gap: "false"
129
+ fe_all_cited: "true"
130
+ fe_cite_checked: "true"
131
+ fe_cite_clean: "true"
132
+ fe_cite_warning: ""
133
+ fe_advisory: "false"
134
+ fe_uncited_before: "not checked — not a lane this run targets"
135
+ fe_test_files: []
136
+ fe_gap: "false"
137
+ any_gap: "false"
138
+ no_gap: "true"
139
+ # CONFIRMATION and generation.
140
+ be_answer: ""
141
+ be_go: "false"
142
+ fe_answer: ""
143
+ fe_go: "false"
144
+ any_go: "false"
145
+ be_existing_text: " (not resolved)"
146
+ fe_existing_text: " (not resolved)"
147
+ be_oracle_rule: "THE REQUIREMENT IS THE ORACLE: each test asserts what the requirement or invariant states. If the code does something else, the test fails, and that is a defect in the code — never a reason to bend the test."
148
+ fe_oracle_rule: "THE REQUIREMENT IS THE ORACLE: each test asserts what the requirement or invariant states. If the UI does something else, the test fails, and that is a defect in the code — never a reason to bend the test."
149
+ be_files: []
150
+ be_ids: "not run — this lane did not generate"
151
+ fe_files: []
152
+ fe_ids: "not run — this lane did not generate"
153
+ # THE TESTS AFTER WRITING. A lane that did not generate does not vote.
154
+ be_test_ok: "false"
155
+ be_test_ran: "true"
156
+ be_test_excerpt: ""
157
+ be_test_log: ""
158
+ be_no_setup: "false"
159
+ be_setup_reason: ""
160
+ be_setup_ok: "true"
161
+ be_repair_files: []
162
+ be_suspected: []
163
+ fe_test_ok: "false"
164
+ fe_test_ran: "true"
165
+ fe_test_excerpt: ""
166
+ fe_test_log: ""
167
+ fe_no_setup: "false"
168
+ fe_setup_reason: ""
169
+ fe_setup_ok: "true"
170
+ fe_repair_files: []
171
+ fe_suspected: []
172
+ be_tests_green: "not run — this lane did not generate"
173
+ be_tests_state: "not run — this lane did not generate"
174
+ fe_tests_green: "not run — this lane did not generate"
175
+ fe_tests_state: "not run — this lane did not generate"
176
+ be_uncited_after: "not checked — this lane did not generate"
177
+ be_after_cited: "false"
178
+ fe_uncited_after: "not checked — this lane did not generate"
179
+ fe_after_cited: "false"
180
+ # The verdict — votes raised by a lane that generated, refusing until its check answers.
181
+ be_tests_vote: "true"
182
+ fe_tests_vote: "true"
183
+ be_cite_vote: "true"
184
+ fe_cite_vote: "true"
185
+ tests_ok: "false"
186
+ cited_ok: "false"
187
+ after_next: "/appweaver analyze-slice — its fix loop repairs the code against the spec, or a test that is genuinely wrong"
188
+ okf_concept: ""
189
+
190
+ workflow:
191
+
192
+ # 1. The active feature and its masters.
193
+ - id: feature_paths
194
+ action: execute_script
195
+ on_error: continue # never crash raw on 'no feature set' — the gate below explains it
196
+ script: { ref: actions }
197
+ subcommand: feature-paths
198
+ outputs:
199
+ - { name: feature, from: "$.feature" }
200
+ - { name: feature_dir, from: "$.feature_dir" }
201
+ - { name: spec_file, from: "$.spec_file" }
202
+ - { name: backend_plan, from: "$.backend_plan" }
203
+ - { name: backend_plan_exists, from: "$.backend_plan_exists" }
204
+ - { name: frontend_plan, from: "$.frontend_plan" }
205
+ - { name: frontend_plan_exists, from: "$.frontend_plan_exists" }
206
+
207
+ - id: require_feature
208
+ action: verify_context
209
+ require_non_empty: feature_dir
210
+ on_empty: stop
211
+ on_empty_message: "No active feature. Run /appweaver select-feature first, then re-run fix-test-coverage."
212
+
213
+ - id: context_paths
214
+ action: execute_script
215
+ on_error: continue
216
+ script: { ref: actions }
217
+ subcommand: context-paths
218
+ outputs:
219
+ - { name: domain_model, from: "$.domain_model" }
220
+
221
+ # 2. THE LANES this developer works on, read with --feature-dir so a lane the feature has
222
+ # no surface in is not worked either.
223
+ - id: lanes
224
+ action: execute_script
225
+ on_error: continue
226
+ script: { ref: actions }
227
+ subcommand: lane-resolve
228
+ args: ["--feature-dir", "{{feature_dir}}"]
229
+ outputs:
230
+ - { name: lanes_declared, from: "$.declared" }
231
+ - { name: be_working, from: "$.backend_working" }
232
+ - { name: be_lane_id, from: "$.backend_lane_id" }
233
+ - { name: be_title, from: "$.backend_title" }
234
+ - { name: be_language, from: "$.backend_language" }
235
+ - { name: be_source_path, from: "$.backend_source_path" }
236
+ - { name: be_test_path, from: "$.backend_test_path" }
237
+ - { name: fe_working, from: "$.frontend_working" }
238
+ - { name: fe_lane_id, from: "$.frontend_lane_id" }
239
+ - { name: fe_title, from: "$.frontend_title" }
240
+ - { name: fe_language, from: "$.frontend_language" }
241
+ - { name: fe_source_path, from: "$.frontend_source_path" }
242
+ - { name: fe_test_path, from: "$.frontend_test_path" }
243
+ - { name: working_lane, from: "$.working_lane" }
244
+
245
+ - id: require_lanes
246
+ action: verify_context
247
+ require_true: lanes_declared
248
+ on_false: stop
249
+ on_false_message: "No lane is declared in .appweaver/stacks.json, so there are no tests to write. Run /appweaver select-stack first."
250
+
251
+ # 3. WHICH RUN THIS IS — decided by the argument, not by the changes on disk. A CR id
252
+ # selects that change; it must be open and name a lane you work on.
253
+ - id: mode_delta
254
+ condition: "{{USER_INPUT}}"
255
+ action: set
256
+ vars:
257
+ mode: "delta"
258
+ cite_mode: "delta"
259
+ where_text: "the ADDED / MODIFIED rows"
260
+ spec_text: "the change's delta-spec.md and its model delta, in context (the master spec.md and domain model too, for an id the change does not restate)"
261
+
262
+ - id: cr_list
263
+ condition: "{{mode}} == delta"
264
+ action: execute_script
265
+ script: { ref: actions }
266
+ subcommand: cr-list
267
+ args: ["--feature-dir", "{{feature_dir}}", "--preselect", "{{USER_INPUT}}", "--lane", "{{working_lane}}"]
268
+ outputs:
269
+ - { name: matched_label, from: "$.matched_label" }
270
+ - { name: open_ids, from: "$.ids_csv" }
271
+ - { name: preselect_hidden, from: "$.preselect_hidden" }
272
+
273
+ - id: preselect_hidden_flag
274
+ condition: "{{preselect_hidden}} != "
275
+ action: set
276
+ vars: { preselect_visible: "false" }
277
+ - id: require_visible_change
278
+ action: verify_context
279
+ require_true: preselect_visible
280
+ on_false: stop
281
+ on_false_message: "Change {{preselect_hidden}} does not touch your working lane `{{working_lane}}` (.appweaver/lane.json): its **Lanes** line names only the other lane, so its tests are that lane's developer's to write. Run /appweaver select-lane to work on that lane."
282
+
283
+ - id: require_change
284
+ condition: "{{mode}} == delta"
285
+ action: verify_context
286
+ require_non_empty: matched_label
287
+ on_empty: stop
288
+ on_empty_message: "`{{USER_INPUT}}` is not an open change of {{feature}} that names your working lane `{{working_lane}}`. Open changes listed for you: {{open_ids}}. Pass one of them — or run /appweaver fix-test-coverage with no argument to fill the master plans' gaps."
289
+
290
+ # The change this run writes tests for becomes the ACTIVE one, as implement and
291
+ # analyze-slice-delta make it: build and the gates after it resolve the change from there.
292
+ - id: activate
293
+ condition: "{{mode}} == delta"
294
+ action: execute_script
295
+ script: { ref: actions }
296
+ subcommand: cr-activate
297
+ args: ["--cr-id", "{{matched_label}}", "--feature-dir", "{{feature_dir}}"]
298
+ outputs:
299
+ - { name: active_change, from: "$.active_change" }
300
+
301
+ - id: cr_paths
302
+ condition: "{{mode}} == delta"
303
+ action: execute_script
304
+ script: { ref: actions }
305
+ subcommand: cr-paths
306
+ args: ["--cr-id", "{{matched_label}}", "--feature-dir", "{{feature_dir}}"]
307
+ outputs:
308
+ - { name: cr_id, from: "$.cr_id" }
309
+ - { name: cr_dir, from: "$.cr_dir" }
310
+ - { name: delta_spec, from: "$.delta_spec" }
311
+ - { name: delta_model, from: "$.delta_model" }
312
+ - { name: delta_backend_plan, from: "$.delta_backend_plan" }
313
+ - { name: delta_backend_plan_exists, from: "$.delta_backend_plan_exists" }
314
+ - { name: delta_frontend_plan, from: "$.delta_frontend_plan" }
315
+ - { name: delta_frontend_plan_exists, from: "$.delta_frontend_plan_exists" }
316
+ - { name: cr_lanes_csv, from: "$.lanes_csv" }
317
+ - { name: be_in_change, from: "$.touches_backend" }
318
+ - { name: fe_in_change, from: "$.touches_frontend" }
319
+ - id: cr_arg_set
320
+ condition: "{{mode}} == delta"
321
+ action: set
322
+ vars:
323
+ cr_arg: " {{cr_id}}"
324
+ after_next: "fix the code — /appweaver implement {{cr_id}} for behaviour that was never generated, or by hand — then /appweaver build and /appweaver analyze-slice-delta {{cr_id}}"
325
+
326
+ # 4. WHICH PLAN EACH LANE IS READ FROM, and which lanes are targets.
327
+ - id: be_plan_master
328
+ condition: "{{mode}} == master"
329
+ action: set
330
+ vars: { be_plan: "{{backend_plan}}", be_plan_ok: "{{backend_plan_exists}}" }
331
+ - id: be_plan_delta
332
+ condition: "{{mode}} == delta"
333
+ action: set
334
+ vars: { be_plan: "{{delta_backend_plan}}", be_plan_ok: "{{delta_backend_plan_exists}}" }
335
+ - id: fe_plan_master
336
+ condition: "{{mode}} == master"
337
+ action: set
338
+ vars: { fe_plan: "{{frontend_plan}}", fe_plan_ok: "{{frontend_plan_exists}}" }
339
+ - id: fe_plan_delta
340
+ condition: "{{mode}} == delta"
341
+ action: set
342
+ vars: { fe_plan: "{{delta_frontend_plan}}", fe_plan_ok: "{{delta_frontend_plan_exists}}" }
343
+
344
+ - id: be_needed_set
345
+ action: set
346
+ vars: { be_plan_needed: "{{be_working}}" }
347
+ - id: be_needed_not_in_change
348
+ condition: "{{be_in_change}} == false"
349
+ action: set
350
+ vars: { be_plan_needed: "false" }
351
+ - id: fe_needed_set
352
+ action: set
353
+ vars: { fe_plan_needed: "{{fe_working}}" }
354
+ - id: fe_needed_not_in_change
355
+ condition: "{{fe_in_change}} == false"
356
+ action: set
357
+ vars: { fe_plan_needed: "false" }
358
+ - id: be_target_set
359
+ condition: "{{be_plan_ok}} == true"
360
+ action: set
361
+ vars: { be_target: "{{be_plan_needed}}" }
362
+ - id: fe_target_set
363
+ condition: "{{fe_plan_ok}} == true"
364
+ action: set
365
+ vars: { fe_target: "{{fe_plan_needed}}" }
366
+
367
+ - id: warn_be_plan
368
+ condition: "{{be_plan_needed}} == true"
369
+ action: verify_context
370
+ require_true: be_plan_ok
371
+ on_false: continue
372
+ on_false_message: "NOTE — backend lane: there is no plan at {{be_plan}}, so there are no requirement ids to read for it. Author it with /appweaver plan{{cr_arg}}."
373
+ - id: warn_fe_plan
374
+ condition: "{{fe_plan_needed}} == true"
375
+ action: verify_context
376
+ require_true: fe_plan_ok
377
+ on_false: continue
378
+ on_false_message: "NOTE — frontend lane: there is no plan at {{fe_plan}}, so there are no requirement ids to read for it. Author it with /appweaver plan{{cr_arg}}."
379
+
380
+ - id: any_target_be
381
+ condition: "{{be_target}} == true"
382
+ action: set
383
+ vars: { any_target: "true" }
384
+ - id: any_target_fe
385
+ condition: "{{fe_target}} == true"
386
+ action: set
387
+ vars: { any_target: "true" }
388
+ - id: require_a_target
389
+ action: verify_context
390
+ require_true: any_target
391
+ on_false: stop
392
+ on_false_message: "No lane to fill tests for. A lane is one when you work on it (`{{working_lane}}`, .appweaver/lane.json), it has the plan this run reads and — for a change — the change names it (it names `{{cr_lanes_csv}}`). Backend: worked {{be_working}}, plan {{be_plan_ok}}; frontend: worked {{fe_working}}, plan {{fe_plan_ok}}. Run /appweaver plan{{cr_arg}} for a lane with no plan, or /appweaver select-lane to work on another lane."
393
+
394
+ # 5. THE GAP, per target lane — the same check the gates run. The slice's files come from
395
+ # its OKF concept; the check keeps the tests among them inside each lane's folders.
396
+ - id: concept_paths
397
+ action: execute_script
398
+ on_error: continue
399
+ script: { ref: xref }
400
+ subcommand: concept-paths
401
+ args: ["--feature-dir", "{{feature_dir}}"]
402
+ outputs:
403
+ - { name: slice_files, from: "$.paths" }
404
+
405
+ - id: cite_backend
406
+ condition: "{{be_target}} == true"
407
+ action: execute_script
408
+ on_error: continue
409
+ script: { ref: actions }
410
+ subcommand: test-citations
411
+ args: ["--plan", "{{be_plan}}", "--kind", "backend", "--mode", "{{cite_mode}}", "--paths", "{{slice_files}}", "--source-path", "{{be_source_path}}", "--test-path", "{{be_test_path}}", "--spec", "{{spec_file}}", "--model", "{{domain_model}}"]
412
+ outputs:
413
+ - { name: be_all_cited, from: "$.all_cited" }
414
+ - { name: be_cite_checked, from: "$.checked" }
415
+ - { name: be_cite_clean, from: "$.clean" }
416
+ - { name: be_cite_warning, from: "$.warning" }
417
+ - { name: be_advisory, from: "$.advisory" }
418
+ - { name: be_uncited_before, from: "$.uncited_text" }
419
+ - { name: be_test_files, from: "$.test_files" }
420
+ - id: cite_frontend
421
+ condition: "{{fe_target}} == true"
422
+ action: execute_script
423
+ on_error: continue
424
+ script: { ref: actions }
425
+ subcommand: test-citations
426
+ args: ["--plan", "{{fe_plan}}", "--kind", "frontend", "--mode", "{{cite_mode}}", "--paths", "{{slice_files}}", "--source-path", "{{fe_source_path}}", "--test-path", "{{fe_test_path}}", "--spec", "{{spec_file}}", "--model", "{{domain_model}}"]
427
+ outputs:
428
+ - { name: fe_all_cited, from: "$.all_cited" }
429
+ - { name: fe_cite_checked, from: "$.checked" }
430
+ - { name: fe_cite_clean, from: "$.clean" }
431
+ - { name: fe_cite_warning, from: "$.warning" }
432
+ - { name: fe_advisory, from: "$.advisory" }
433
+ - { name: fe_uncited_before, from: "$.uncited_text" }
434
+ - { name: fe_test_files, from: "$.test_files" }
435
+
436
+ # A plan the check could not read (no Traceability section, no ADDED / MODIFIED rows) is
437
+ # said here; the gap list itself is put to you in the question below.
438
+ - id: warn_be_unchecked
439
+ condition: "{{be_cite_checked}} == false"
440
+ action: verify_context
441
+ require_true: be_cite_clean
442
+ on_false: continue
443
+ on_false_message: "{{be_cite_warning}}"
444
+ - id: warn_fe_unchecked
445
+ condition: "{{fe_cite_checked}} == false"
446
+ action: verify_context
447
+ require_true: fe_cite_clean
448
+ on_false: continue
449
+ on_false_message: "{{fe_cite_warning}}"
450
+
451
+ - id: be_gap_set
452
+ condition: "{{be_all_cited}} == false"
453
+ action: set
454
+ vars: { be_gap: "true", any_gap: "true", no_gap: "false" }
455
+ - id: fe_gap_set
456
+ condition: "{{fe_all_cited}} == false"
457
+ action: set
458
+ vars: { fe_gap: "true", any_gap: "true", no_gap: "false" }
459
+ - id: note_nothing_to_fill
460
+ action: verify_context
461
+ require_true: any_gap
462
+ on_false: continue
463
+ on_false_message: "Nothing to fill — every FR- and BR- id in {{where_text}} of each lane's plan is already named by a test of {{feature}}."
464
+
465
+ # An adopted feature's tests are judged by what its spec was found to say.
466
+ - id: be_oracle_reversed
467
+ condition: "{{be_advisory}} == true"
468
+ action: set
469
+ vars: { be_oracle_rule: "THIS PLAN WAS REVERSE-ENGINEERED from the existing code (origin: reverse-engineered), so the spec describes what the code was found to do: each test asserts what the spec states, and is expected to pass. One that fails is a mismatch between the adopted spec and the code — report it, and never change the code or bend the assertion to make it pass." }
470
+ - id: fe_oracle_reversed
471
+ condition: "{{fe_advisory}} == true"
472
+ action: set
473
+ vars: { fe_oracle_rule: "THIS PLAN WAS REVERSE-ENGINEERED from the existing UI code (origin: reverse-engineered), so the spec describes what the code was found to do: each test asserts what the spec states, and is expected to pass. One that fails is a mismatch between the adopted spec and the code — report it, and never change the code or bend the assertion to make it pass." }
474
+
475
+ # 6. CONFIRM EACH LANE before anything is written, with the ids in front of you.
476
+ - id: ask_backend
477
+ condition: "{{be_gap}} == true"
478
+ action: ask
479
+ prompt: "Write the missing tests for the BACKEND lane now? Lane `{{be_lane_id}}` — {{be_title}}, {{be_language}}. No test of {{feature}} names {{be_uncited_before}} ({{where_text}} of {{be_plan}}). Tests go under {{be_test_path}}; production code is not touched."
480
+ options: ["yes", "no"]
481
+ answer_var: be_answer
482
+ - id: be_go_set
483
+ condition: "{{be_answer}} == yes"
484
+ action: set
485
+ vars: { be_go: "{{be_gap}}", any_go: "true" }
486
+ - id: ask_frontend
487
+ condition: "{{fe_gap}} == true"
488
+ action: ask
489
+ prompt: "Write the missing tests for the FRONTEND lane now? Lane `{{fe_lane_id}}` — {{fe_title}}, {{fe_language}}. No test of {{feature}} names {{fe_uncited_before}} ({{where_text}} of {{fe_plan}}). Tests go under {{fe_test_path}}; production code is not touched."
490
+ options: ["yes", "no"]
491
+ answer_var: fe_answer
492
+ - id: fe_go_set
493
+ condition: "{{fe_answer}} == yes"
494
+ action: set
495
+ vars: { fe_go: "{{fe_gap}}", any_go: "true" }
496
+
497
+ - id: resolve_pre_hooks
498
+ condition: "{{any_go}} == true"
499
+ action: execute_script
500
+ script: { ref: actions }
501
+ subcommand: resolve-hooks
502
+ args: ["--phase", "before_fix_test_coverage"]
503
+ outputs:
504
+ - { name: before_hooks, from: "$.hooks" }
505
+ - id: dispatch_pre_hooks
506
+ condition: "{{any_go}} == true"
507
+ action: dispatch_hooks
508
+ hooks_from_variable: before_hooks
509
+
510
+ # ═══════════════════════════════ BACKEND LANE ═══════════════════════════════
511
+ # The lane's files, as a path list the generation step reads with its tools.
512
+ - id: be_existing_code
513
+ condition: "{{be_go}} == true"
514
+ action: execute_script
515
+ on_error: continue
516
+ script: { ref: xref }
517
+ subcommand: concept-paths
518
+ args: ["--feature-dir", "{{feature_dir}}", "--under", "{{be_source_path}}", "--under", "{{be_test_path}}"]
519
+ outputs:
520
+ - { name: be_existing_text, from: "$.paths_text" }
521
+ - id: tech_select
522
+ condition: "{{be_go}} == true"
523
+ action: include
524
+ module: .appweaver/orchestrator/modules/tech-select.orch.yaml
525
+ with: { command: aw-spec-implement-backend }
526
+ - id: ctx_backend
527
+ condition: "{{be_go}} == true"
528
+ action: build_context
529
+ context:
530
+ - { path: "{{be_plan}}" } # THE MAP — which element realises each id
531
+ - { path: "{{delta_spec}}", optional: true } # a change's requirement text (delta path only)
532
+ - { path: "{{delta_model}}", optional: true } # the invariants a change adds (delta path only)
533
+ - { path: "{{spec_file}}", optional: true } # THE ORACLE — what each FR- states
534
+ - { path: "{{domain_model}}", optional: true } # what each BR- states, and the names the domain uses
535
+ - id: gen_backend
536
+ condition: "{{be_go}} == true"
537
+ action: agent_generate
538
+ tools: "Read,Write,Edit"
539
+ permission_mode: acceptEdits
540
+ task: |
541
+ WRITE THE MISSING TESTS for the `{{be_lane_id}}` lane ({{be_title}}, {{be_language}})
542
+ of {{feature}}. No test of this feature names these requirement ids:
543
+ {{be_uncited_before}}. They come from {{where_text}} of {{be_plan}} (in context). What
544
+ each one states is in {{spec_text}} — an FR- in the spec, a BR- in the domain model.
545
+ {{be_oracle_rule}}
546
+ The lane's files — READ the ones you need:
547
+ {{be_existing_text}}
548
+ The feature's existing tests: {{be_test_files}}
549
+
550
+ For EACH id:
551
+ 1. Read what it states, and find the code that realises it — the plan maps the id to
552
+ its element(s).
553
+ 2. If a test of this feature already exercises exactly what the id states, NAME THE ID
554
+ in that test — its name, a comment, an attribute or a tag — and write no second
555
+ test. Report it `annotated`.
556
+ 3. Otherwise WRITE a test that exercises what the id states — the behaviour, the rule,
557
+ the error it names — and asserts the outcome the text gives. Name the id in it as
558
+ `FR-004`, `FR_004` or `FR004`, whichever this language allows. Report it `written`.
559
+ 4. If the code does not realise the requirement at all, write NO test for it: a test
560
+ against missing behaviour either passes without checking anything or fails for a
561
+ reason no test can fix. Report it `not-implemented` — generating the code is
562
+ /appweaver implement's job.
563
+ Rules:
564
+ - TESTS ONLY. Never create, edit or delete production code. Tests go under
565
+ `{{be_test_path}}`, in the framework, layout and naming this feature's existing
566
+ tests use. The one edit allowed outside it: registering a new test project in the
567
+ file the lane's test command discovers tests through (e.g. a .NET solution file).
568
+ - Never weaken, delete or rewrite an existing test; adding an id to one is the only
569
+ change allowed to it.
570
+ - Use the domain model's names for entities, fields and states.
571
+ - Report every listed id in an `ids` list in your closing JSON block, spelled as
572
+ listed: {"id": "<id>", "status": "written" | "annotated" | "not-implemented" |
573
+ "blocked", "test": "<file>", "reason": "<why, when not written or annotated>"}.
574
+ outputs:
575
+ - { name: be_files, from: "$.files" }
576
+ - { name: be_ids, from: "$.ids" }
577
+ - id: be_ids_none
578
+ condition: "{{be_ids}} == "
579
+ action: set
580
+ vars: { be_ids: "(none reported)" }
581
+
582
+ - id: be_tests
583
+ condition: "{{be_go}} == true"
584
+ action: execute_script
585
+ on_error: continue
586
+ script: { ref: actions }
587
+ subcommand: test-run
588
+ args: ["--feature-dir", "{{feature_dir}}", "--scope", "slice", "--kind", "backend"]
589
+ outputs:
590
+ - { name: be_test_ok, from: "$.fence_green" }
591
+ - { name: be_test_ran, from: "$.ran" }
592
+ - { name: be_test_excerpt, from: "$.failing_excerpt" }
593
+ - { name: be_test_log, from: "$.log_tail" }
594
+ - { name: be_no_setup, from: "$.no_setup" }
595
+ - { name: be_setup_reason, from: "$.setup_reason" }
596
+
597
+ # AT MOST TWO MECHANICAL REPAIRS, each re-run. A lane with no test setup has nothing a
598
+ # repair can help, and a pass that changed nothing has nothing left to try.
599
+ - id: be_repair_loop
600
+ condition: "{{be_go}} == true"
601
+ action: loop
602
+ while: "{{be_test_ok}} != true"
603
+ max_iterations: 2
604
+ steps:
605
+ - id: be_no_setup_break
606
+ action: break
607
+ condition: "{{be_no_setup}} == true"
608
+ - id: be_repair
609
+ action: agent_generate
610
+ tools: "Read,Write,Edit"
611
+ permission_mode: acceptEdits
612
+ task: |
613
+ THE TESTS DO NOT PASS after the missing tests were written for the
614
+ `{{be_lane_id}}` lane. Failing lines (each labelled with its command and exit code):
615
+ {{be_test_excerpt}}
616
+ Log tail: {{be_test_log}}
617
+ You may change ONLY the files this run wrote or annotated: {{be_files}}.
618
+ Repair what is MECHANICAL in them — a test that does not compile, a missing import,
619
+ the wrong fixture, setup, constructor or path. Never change production code, never
620
+ touch another test, and never change what an assertion EXPECTS: the expected
621
+ outcome comes from the requirement's text.
622
+ {{be_oracle_rule}}
623
+ A test that runs and fails its ASSERTION has found a place where the code does not
624
+ do what the requirement says: leave it as it is and report it in a
625
+ `suspected_defects` list in your closing JSON block — {"id": "<id>", "test":
626
+ "<file>", "failure": "<what was expected, and what happened>"}. A failure in a test
627
+ this run did not write is not yours to repair; say so. If no failure is mechanical,
628
+ change nothing.
629
+ outputs:
630
+ - { name: be_repair_files, from: "$.files" }
631
+ - { name: be_suspected, from: "$.suspected_defects" }
632
+ - id: be_repair_idle_break
633
+ action: break
634
+ condition: "{{be_repair_files}} == []"
635
+ - id: be_retest
636
+ action: execute_script
637
+ on_error: continue
638
+ script: { ref: actions }
639
+ subcommand: test-run
640
+ args: ["--feature-dir", "{{feature_dir}}", "--scope", "slice", "--kind", "backend"]
641
+ outputs:
642
+ - { name: be_test_ok, from: "$.fence_green" }
643
+ - { name: be_test_ran, from: "$.ran" }
644
+ - { name: be_test_excerpt, from: "$.failing_excerpt" }
645
+ - { name: be_test_log, from: "$.log_tail" }
646
+
647
+ - id: warn_be_not_run
648
+ condition: "{{be_go}} == true"
649
+ action: verify_context
650
+ require_true: be_test_ran
651
+ on_false: continue
652
+ on_false_message: "NOTE — the backend lane declares no test command, so the tests just written were not run. Their citations are checked; whether they pass is not."
653
+ - id: be_setup_ok_flag
654
+ condition: "{{be_no_setup}} == true"
655
+ action: set
656
+ vars: { be_setup_ok: "false" }
657
+ - id: warn_be_no_setup
658
+ condition: "{{be_go}} == true"
659
+ action: verify_context
660
+ require_true: be_setup_ok
661
+ on_false: continue
662
+ on_false_message: "WARNING — the backend lane's test command found nothing to run ({{be_setup_reason}}), so the tests just written were not run. Wire up the lane's tests (the runner or test project its command expects, or the command in .appweaver/stacks.json); analyze-slice fails a slice with no tests to run."
663
+
664
+ # The tests join the feature's concept, so the gates read them as this feature's own.
665
+ - id: be_concept
666
+ condition: "{{be_go}} == true"
667
+ action: execute_script
668
+ on_error: continue
669
+ script: { ref: xref }
670
+ subcommand: concept-upsert
671
+ args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{be_files}}", "--merge"]
672
+ outputs:
673
+ - { name: okf_concept, from: "$.concept" }
674
+
675
+ - id: be_recheck
676
+ condition: "{{be_go}} == true"
677
+ action: execute_script
678
+ on_error: continue
679
+ script: { ref: actions }
680
+ subcommand: test-citations
681
+ args: ["--plan", "{{be_plan}}", "--kind", "backend", "--mode", "{{cite_mode}}", "--paths", "{{slice_files}}", "--paths", "{{be_files}}", "--source-path", "{{be_source_path}}", "--test-path", "{{be_test_path}}", "--spec", "{{spec_file}}", "--model", "{{domain_model}}"]
682
+ outputs:
683
+ - { name: be_after_cited, from: "$.all_cited" }
684
+ - { name: be_uncited_after, from: "$.uncited_text" }
685
+
686
+ # ═══════════════════════════════ FRONTEND LANE ══════════════════════════════
687
+ # Its rules REPLACE the backend's, so each lane's tests are written under its own set.
688
+ - id: fe_existing_code
689
+ condition: "{{fe_go}} == true"
690
+ action: execute_script
691
+ on_error: continue
692
+ script: { ref: xref }
693
+ subcommand: concept-paths
694
+ args: ["--feature-dir", "{{feature_dir}}", "--under", "{{fe_source_path}}", "--under", "{{fe_test_path}}"]
695
+ outputs:
696
+ - { name: fe_existing_text, from: "$.paths_text" }
697
+ - id: tech_select_ui
698
+ condition: "{{fe_go}} == true"
699
+ action: include
700
+ module: .appweaver/orchestrator/modules/tech-select.orch.yaml
701
+ with: { command: aw-spec-implement-frontend, replace: "true" }
702
+ - id: ctx_frontend
703
+ condition: "{{fe_go}} == true"
704
+ action: build_context
705
+ context:
706
+ - { path: "{{fe_plan}}" }
707
+ - { path: "{{delta_spec}}", optional: true }
708
+ - { path: "{{delta_model}}", optional: true }
709
+ - { path: "{{spec_file}}", optional: true }
710
+ - { path: "{{domain_model}}", optional: true }
711
+ - id: gen_frontend
712
+ condition: "{{fe_go}} == true"
713
+ action: agent_generate
714
+ tools: "Read,Write,Edit"
715
+ permission_mode: acceptEdits
716
+ task: |
717
+ WRITE THE MISSING TESTS for the `{{fe_lane_id}}` lane ({{fe_title}}, {{fe_language}})
718
+ of {{feature}}. No test of this feature names these requirement ids:
719
+ {{fe_uncited_before}}. They come from {{where_text}} of {{fe_plan}} (in context). What
720
+ each one states is in {{spec_text}} — an FR- in the spec, a BR- in the domain model.
721
+ {{fe_oracle_rule}}
722
+ The lane's files — READ the ones you need:
723
+ {{fe_existing_text}}
724
+ The feature's existing tests: {{fe_test_files}}
725
+
726
+ For EACH id:
727
+ 1. Read what it states, and find the screen, component, state or binding that
728
+ realises it — the plan maps the id to its element(s).
729
+ 2. If a test of this feature already exercises exactly what the id states, NAME THE ID
730
+ in that test — its name, a comment, an attribute or a tag — and write no second
731
+ test. Report it `annotated`.
732
+ 3. Otherwise WRITE a test that exercises what the id states — the interaction, the
733
+ state the user sees, the rule the UI enforces for feedback — and asserts the
734
+ outcome the text gives. A snapshot alone is not a test. Name the id in it as
735
+ `FR-004`, `FR_004` or `FR004`, whichever this language allows. Report it `written`.
736
+ 4. If the UI does not realise the requirement at all, write NO test for it and report
737
+ it `not-implemented` — generating the code is /appweaver implement's job.
738
+ Rules:
739
+ - TESTS ONLY. Never create, edit or delete production code, the app shell, the global
740
+ layout or the design system. Tests go under `{{fe_test_path}}`, in the framework,
741
+ layout and naming this feature's existing tests use.
742
+ - Never weaken, delete or rewrite an existing test; adding an id to one is the only
743
+ change allowed to it.
744
+ - Labels, states and messages in an assertion use the domain model's ubiquitous
745
+ language.
746
+ - Report every listed id in an `ids` list in your closing JSON block, spelled as
747
+ listed: {"id": "<id>", "status": "written" | "annotated" | "not-implemented" |
748
+ "blocked", "test": "<file>", "reason": "<why, when not written or annotated>"}.
749
+ outputs:
750
+ - { name: fe_files, from: "$.files" }
751
+ - { name: fe_ids, from: "$.ids" }
752
+ - id: fe_ids_none
753
+ condition: "{{fe_ids}} == "
754
+ action: set
755
+ vars: { fe_ids: "(none reported)" }
756
+
757
+ - id: fe_tests
758
+ condition: "{{fe_go}} == true"
759
+ action: execute_script
760
+ on_error: continue
761
+ script: { ref: actions }
762
+ subcommand: test-run
763
+ args: ["--feature-dir", "{{feature_dir}}", "--scope", "slice", "--kind", "frontend"]
764
+ outputs:
765
+ - { name: fe_test_ok, from: "$.fence_green" }
766
+ - { name: fe_test_ran, from: "$.ran" }
767
+ - { name: fe_test_excerpt, from: "$.failing_excerpt" }
768
+ - { name: fe_test_log, from: "$.log_tail" }
769
+ - { name: fe_no_setup, from: "$.no_setup" }
770
+ - { name: fe_setup_reason, from: "$.setup_reason" }
771
+
772
+ - id: fe_repair_loop
773
+ condition: "{{fe_go}} == true"
774
+ action: loop
775
+ while: "{{fe_test_ok}} != true"
776
+ max_iterations: 2
777
+ steps:
778
+ - id: fe_no_setup_break
779
+ action: break
780
+ condition: "{{fe_no_setup}} == true"
781
+ - id: fe_repair
782
+ action: agent_generate
783
+ tools: "Read,Write,Edit"
784
+ permission_mode: acceptEdits
785
+ task: |
786
+ THE TESTS DO NOT PASS after the missing tests were written for the
787
+ `{{fe_lane_id}}` lane. Failing lines (each labelled with its command and exit code):
788
+ {{fe_test_excerpt}}
789
+ Log tail: {{fe_test_log}}
790
+ You may change ONLY the files this run wrote or annotated: {{fe_files}}.
791
+ Repair what is MECHANICAL in them — a test that does not compile, a missing import,
792
+ the wrong render setup, mock, provider or path. Never change production code, never
793
+ touch another test, and never change what an assertion EXPECTS: the expected
794
+ outcome comes from the requirement's text.
795
+ {{fe_oracle_rule}}
796
+ A test that runs and fails its ASSERTION has found a place where the UI does not do
797
+ what the requirement says: leave it as it is and report it in a
798
+ `suspected_defects` list in your closing JSON block — {"id": "<id>", "test":
799
+ "<file>", "failure": "<what was expected, and what happened>"}. A failure in a test
800
+ this run did not write is not yours to repair; say so. If no failure is mechanical,
801
+ change nothing.
802
+ outputs:
803
+ - { name: fe_repair_files, from: "$.files" }
804
+ - { name: fe_suspected, from: "$.suspected_defects" }
805
+ - id: fe_repair_idle_break
806
+ action: break
807
+ condition: "{{fe_repair_files}} == []"
808
+ - id: fe_retest
809
+ action: execute_script
810
+ on_error: continue
811
+ script: { ref: actions }
812
+ subcommand: test-run
813
+ args: ["--feature-dir", "{{feature_dir}}", "--scope", "slice", "--kind", "frontend"]
814
+ outputs:
815
+ - { name: fe_test_ok, from: "$.fence_green" }
816
+ - { name: fe_test_ran, from: "$.ran" }
817
+ - { name: fe_test_excerpt, from: "$.failing_excerpt" }
818
+ - { name: fe_test_log, from: "$.log_tail" }
819
+
820
+ - id: warn_fe_not_run
821
+ condition: "{{fe_go}} == true"
822
+ action: verify_context
823
+ require_true: fe_test_ran
824
+ on_false: continue
825
+ on_false_message: "NOTE — the frontend lane declares no test command, so the tests just written were not run. Their citations are checked; whether they pass is not."
826
+ - id: fe_setup_ok_flag
827
+ condition: "{{fe_no_setup}} == true"
828
+ action: set
829
+ vars: { fe_setup_ok: "false" }
830
+ - id: warn_fe_no_setup
831
+ condition: "{{fe_go}} == true"
832
+ action: verify_context
833
+ require_true: fe_setup_ok
834
+ on_false: continue
835
+ on_false_message: "WARNING — the frontend lane's test command found nothing to run ({{fe_setup_reason}}), so the tests just written were not run. Wire up the lane's tests (the runner its command expects, or the command in .appweaver/stacks.json); analyze-slice fails a slice with no tests to run."
836
+
837
+ - id: fe_concept
838
+ condition: "{{fe_go}} == true"
839
+ action: execute_script
840
+ on_error: continue
841
+ script: { ref: xref }
842
+ subcommand: concept-upsert
843
+ args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{fe_files}}", "--merge"]
844
+ outputs:
845
+ - { name: okf_concept, from: "$.concept" }
846
+
847
+ - id: fe_recheck
848
+ condition: "{{fe_go}} == true"
849
+ action: execute_script
850
+ on_error: continue
851
+ script: { ref: actions }
852
+ subcommand: test-citations
853
+ args: ["--plan", "{{fe_plan}}", "--kind", "frontend", "--mode", "{{cite_mode}}", "--paths", "{{slice_files}}", "--paths", "{{fe_files}}", "--source-path", "{{fe_source_path}}", "--test-path", "{{fe_test_path}}", "--spec", "{{spec_file}}", "--model", "{{domain_model}}"]
854
+ outputs:
855
+ - { name: fe_after_cited, from: "$.all_cited" }
856
+ - { name: fe_uncited_after, from: "$.uncited_text" }
857
+
858
+ # ════════════════════════════════ THE VERDICT ════════════════════════════════
859
+ # Each generating lane votes its test result and its re-check; a lane that did not
860
+ # generate does not vote.
861
+ - id: be_tests_vote_set
862
+ condition: "{{be_go}} == true"
863
+ action: set
864
+ vars:
865
+ be_tests_vote: "{{be_test_ok}}"
866
+ be_cite_vote: "{{be_after_cited}}"
867
+ be_tests_green: "{{be_test_ok}}"
868
+ be_tests_state: "green={{be_test_ok}} · failing lines: {{be_test_excerpt}} · suspected defects: {{be_suspected}}"
869
+ - id: fe_tests_vote_set
870
+ condition: "{{fe_go}} == true"
871
+ action: set
872
+ vars:
873
+ fe_tests_vote: "{{fe_test_ok}}"
874
+ fe_cite_vote: "{{fe_after_cited}}"
875
+ fe_tests_green: "{{fe_test_ok}}"
876
+ fe_tests_state: "green={{fe_test_ok}} · failing lines: {{fe_test_excerpt}} · suspected defects: {{fe_suspected}}"
877
+ # Tests that could not run were said above; they are not a failing suite.
878
+ - id: be_tests_vote_no_setup
879
+ condition: "{{be_setup_ok}} == false"
880
+ action: set
881
+ vars: { be_tests_vote: "true" }
882
+ - id: fe_tests_vote_no_setup
883
+ condition: "{{fe_setup_ok}} == false"
884
+ action: set
885
+ vars: { fe_tests_vote: "true" }
886
+ - id: verdict_seed
887
+ action: set
888
+ vars: { tests_ok: "true", cited_ok: "true" }
889
+ - id: tests_clear_backend
890
+ condition: "{{be_tests_vote}} != true"
891
+ action: set
892
+ vars: { tests_ok: "false" }
893
+ - id: tests_clear_frontend
894
+ condition: "{{fe_tests_vote}} != true"
895
+ action: set
896
+ vars: { tests_ok: "false" }
897
+ - id: cited_clear_backend
898
+ condition: "{{be_cite_vote}} != true"
899
+ action: set
900
+ vars: { cited_ok: "false" }
901
+ - id: cited_clear_frontend
902
+ condition: "{{fe_cite_vote}} != true"
903
+ action: set
904
+ vars: { cited_ok: "false" }
905
+
906
+ # A RED SUITE FIRST: a failing assertion is a finding about the code, and it is the more
907
+ # important of the two.
908
+ - id: tests_gate
909
+ action: verify_context
910
+ require_true: tests_ok
911
+ on_false: stop
912
+ on_false_message: >
913
+ The tests do not pass after the missing ones were written and repaired as far as a
914
+ test may be. Backend: {{be_tests_state}}. Frontend: {{fe_tests_state}}. A suspected defect is a new test that runs and
915
+ fails its assertion — the code does not do what the requirement says. The tests are
916
+ kept as the evidence. Next: {{after_next}}. For an adopted feature, a failing assertion
917
+ is a mismatch between its spec and its code: correct the spec through a change
918
+ (/appweaver specify), never the code to match a test.
919
+
920
+ - id: cited_gate
921
+ action: verify_context
922
+ require_true: cited_ok
923
+ on_false: stop
924
+ on_false_message: >
925
+ Still named by no test after this run — backend: {{be_uncited_after}}; frontend:
926
+ {{fe_uncited_after}}. What the model reported per id — backend: {{be_ids}};
927
+ frontend: {{fe_ids}}. An id reported not-implemented has no code to test:
928
+ /appweaver implement{{cr_arg}} generates it, then run /appweaver
929
+ fix-test-coverage{{cr_arg}} again. For any other id, run this again or name the id in
930
+ the test that exercises it by hand.
931
+
932
+ - id: resolve_post_hooks
933
+ condition: "{{any_go}} == true"
934
+ action: execute_script
935
+ script: { ref: actions }
936
+ subcommand: resolve-hooks
937
+ args: ["--phase", "after_fix_test_coverage"]
938
+ outputs:
939
+ - { name: after_hooks, from: "$.hooks" }
940
+ - id: dispatch_post_hooks
941
+ condition: "{{any_go}} == true"
942
+ action: dispatch_hooks
943
+ hooks_from_variable: after_hooks
944
+
945
+ outputs:
946
+ feature_dir: "{{feature_dir}}"
947
+ mode: "{{mode}}" # master | delta
948
+ cr_id: "{{cr_id}}"
949
+ working_lane: "{{working_lane}}"
950
+ # Per lane: the ids no test named before this run, what the model reported for each, and
951
+ # what the script found after it. `none` after = every id is now named by a test.
952
+ backend_uncited_before: "{{be_uncited_before}}"
953
+ backend_ids: "{{be_ids}}"
954
+ backend_files: "{{be_files}}"
955
+ backend_tests_green: "{{be_tests_green}}"
956
+ backend_suspected_defects: "{{be_suspected}}"
957
+ backend_uncited_after: "{{be_uncited_after}}"
958
+ frontend_uncited_before: "{{fe_uncited_before}}"
959
+ frontend_ids: "{{fe_ids}}"
960
+ frontend_files: "{{fe_files}}"
961
+ frontend_tests_green: "{{fe_tests_green}}"
962
+ frontend_suspected_defects: "{{fe_suspected}}"
963
+ frontend_uncited_after: "{{fe_uncited_after}}"
964
+ okf_concept: "{{okf_concept}}"
965
+ next: "MASTER (no argument): /appweaver analyze-slice — it re-runs the tests and checks every FR- and BR- the plans trace is named by a test. CHANGE (a CR id): /appweaver build, then /appweaver analyze-slice-delta {{cr_id}}. Review the new tests with git diff either way: the script proves each id is named and the suite is green, not that a test asserts what its requirement says."