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.
- package/README.md +76 -81
- package/assets/.appweaver/DESIGN-FORMAT.md +56 -17
- package/assets/.appweaver/STACK-PROFILES.md +95 -60
- package/assets/.appweaver/orchestrator/LAUNCHER.md +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +173 -32
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +29 -1
- package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +7 -4
- package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +49 -60
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +58 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +26 -8
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +20 -16
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +30 -6
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +9 -7
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +5 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +341 -36
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +254 -43
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +248 -68
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +251 -20
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +316 -10
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +6 -47
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +62 -16
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +132 -9
- package/assets/.appweaver/orchestrator/workflows/aw-spec-fix-test-coverage.orch.yaml +965 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +222 -169
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +28 -12
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +453 -116
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +7 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +83 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +45 -2
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +324 -74
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +275 -44
- package/assets/.appweaver/scripts/python/aw-foundation.py +16 -13
- package/assets/.appweaver/scripts/python/aw-gaps.py +11 -3
- package/assets/.appweaver/scripts/python/aw-model-guard.py +295 -0
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +412 -3
- package/assets/.appweaver/scripts/python/aw-plangate.py +23 -16
- package/assets/.appweaver/scripts/python/aw-stack.py +1794 -265
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +0 -2
- package/assets/.appweaver/stack-profiles/java-spring.json +0 -2
- package/assets/.appweaver/templates/api-plan-template.md +107 -0
- package/assets/.appweaver/templates/delta-api-plan-template.md +87 -0
- package/assets/.appweaver/templates/delta-design-template.md +25 -23
- package/assets/.appweaver/templates/delta-spec-template.md +3 -3
- package/assets/.appweaver/templates/design-template.md +20 -42
- package/assets/.appweaver/templates/domain-model-template.md +3 -0
- package/assets/.appweaver/templates/ui-delta-design-template.md +11 -8
- package/assets/.appweaver/templates/ui-design-template.md +4 -4
- package/assets/foundations/foundation.spec.template.txt +25 -18
- package/assets/guardrails/README.md +61 -25
- package/assets/guardrails/methodology/ux/accessibility-standards.md +38 -0
- package/assets/guardrails/methodology/ux/content-design.md +34 -0
- package/assets/guardrails/methodology/ux/interaction-states.md +45 -0
- package/assets/guardrails/methodology/ux/journey-mapping.md +41 -0
- package/assets/guardrails/project-specific/domain/domain-requirements.md +25 -38
- package/assets/guardrails/project-specific/domain-rule-bindings.md +33 -69
- package/assets/guardrails/project-specific/frontend/ui-requirements.md +2 -2
- package/assets/guardrails/project-specific/service/technical-requirements.md +2 -2
- package/assets/guardrails/project-specific/tech-rule-bindings.md +16 -13
- package/assets/guardrails/sample-healthcare/domain/domain-requirements.md +58 -0
- package/assets/guardrails/sample-healthcare/domain-rule-bindings.md +138 -0
- package/assets/guardrails/sample-healthcare/frontend/ui-requirements.md +43 -0
- package/assets/guardrails/sample-healthcare/service/technical-requirements.md +40 -0
- package/assets/guardrails/sample-healthcare/tech-rule-bindings.md +303 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +216 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/config.js +1 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/foundation.d.ts +36 -0
- package/dist/utils/foundation.d.ts.map +1 -0
- package/dist/utils/foundation.js +109 -0
- package/dist/utils/foundation.js.map +1 -0
- package/dist/utils/paths.js +1 -1
- package/dist/utils/paths.js.map +1 -1
- 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."
|