aw-spec-cli 0.1.16 → 0.1.18

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 (70) hide show
  1. package/assets/.appweaver/DESIGN-FORMAT.md +18 -18
  2. package/assets/.appweaver/ORCH-DIALECT.md +1 -3
  3. package/assets/.appweaver/RULE-PARTITION.md +29 -13
  4. package/assets/.appweaver/STACK-PROFILES.md +10 -9
  5. package/assets/.appweaver/orchestrator/.state/smoke.state.json +32 -0
  6. package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +4 -1
  7. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +251 -0
  8. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +104 -35
  9. package/assets/.appweaver/orchestrator/orchestrator.py +5 -4
  10. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +94 -36
  11. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +1 -1
  12. package/assets/.appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md +115 -0
  13. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +2 -9
  14. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +16 -40
  15. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +38 -17
  16. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +8 -8
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +10 -11
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +673 -0
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +535 -0
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +643 -0
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +84 -701
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +7 -7
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +16 -14
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +2 -2
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +243 -144
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +877 -578
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -4
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +32 -91
  30. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
  31. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +836 -514
  32. package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +2 -2
  33. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +132 -0
  34. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +38 -5
  35. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +3 -3
  36. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +271 -979
  37. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1085 -3385
  38. package/assets/.appweaver/scripts/bash/aw-xref.sh +1 -2
  39. package/assets/.appweaver/scripts/bash/common.sh +47 -0
  40. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +914 -3220
  41. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
  42. package/assets/.appweaver/scripts/python/aw-payload-parity.py +19 -3
  43. package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
  44. package/assets/.appweaver/scripts/python/aw-xref.py +86 -350
  45. package/assets/.appweaver/templates/constitution-ui-template.md +1 -1
  46. package/assets/.appweaver/templates/delta-design-template.md +120 -30
  47. package/assets/.appweaver/templates/delta-domain-model-template.md +81 -0
  48. package/assets/.appweaver/templates/delta-spec-template.md +43 -17
  49. package/assets/.appweaver/templates/design-template.md +68 -98
  50. package/assets/.appweaver/templates/domain-model-template.md +130 -0
  51. package/assets/.appweaver/templates/okf-concept-template.md +2 -2
  52. package/assets/.appweaver/templates/source-map-template.md +3 -3
  53. package/assets/.appweaver/templates/spec-template.md +100 -122
  54. package/assets/.appweaver/templates/ui-delta-design-template.md +159 -0
  55. package/assets/.appweaver/templates/ui-design-template.md +46 -75
  56. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +7 -18
  57. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +34 -39
  58. package/assets/guardrails/requirement-spec.txt +2 -2
  59. package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
  60. package/package.json +1 -1
  61. package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +0 -105
  62. package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +0 -87
  63. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
  64. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -813
  65. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
  66. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -663
  67. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -600
  68. package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -1147
  69. package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1121
  70. package/assets/.appweaver/templates/story-template.md +0 -178
@@ -1,84 +1,52 @@
1
1
  # =============================================================================
2
- # aw-spec-analyze-slice (orchestrator) — the spec ↔ CODE conformance GATE
2
+ # aw-spec-analyze-slice (orchestrator) — the design↔code conformance GATE
3
3
  #
4
4
  # The second of the two discrete conformance gates on the forward path, and the
5
- # sibling of analyze-design:
6
- # analyze-design gates spec ↔ feature plan — STATIC, before implement
7
- # analyze-slice gates spec ↔ CODE — DYNAMIC, after build
8
- #
9
- # THE SPEC IS THE ORACLE; THE DESIGN IS THE BRIDGE. This gate used to score code
10
- # against the design, which only ever proved that two documents agreed with each
11
- # other. The contract is spec.md — its FR-/BR- wording, and the CN-/DC-/IC-/UX- a
12
- # slice is bound by — and this is the stage that asks whether the code delivers it.
13
- # The design is how the question is ANSWERABLE: the story's design pass names which
14
- # elements realise which id, and the feature plan says where those elements live in
15
- # the architecture. Trace runs spec id → design element → code, and a break at either
16
- # hop is a different finding with a different route:
17
- # the code does not deliver an id the design maps → the code is wrong (implement)
18
- # no design maps an in-scope id at all → the BRIDGE is broken (story-plan)
19
- # the code is right and the spec words it wrongly → the spec is wrong (sync-spec)
20
- # The bridge's own integrity is REPORTED, never gated: a documented gap in a design
21
- # does not make working code fail its contract.
22
- #
23
- # SCOPED TO A STORY WHEN ONE IS NAMED. `/appweaver analyze-slice Order-002` judges that
24
- # story's ids; with no story named the whole feature is judged, MINUS every id that has
25
- # an open story, because in-flight work is not drift (law 39). And a story narrows the
26
- # QUESTION, not the file set: the code under analysis is always the slice's whole OKF
27
- # file list, both lanes, because a requirement can only be judged against everything
28
- # that could realise it.
29
- #
30
- # THE SPEC IS THE WORDING; THE STORY IS THE EXTENT. A story rarely delivers a whole
31
- # requirement — its "## Requirements delivered" section says which part — so a
32
- # story-scoped run judges the code against the spec's wording of that part alone. A
33
- # slice is never failed for the half of a requirement it never claimed.
34
- #
35
- # FIVE PASSES over the slice — its code, its paired tests, its OKF concept:
5
+ # sibling of analyze-plan:
6
+ # analyze-plan gates spec ↔ plan — STATIC, before implement
7
+ # analyze-slice gates plan ↔ slice — DYNAMIC, after build
8
+ # "Slice" is the unit under analysis: the aggregate's code, its paired tests, and
9
+ # its OKF knowledge concept. Three passes over it —
36
10
  # 1. BEHAVIOUR run the tests (paired to the spec's acceptance criteria by the
37
11
  # design docs); on red, fix up to 3×.
38
- # 2. ORACLE a TECHNICAL story delivers no requirement, so the unchanged test
39
- # suite is the only thing that can say whether behaviour moved: a
40
- # MODIFIED test fails it, red or green (law 32).
41
- # 3. CONFORMANCE check the code against the domain gate overlays (bounded repair).
42
- # 4. INTEGRITY re-run every referring aggregate's tests read-only; a break stops
43
- # the gate and is written up for a human.
44
- # 5. TRACE spec ↔ code, through the design. The verdict this gate is about.
45
- # Then the report + the machine-readable verdict, then the stop. Every fix made along
46
- # the way is merged into the slice's OKF concept, which is also where the file list is
47
- # READ from (`aw-xref concept-coverage`, both lanes — `concept-paths` reads only
48
- # `source_files`, so since implement-ui began writing `ui_source_files` a UI-bearing
49
- # requirement had no UI code in view to be traced to).
12
+ # 2. CONFORMANCE check the code against the domain gate overlays (validate-only).
13
+ # 3. KNOWLEDGE refresh + review the slice's OKF concept (advisory).
14
+ # Every fix made here is recorded in that concept, which is also where the slice's
15
+ # file list is READ from (2.3.0 — `aw-xref concept-paths`, replacing the retired
16
+ # `design-paths` scan for ```lang path=… fences that the tech-agnostic backend-plan.md no
17
+ # longer carries; the paired `design-reconcile` write-back is retired with it, its job
18
+ # already done by the concept-upsert --merge steps in pass 3).
50
19
  #
51
20
  # TWO POSITIONS, one gate — and in BOTH the master spec.md is the whole truth:
52
21
  # BOOTSTRAP implement → build → analyze-slice → run
53
- # CHANGE implement-delta → build → finalize-delta → analyze-slice → close-delta
22
+ # CHANGE implement (the delta) → build → analyze-slice-delta → finalize-delta
23
+ # → analyze-slice → close-delta
54
24
  # On the CHANGE path this gate deliberately runs AFTER finalize-delta, which has already
55
- # merged the delta into spec.md + the plans — so the master carries the change and needs
25
+ # merged the delta into spec.md + domain-model.md — so the master carries the change and needs
56
26
  # no delta-awareness here. It runs BEFORE close-delta, so the change is still OPEN: a red
57
27
  # result is recoverable ("fix and re-run"), not a defect on an already-shipped version.
58
28
  # The only thing this gate reads the open change for is where to send you next.
59
29
  #
60
- # IT REPAIRS TWO THINGS AND AUTHORS NOTHING. A red test and a domain-overlay violation
61
- # are mechanical, local and have a named rule, so they are fixed in place and recorded.
62
- # A missing requirement is not: it is feature work, and writing it here would produce
63
- # code that no design pass names and no gate ever conformed — a gate becoming an author,
64
- # which is the same reason analyze-design refuses to scaffold the plan it gates.
30
+ # IT HAS A DELTA TWIN, AND THE TWO ASK THE SAME QUESTION OF DIFFERENT SCOPES.
31
+ # analyze-slice-delta runs one stage earlier, before the merge, and asks whether the code
32
+ # does what THE CHANGE'S delta-spec asked for — the same requirements-anchored question this
33
+ # gate asks of the whole spec, narrowed to one change's ids and answered while the delta is
34
+ # still the only document that carries them. This gate cannot substitute for it: it is
35
+ # test-driven, and a requirement that was never implemented leaves NO test rather than a
36
+ # failing one, so nothing here fails on it. What this gate does that its twin cannot is
37
+ # score the WHOLE slice, against masters that carry the change, with the fix loops and the
38
+ # referrer blast radius.
65
39
  #
66
- # Launch: /appweaver analyze-slice [<story-id>]
40
+ # Launch: /appweaver analyze-slice [strategy]
67
41
  # Prereq: /appweaver build (compiled code for the active feature).
68
42
  # =============================================================================
69
43
 
70
44
  name: aw-spec-analyze-slice
71
- description: "The spec ↔ code conformance gate — traces every in-scope requirement from spec.md through the design that bridges it to the code that realises it, and fails the slice when the code does not deliver the contract. Scoped to one story when named, to the feature minus its in-flight ids when not. Runs the slice's tests first (fixing red, bounded), holds a technical story to the unchanged-suite oracle, checks the code against the domain gate overlays, verifies the change broke no referring aggregate, refreshes the OKF concept, then writes a traceability report and a machine-readable verdict beside it. Sits after finalize-delta on the change path, so the master spec it scores against already carries the change, and before close-delta, so a red gate leaves the change open and recoverable."
72
- version: 3.0.0
45
+ description: The design↔code conformance gate — run the slice's tests against its acceptance criteria, check the code against the domain gate overlays, verify the change did not break any referring aggregate (the OKF referrer blast radius) and write a triage report before stopping if it did, refresh + review its OKF concept, and record every fix in it. Sits after finalize-delta on the change path, so the master spec it scores against already carries the change, and before close-delta, so a red gate leaves the change open and recoverable.
46
+ version: 2.3.0
73
47
  agent_agnostic: true
74
48
  dialect: aw-spec.orchestrator/v1
75
49
 
76
- inputs:
77
- user_input:
78
- description: Optional story id to scope the gate to (e.g. "Order-002"). With none, the whole feature is judged minus its in-flight ids.
79
- required: false
80
- bind: USER_INPUT
81
-
82
50
  scripts:
83
51
  actions:
84
52
  sh: .appweaver/scripts/bash/aw-spec-actions.sh
@@ -88,8 +56,8 @@ scripts:
88
56
  ps1: .appweaver/scripts/powershell/aw-xref.ps1
89
57
 
90
58
  # No hardcoded technical standards seed. The technical rule files the code/conformance
91
- # passes check against (testing + coding-standards) are CONFIGURATION, loaded per-command
92
- # from guardrails/projects/<project>/tech-rule-bindings.md under "## /aw-spec-analyze-slice"
59
+ # passes check against (testing + coding-standards) are now CONFIGURATION, loaded per-
60
+ # command from guardrails/projects/<project>/tech-rule-bindings.md under "## /aw-spec-analyze-slice"
93
61
  # by the tech-select module (see the tech_select step below). The DOMAIN gate overlays are
94
62
  # still loaded separately, as before (resolve_gate_overlays → load_gate_overlays). This gate
95
63
  # does not reference constitution.technical.md. The seed below is behaviour-only — the kit's
@@ -99,99 +67,24 @@ standards:
99
67
  - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
100
68
 
101
69
  vars:
102
- # --- the feature and its documents --------------------------------------
103
- feature_dir: ""
104
- spec_file: ""
105
- spec_exists: "false"
106
- be_plan: ""
107
- be_plan_exists: "false"
108
- fe_plan: ""
109
- fe_plan_exists: "false"
110
- # --- scope --------------------------------------------------------------
111
- # Defaults describe the FEATURE-scoped run, which is what happens when no story is
112
- # named — including on brownfield, where adopt emits an empty backlog by design.
113
- scope: "feature"
114
- story: ""
115
- story_path: ""
116
- story_title: ""
117
- story_type: ""
118
- story_status: ""
119
- story_dir: ""
120
- story_resolved: "false"
121
- is_technical: "false"
122
- exercises_csv: ""
123
- constraints_csv: ""
124
- current_version: ""
125
- stale_csv: ""
126
- stale_note: ""
127
- inflight_csv: ""
128
- backlog_digest: ""
129
- # Non-empty only when the caller said SOMETHING and no story came back from it — the
130
- # one case where widening to feature scope is more likely a typo than a choice.
131
- unresolved_input: ""
132
- scope_brief: "SCOPE — the WHOLE FEATURE. No story was named, so every FR- and BR- in spec.md is in scope and each is judged in FULL. EXTENT: the whole of each id."
133
- story_be_doc: ""
134
- story_be_exists: "false"
135
- story_be_pass: "0"
136
- story_fe_doc: ""
137
- story_fe_exists: "false"
138
- story_fe_pass: "0"
139
- bridge_note: "BRIDGE — the FEATURE PLANS (backend-plan.md, and frontend-plan.md where a frontend lane is declared). No story design is in scope this run, so the plans are the only map from a spec id to the elements that realise it."
140
- # --- the code under analysis (OKF) --------------------------------------
141
- coverage_digest: ""
142
- be_count: "0"
143
- fe_count: "0"
144
- paths_missing: ""
145
- okf_concept: ""
146
- # --- pass 1: behaviour ---------------------------------------------------
147
70
  test_ok: "false"
148
- no_test_edit_rule: ""
149
- fixed_files: ""
150
- # --- pass 2: the technical-story oracle ----------------------------------
151
- # Named per case rather than derived, because "the suite is clean" and "the suite
152
- # could not be read" must never collapse into one another: a gate that treats "could
153
- # not look" as "nothing found" is the false green the kit refuses everywhere else.
154
- oracle: "clean"
155
- oracle_case: ""
156
- tests_changed_csv: ""
157
- tests_changed_count: "0"
158
- tdiff_digest: ""
159
- tdiff_base: ""
160
- tdiff_base_kind: ""
161
- tech_gate_ok: "true"
162
- tech_note: "Not a technical story — the unchanged-suite oracle does not apply."
163
- # --- pass 3: domain conformance ------------------------------------------
164
71
  conformance_ok: "false"
165
- conformance_violations: ""
166
- conformance_fixes: ""
167
- # --- pass 4: integrity (referrer blast radius) ---------------------------
168
- # Defaults are safe values if xref/test-run is skipped or errors, so the gate reads a
169
- # real verdict rather than an empty var.
170
- integrity_ok: "false"
171
- referrer_csv: ""
172
- referrer_count: "0"
173
- referrer_names: ""
174
- integrity_log: ""
175
- # --- pass 5: trace (the verdict) -----------------------------------------
176
- trace_ok: "false"
177
- bridge_ok: "true"
178
- trace_gaps: ""
179
- trace_gap_count: "0"
180
- trace_gap_refs: ""
181
- trace_coverage: ""
182
- bridge_gaps: ""
183
- # --- OKF knowledge (advisory) --------------------------------------------
184
- okf_ok: "unknown"
185
- okf_orphans: "0"
186
- # --- reporting ------------------------------------------------------------
187
- report_path: ""
188
- result_file: ""
189
72
  # NAVIGATION ONLY. Whether a change is open decides where this gate sends you next
190
73
  # (close-delta vs run) — nothing else in this workflow reads them. On the CHANGE path
191
74
  # finalize-delta has already merged the delta into the master docs, so the delta files
192
75
  # are redundant here and are deliberately NOT loaded into any context.
193
76
  active_change: ""
194
77
  has_change: "false"
78
+ # Integrity pass (referrer blast radius) defaults — safe values if xref/test-run is
79
+ # skipped or errors, so the gate reads a real verdict rather than an empty var.
80
+ integrity_ok: "false"
81
+ referrer_csv: ""
82
+ referrer_count: "0"
83
+ referrer_names: ""
84
+ # Pass-3 (OKF) reporting defaults — the knowledge pass is advisory, so these stay
85
+ # readable in the outputs even when a step is skipped or errors out.
86
+ okf_ok: "unknown"
87
+ okf_orphans: "0"
195
88
 
196
89
  workflow:
197
90
 
@@ -212,13 +105,8 @@ workflow:
212
105
  script: { ref: actions }
213
106
  subcommand: feature-paths
214
107
  outputs:
215
- - { name: feature_dir, from: "$.feature_dir" }
216
- - { name: spec_file, from: "$.spec_file" }
217
- - { name: spec_exists, from: "$.spec_exists" }
218
- - { name: be_plan, from: "$.backend_plan_file" }
219
- - { name: be_plan_exists, from: "$.backend_plan_exists" }
220
- - { name: fe_plan, from: "$.frontend_plan_file" }
221
- - { name: fe_plan_exists, from: "$.frontend_plan_exists" }
108
+ - { name: feature_dir, from: "$.feature_dir" }
109
+ - { name: spec_file, from: "$.spec_file" }
222
110
 
223
111
  - id: require_feature
224
112
  action: verify_context
@@ -226,149 +114,6 @@ workflow:
226
114
  on_empty: stop
227
115
  on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run analyze-slice."
228
116
 
229
- # The anchor. Every judgement below resolves an id's meaning from this document, so a
230
- # feature without one has nothing for the code to be measured against.
231
- - id: require_spec
232
- action: verify_context
233
- require_true: spec_exists
234
- on_false: stop
235
- on_false_message: "No spec.md for the current feature. Run /appweaver specify first — the spec is this gate's oracle, and code cannot be conformed to a contract that does not exist."
236
-
237
- # THE ORACLE HAS TO BE HONEST ABOUT ITS OWN VERSION. This is the only gate whose
238
- # declared oracle IS spec.md — every judgement below resolves an id's meaning from it
239
- # — and it stamps {{current_version}} into the machine-readable verdict a pipeline
240
- # branches on. A spec whose version no longer names its contents makes that stamp a
241
- # false claim about what was certified, so the fence every other consumer carries
242
- # belongs here most of all.
243
- - id: spec_state_guard
244
- action: include
245
- module: .appweaver/orchestrator/modules/spec-state-gate.orch.yaml
246
- with: { command: "/appweaver analyze-slice" }
247
-
248
- # 1. WHICH STORY, if any. An id in the input scopes the gate to that story; anything
249
- # else widens it to the feature. Deliberately NO ask: this gate has to be runnable
250
- # unattended, and a gate that stops to ask cannot run in CI. Naming a story narrows
251
- # it, naming none widens it, and both are legitimate runs.
252
- - id: story_scan
253
- action: execute_script
254
- on_error: continue
255
- script: { ref: actions }
256
- subcommand: story-plan-check
257
- args: ["--feature-dir", "{{feature_dir}}", "--input", "{{USER_INPUT}}"]
258
- outputs:
259
- - { name: story_resolved, from: "$.resolved" }
260
- - { name: story, from: "$.story" }
261
- - { name: story_path, from: "$.story_path" }
262
- - { name: story_title, from: "$.story_title" }
263
- - { name: story_type, from: "$.story_type" }
264
- - { name: story_status, from: "$.story_status" }
265
- - { name: story_dir, from: "$.story_dir" }
266
- - { name: is_technical, from: "$.is_technical" }
267
- - { name: exercises_csv, from: "$.exercises_csv" }
268
- - { name: constraints_csv, from: "$.constraints_csv" }
269
- - { name: current_version, from: "$.current_version" }
270
- - { name: stale_csv, from: "$.stale_csv" }
271
- - { name: stale_note, from: "$.stale_note" }
272
- - { name: backlog_digest, from: "$.digest" }
273
- - { name: story_be_doc, from: "$.be_doc_path" }
274
- - { name: story_be_exists, from: "$.be_doc_exists" }
275
- - { name: story_be_pass, from: "$.be_last_pass" }
276
- - { name: story_fe_doc, from: "$.fe_doc_path" }
277
- - { name: story_fe_exists, from: "$.fe_doc_exists" }
278
- - { name: story_fe_pass, from: "$.fe_last_pass" }
279
-
280
- # WHICH IDS ARE IN FLIGHT. On a feature-scoped run these are subtracted from the
281
- # question: a requirement with an OPEN story is work someone is doing, and reporting
282
- # it as missing code turns every partially-built feature red (law 39). Story-scoped
283
- # runs subtract nothing — the story under analysis IS the work in flight.
284
- - id: inflight_ids
285
- action: execute_script
286
- on_error: continue
287
- script: { ref: actions }
288
- subcommand: story-refs
289
- args: ["--feature-dir", "{{feature_dir}}"]
290
- outputs:
291
- - { name: inflight_csv, from: "$.covered_csv" }
292
-
293
- # 2. SCOPE + BRIDGE, stated once as the two sentences the trace pass reads. Both vars
294
- # default to the feature-scoped wording (see `vars:`) and are overwritten only when
295
- # a story actually resolved, so nothing here can half-apply.
296
- - id: scope_story
297
- condition: "{{story_resolved}} == true"
298
- action: set
299
- vars:
300
- scope: "story"
301
- scope_brief: "SCOPE — story {{story}} ({{story_title}}), type {{story_type}}, status {{story_status}}. In scope: {{exercises_csv}} (delivered by this story) and {{constraints_csv}} (bounds it must meet). EXTENT — the story's '## Requirements delivered' section says which PART of each id this slice delivers; judge the code against the spec's wording of THAT PART alone. A slice is never failed for the half of a requirement it never claimed, and an id no story has claimed yet is not this run's business."
302
- # A technical story delivers no requirement, so `exercises:` is empty BY DESIGN and there
303
- # is nothing to trace forward. Its real check already ran — the unchanged-suite oracle —
304
- # and what is left for trace is the bounds it is still held to. Without this the pass
305
- # would be handed "in scope: (nothing)" and would go looking for something to report.
306
- - id: scope_story_technical
307
- condition: "{{is_technical}} == true"
308
- action: set
309
- vars:
310
- scope_brief: "SCOPE — story {{story}} ({{story_title}}) is a TECHNICAL story: a refactor, dependency upgrade or performance fix that delivers NO requirement. `exercises:` is empty by design, so there is no FR-/BR- to trace forward and NONE is expected. Judge only this: the code still meets the bounds this story is held to ({{constraints_csv}}), and nothing it changed has quietly altered behaviour the spec fixes. Its real oracle is the UNCHANGED test suite, which has already been checked this run. Report NO gap for a requirement — an unimplemented requirement is some other story's business, and a technical story that had to change behaviour was already caught upstream."
311
-
312
- - id: scope_feature_note
313
- condition: "{{story_resolved}} == false"
314
- action: set
315
- vars:
316
- scope_brief: "SCOPE — the WHOLE FEATURE, because no story was named. In scope: every FR- and BR- you can see in spec.md EXCEPT these, which have OPEN stories and are work in flight rather than drift: {{inflight_csv}}. EXTENT — the whole of each in-scope id. Also in scope are the CN-/DC-/IC-/UX- the spec binds this feature by, and the SC- Success Criteria — this phase is the one the spec names as their verifier, and a feature-scoped run is the only scope at which a feature-level outcome can be judged. SC- is scored differently from every other class; see the SUCCESS CRITERIA rules below."
317
- - id: bridge_story
318
- condition: "{{story_be_exists}} == true"
319
- action: set
320
- vars:
321
- bridge_note: "BRIDGE — the STORY DESIGN, service lane: design pass {{story_be_pass}} of {{story_be_doc}} (the LAST pass; earlier ones are superseded history). Its '### Changes' section is the map from this slice's ids to the files that realise them. The feature plans are in context underneath it as ARCHITECTURE — where those elements live — not as a second contract."
322
- - id: bridge_story_ui
323
- condition: "{{story_fe_exists}} == true"
324
- action: set
325
- vars:
326
- bridge_note: "BRIDGE — the STORY DESIGN, both lanes: service pass {{story_be_pass}} of {{story_be_doc}} and UI pass {{story_fe_pass}} of {{story_fe_doc}} (the LAST pass of each; earlier ones are superseded history). Their '### Changes' sections are the map from this slice's ids to the files that realise them. A requirement with a user-facing surface is realised in BOTH lanes and needs a home in each. The feature plans are in context underneath as ARCHITECTURE, not as a second contract."
327
-
328
- # 3. WHERE THE VERDICT GOES. A story-scoped verdict written at feature level would be
329
- # overwritten by the next story's run and then read as the feature's, so each scope
330
- # keeps its own file. gate-result writes the JSON beside the markdown and names it
331
- # after it, so a job holding either path holds the other.
332
- # A named story that matched nothing would otherwise widen the run to feature scope in
333
- # silence, which is a different gate than the one that was asked for. Announced, never
334
- # refused: the input may equally have been guidance that names no story at all.
335
- - id: mark_unresolved_input
336
- condition: "{{story_resolved}} == false"
337
- action: set
338
- vars: { unresolved_input: "{{USER_INPUT}}" }
339
- - id: unresolved_input_notice
340
- action: verify_context
341
- require_value: unresolved_input
342
- equals: ""
343
- on_mismatch: continue
344
- on_mismatch_message: |
345
- No story matched "{{unresolved_input}}", so this run is FEATURE-SCOPED — every FR- and
346
- BR- in the spec except the ids with open stories. If you meant to gate one story, re-run
347
- naming it exactly as its file does. The open backlog:
348
- {{backlog_digest}}
349
-
350
- - id: report_paths
351
- action: set
352
- vars: { report_path: "{{feature_dir}}/slice-analyze-report.md" }
353
- - id: report_paths_story
354
- condition: "{{story_resolved}} == true"
355
- action: set
356
- vars: { report_path: "{{story_dir}}/slice-analyze-report.md" }
357
-
358
- # 3a. INVALIDATE any previous verdict, before anything downstream can stop. A gate that
359
- # writes its result only on completion leaves the LAST run's verdict on disk for
360
- # every run that aborts — a red test loop, a broken referrer — and a pipeline
361
- # reading that file would act on a certification this run never made. Every exit
362
- # from here on therefore leaves at least a red; the real verdict overwrites it.
363
- - id: gate_result_open
364
- action: execute_script
365
- script: { ref: actions }
366
- subcommand: gate-result
367
- # The lenient loader needs a flow sequence on ONE line.
368
- args: ["--gate", "analyze-slice", "--feature-dir", "{{feature_dir}}", "--ok", "false", "--report", "{{report_path}}", "--story", "{{story}}", "--note", "analyze-slice started and has not reached a verdict; a run that stops before certifying leaves this red on purpose"]
369
- outputs:
370
- - { name: result_file, from: "$.result_file" }
371
-
372
117
  # Is a change OPEN on this feature? Read for NAVIGATION only — see the vars note. Safe
373
118
  # on the bootstrap path: with no active_version.json, version-read returns
374
119
  # active_change="" / has_change=false and exits 0, so nothing here is conditional on it.
@@ -381,35 +126,21 @@ workflow:
381
126
  - { name: active_change, from: "$.active_change" }
382
127
  - { name: has_change, from: "$.has_change" }
383
128
 
384
- # 4. THE CODE UNDER ANALYSIS, from the slice's OKF concept — BOTH LANES. Every pass
385
- # below works over this list. `concept-paths` reads `source_files` alone, so from
386
- # the moment implement-ui started writing `ui_source_files` a UI-bearing requirement
387
- # had no UI code in view to be traced to; `all_paths` is the union of the two.
388
- # A story narrows the QUESTION, not the file set: a requirement can only be judged
389
- # against everything that could realise it, and the concept records no per-story
390
- # split. Read-only, exits 0 with empty lists when there is no concept yet;
391
- # on_error: continue so a missing bundle degrades the gate rather than blocking it.
392
- - id: concept_coverage
129
+ # The slice's source files, from its OKF concept — the context all three passes below
130
+ # work over (test fix loop, integrity triage, conformance). Replaces the retired
131
+ # `design-paths` fence scan: backend-plan.md is tech-agnostic prose with no ```lang path=…
132
+ # code blocks, so that scan returned nothing and every pass ran on an EMPTY file list.
133
+ # Read-only, exits 0 with paths=[] when there is no concept yet; on_error: continue so
134
+ # a missing bundle degrades the gate to its pre-existing (empty) behaviour, never blocks.
135
+ - id: concept_paths
393
136
  action: execute_script
394
137
  on_error: continue
395
138
  script: { ref: xref }
396
- subcommand: concept-coverage
397
- args: ["--feature-dir", "{{feature_dir}}", "--ids", "{{exercises_csv}}"]
139
+ subcommand: concept-paths
140
+ args: ["--feature-dir", "{{feature_dir}}"]
398
141
  outputs:
399
- - { name: all_paths, from: "$.all_paths" }
400
- # THE SAME FILES AS A CSV, for the steps that name them in a prompt instead of
401
- # inlining them. `context_from: all_paths` APPENDS every one of those paths to the
402
- # envelope, and the engine reads the envelope IN FULL into the user block — so this
403
- # gate inlined the feature's whole codebase four times over, and again on every fix
404
- # iteration. Every consumer of that list can open a file itself; what it needed was
405
- # to be told which files, not to be handed their contents.
406
- - { name: all_csv, from: "$.all_csv" }
407
- - { name: all_count, from: "$.all_count" }
408
- - { name: paths_missing, from: "$.all_missing_csv" }
409
- - { name: coverage_digest, from: "$.digest" }
410
- - { name: be_count, from: "$.backend_count" }
411
- - { name: fe_count, from: "$.frontend_count" }
412
- - { name: okf_concept, from: "$.concept" }
142
+ - { name: all_paths, from: "$.paths" }
143
+ - { name: paths_missing, from: "$.missing" }
413
144
 
414
145
  - id: resolve_pre_hooks
415
146
  action: execute_script
@@ -424,15 +155,6 @@ workflow:
424
155
 
425
156
  # ═══════ PASS 1 — BEHAVIOUR: the slice's tests vs the spec's acceptance criteria ═══════
426
157
  # Run tests → (if red) fix → re-run, up to 3×.
427
- #
428
- # For a TECHNICAL story the fix loop is told not to touch a test, because a test it
429
- # edited to reach green is itself the behaviour change pass 2 exists to catch — and
430
- # pass 2 counts this loop's edits, so the rule and the oracle agree.
431
- - id: arm_no_test_edit
432
- condition: "{{is_technical}} == true"
433
- action: set
434
- vars:
435
- no_test_edit_rule: "THIS IS A TECHNICAL STORY. Do NOT modify, weaken, delete or rename a single test — the existing suite is this story's only oracle, and a test that has to change means behaviour changed and the work was a functional story citing a requirement. Fix the CODE until the tests as written pass. If a test genuinely cannot pass without changing it, STOP and say so rather than editing it: that finding is the point of the story, not an obstacle to it."
436
158
  - id: test_loop
437
159
  action: loop
438
160
  while: "{{test_ok}} != true"
@@ -461,24 +183,18 @@ workflow:
461
183
  action: build_context
462
184
  vars:
463
185
  test_log: "{{test_log}}"
464
- no_test_edit_rule: "{{no_test_edit_rule}}"
465
- all_csv: "{{all_csv}}"
466
- all_count: "{{all_count}}"
467
186
  context:
468
187
  - { path: "{{spec_file}}" }
188
+ context_from: all_paths
469
189
  - id: fix
470
190
  action: agent_generate
471
191
  tools: "Read,Write,Edit,Bash"
472
192
  permission_mode: acceptEdits
473
193
  task: |
474
194
  Tests failed. Make them pass by fixing the code (or a genuinely wrong
475
- test), keeping behaviour aligned with the spec's acceptance criteria (spec
476
- in context). {{no_test_edit_rule}} Test log:
195
+ test) in the files in context, keeping behaviour aligned with the spec's
196
+ acceptance criteria (spec in context). Test log:
477
197
  {{test_log}}
478
-
479
- This slice's code, both lanes ({{all_count}} file(s)) — READ the ones the
480
- failures point at; they are named, not inlined:
481
- {{all_csv}}
482
198
  outputs:
483
199
  - { name: fixed_files, from: "$.files" }
484
200
 
@@ -496,89 +212,7 @@ workflow:
496
212
  ones this feature did not cause; matched is not meaningful in that case. Last log:
497
213
  {{test_log}}
498
214
 
499
- # ═══════ PASS 2 — THE TECHNICAL-STORY ORACLE: the suite must be UNCHANGED ═══════
500
- # A technical story (a refactor, a dependency upgrade, a performance fix) delivers no
501
- # requirement, so `exercises:` is empty and there is no FR- to trace it to. The
502
- # existing test suite is its whole oracle, and the rule is stronger than "the tests are
503
- # green": a test that had to be MODIFIED means behaviour moved, and the work was a
504
- # functional story citing a requirement (law 32). Run AFTER pass 1 on purpose, so the
505
- # fix loop's own edits are counted — a gate that had to rewrite a test to reach green
506
- # has proved the same thing.
507
- #
508
- # A new test is not a changed one: adding characterisation tests around a refactor is
509
- # exactly the work a technical story exists to do. Deletions count as changes.
510
- - id: test_diff
511
- action: execute_script
512
- on_error: continue
513
- script: { ref: actions }
514
- subcommand: test-diff
515
- args: ["--feature-dir", "{{feature_dir}}"]
516
- outputs:
517
- - { name: oracle, from: "$.oracle" }
518
- - { name: tests_changed_csv, from: "$.tests_changed_csv" }
519
- - { name: tests_changed_count, from: "$.tests_changed_count" }
520
- - { name: tdiff_digest, from: "$.digest" }
521
- - { name: tdiff_base, from: "$.base" }
522
- - { name: tdiff_base_kind, from: "$.base_kind" }
523
-
524
- # The story's type and the diff's state, combined into ONE string so each case is a
525
- # single comparison the evaluator can make. Both halves are strings, so this needs no
526
- # boolean gymnastics — a bool rendered by {{ }} and a bool read by require_value never
527
- # compare equal (law 43), which is why the type arrives as a word rather than a flag.
528
- - id: oracle_case_set
529
- action: set
530
- vars: { oracle_case: "{{story_type}}/{{oracle}}" }
531
- - id: oracle_fail
532
- condition: "{{oracle_case}} == technical/changed"
533
- action: set
534
- vars:
535
- tech_gate_ok: "false"
536
- tech_note: "FAILED — {{tests_changed_count}} test(s) modified or deleted: {{tests_changed_csv}}"
537
- - id: oracle_unverified
538
- condition: "{{oracle_case}} == technical/unverified"
539
- action: set
540
- vars:
541
- tech_note: "UNVERIFIED — this is not a git working copy, so the suite's history cannot be read. The oracle did not pass; it could not run."
542
- - id: oracle_clean
543
- condition: "{{oracle_case}} == technical/clean"
544
- action: set
545
- vars:
546
- tech_note: "PASSED — no test was modified or deleted against {{tdiff_base_kind}} baseline {{tdiff_base}}."
547
-
548
- # An oracle that could not be read never fails the gate — it announces. Refusing on the
549
- # strength of a check that did not run is the mirror image of passing on one.
550
- - id: oracle_unverified_notice
551
- condition: "{{oracle_case}} == technical/unverified"
552
- action: verify_context
553
- require_value: oracle
554
- equals: "clean"
555
- on_mismatch: continue
556
- on_mismatch_message: |
557
- TECHNICAL-STORY ORACLE UNVERIFIED for story {{story}}. A technical story delivers no
558
- requirement, so the unchanged test suite is the only thing that can say whether its
559
- behaviour moved — and that history is not readable here (this is not a git working
560
- copy). Everything else in this gate ran; this one check did not, and it is recorded
561
- as unverified rather than as a pass in {{result_file}}.
562
-
563
- - id: oracle_gate
564
- action: verify_context
565
- require_true: tech_gate_ok
566
- on_false: stop
567
- on_false_message: |
568
- TECHNICAL-STORY ORACLE FAILED — story {{story}} is type `technical`, and a technical
569
- story's oracle is the UNCHANGED test suite. These tests were modified or deleted:
570
- {{tests_changed_csv}}
571
- {{tdiff_digest}}
572
- A test that had to change means behaviour changed, and behaviour that changes belongs
573
- to a FUNCTIONAL story citing the requirement it moves. Two ways out, and they are not
574
- interchangeable: restore the tests and make the refactor behaviour-preserving, or
575
- accept that this work changes behaviour — close this story as cancelled, raise a
576
- functional story for the change through /appweaver sync-spec (⌛ not yet built — until it is, fold the change back by hand under .appweaver/SPEC-EDIT-PROTOCOL.md) (if the contract moves)
577
- or by raising a story by hand from .appweaver/templates/story-template.md
578
- (/appweaver add-story, which mints the id and stamps the frontmatter, is ⌛ not yet
579
- built), and build it there where an FR- can be traced to it.
580
-
581
- # ═══════ PASS 3 — DOMAIN CONFORMANCE: code vs the gate overlay checklist ═══════
215
+ # ═══════ PASS 2 — DOMAIN CONFORMANCE: code vs the gate overlay checklist (validation) ═══════
582
216
  # With tests green, scan the feature's CODE against the domain gate overlays (the
583
217
  # forbidden-patterns / refusal conditions / CI gates). The overlays load as the
584
218
  # checklist — VALIDATION only, never an authoring input to feature code. No-op for a
@@ -602,28 +236,18 @@ workflow:
602
236
  steps:
603
237
  - id: ctx_conformance
604
238
  action: build_context
605
- vars:
606
- no_test_edit_rule: "{{no_test_edit_rule}}"
607
- all_csv: "{{all_csv}}"
608
- all_count: "{{all_count}}"
609
239
  context:
610
240
  - { path: "{{spec_file}}" }
241
+ context_from: all_paths
611
242
  - id: check_overlays
612
243
  action: agent_query
613
- # A query step is PAYLOAD-ONLY unless it declares tools. It read the code because
614
- # context_from inlined every file; naming them instead without granting Read would
615
- # leave it reasoning about a codebase it cannot open.
616
- tools: Read
617
244
  include_standards: true # the gate overlays (checklist) are in the standards
618
245
  schema: { ok: true, violations: [ { rule: "<overlay rule / id>", where: "<file:symbol>", issue: "<what is violated>" } ] }
619
246
  mock: { ok: true, violations: [] }
620
247
  task: |
621
248
  The domain gate checklist is loaded in the standards (the overlay
622
- forbidden-patterns, refusal conditions, and CI gates). This slice's code and
623
- tests, both lanes ({{all_count}} file(s)) — READ them; they are named, not
624
- inlined:
625
- {{all_csv}}
626
- Check them against EACH checklist rule and list
249
+ forbidden-patterns, refusal conditions, and CI gates). Check the feature's
250
+ generated code + tests (in context) against EACH checklist rule and list
627
251
  EVERY violation, e.g.: a hard delete of a retention-protected aggregate /
628
252
  version history / audit record; a second write path bypassing the aggregate
629
253
  or interop adapter; an invented/free-text coded value where a validated code
@@ -646,8 +270,7 @@ workflow:
646
270
  Fix these domain-conformance violations in the feature's code (in context),
647
271
  following the gate checklist rules (in the standards) and keeping the design's
648
272
  behaviour intact. Do NOT weaken or delete a test to hide a violation — fix the
649
- code. {{no_test_edit_rule}} After fixing, re-run the tests (Bash) to confirm
650
- they stay green.
273
+ code. After fixing, re-run the tests (Bash) to confirm they stay green.
651
274
  Violations: {{conformance_violations}}
652
275
  outputs:
653
276
  - { name: conformance_fixes, from: "$.files" }
@@ -656,9 +279,9 @@ workflow:
656
279
  action: verify_context
657
280
  require_true: conformance_ok
658
281
  on_false: stop
659
- on_false_message: "Code violates the domain gate checklist after 2 fix attempts (violations: {{conformance_violations}}). These are the domain constraints design/implement must satisfy — fix the code, or if the constraint itself is wrong, correct it upstream (/appweaver specify → plan)."
282
+ on_false_message: "Code violates the domain gate checklist after 2 fix attempts (violations: {{conformance_violations}}). These are the domain constraints plan/implement must satisfy — fix the code, or if the constraint itself is wrong, correct it upstream (/appweaver specify → plan)."
660
283
 
661
- # ═══════ PASS 4 — INTEGRITY: did this change break any aggregate that USES it? ═══════
284
+ # ═══════ INTEGRITY — did this feature's change break any aggregate that USES it? ═══════
662
285
  # The slice is green + domain-conformant on its own. But a change to a feature can break
663
286
  # any OTHER aggregate that references it. Ask the OKF Cross-References index "who refers to
664
287
  # me?" (the transitive referrer closure — C→B→A all count), then RE-RUN the feature's tests
@@ -676,7 +299,7 @@ workflow:
676
299
  outputs:
677
300
  - { name: referrer_csv, from: "$.referrer_closure_csv" }
678
301
  - { name: referrer_count, from: "$.referrer_closure_count" }
679
- - { name: referrer_names, from: "$.referrer_closure_csv" }
302
+ - { name: referrer_names, from: "$.referrer_closure" }
680
303
 
681
304
  - id: run_integrity
682
305
  action: execute_script
@@ -700,16 +323,12 @@ workflow:
700
323
  referrer_names: "{{referrer_names}}"
701
324
  referrer_count: "{{referrer_count}}"
702
325
  integrity_log: "{{integrity_log}}"
703
- all_csv: "{{all_csv}}"
704
- all_count: "{{all_count}}"
705
326
  context:
706
327
  - { path: "{{spec_file}}" }
328
+ context_from: all_paths
707
329
  - id: write_broken_referrers
708
330
  action: agent_author
709
331
  condition: "{{integrity_ok}} != true"
710
- # An author step is PAYLOAD-ONLY unless it declares tools. The triage report has to
711
- # name what this slice CHANGED that the referrers depend on, which means reading it.
712
- tools: Read
713
332
  include_standards: false # a triage report, not generated code
714
333
  output_target: { type: file, path: "{{feature_dir}}/broken-referrers.md" }
715
334
  task: |
@@ -719,9 +338,6 @@ workflow:
719
338
  referrer (that is the referrer's own slice's job). Referrer blast radius
720
339
  ({{referrer_count}}): {{referrer_names}}. Failing test log:
721
340
  {{integrity_log}}
722
- This slice's code, both lanes ({{all_count}} file(s)) — READ what it changed on the
723
- published surface to say WHY the referrers broke; the files are named, not inlined:
724
- {{all_csv}}
725
341
  Write markdown, in this order:
726
342
  - `# Broken Referrers — <feature>` and a one-line statement of what this file is:
727
343
  aggregates that USE this feature and whose tests went red when this feature's
@@ -754,151 +370,7 @@ workflow:
754
370
  on_false: stop
755
371
  on_false_message: "Integrity check failed — this feature's change breaks a test in an aggregate that REFERS to it. Referrer blast radius ({{referrer_count}}): {{referrer_names}}. Triage written to {{feature_dir}}/broken-referrers.md — resolve each in the referrer's own slice (or reconsider this feature's contract change), then re-run. Last log: {{integrity_log}}"
756
372
 
757
- # ═══════ PASS 5 — TRACE: spec ↔ code, through the design ═══════════════════
758
- # The question this gate exists to answer, asked last because everything before it is a
759
- # precondition: the code runs, it changed no oracle it should not have, it violates no
760
- # domain constraint, and it broke nobody. What is left is the only thing that matters —
761
- # is this the thing the contract asked for?
762
- #
763
- # READ-ONLY. It reports and never repairs: a missing requirement is feature work, and
764
- # writing it here would produce code no design pass names and no gate ever conformed.
765
- # Nothing between this step and the report can stop the run, so the verdict always
766
- # reaches both the markdown and the JSON.
767
- - id: ctx_trace
768
- action: build_context
769
- vars:
770
- scope_brief: "{{scope_brief}}"
771
- bridge_note: "{{bridge_note}}"
772
- coverage_digest: "{{coverage_digest}}"
773
- stale_note: "{{stale_note}}"
774
- all_csv: "{{all_csv}}"
775
- all_count: "{{all_count}}"
776
- context:
777
- - { path: "{{spec_file}}" } # THE ORACLE — the contract's own wording
778
- - { path: "{{story_path}}", optional: true } # the extent: what this slice claims of each id
779
- - { path: "{{story_be_doc}}", optional: true } # the BRIDGE, service lane — the latest pass
780
- - { path: "{{story_fe_doc}}", optional: true } # the BRIDGE, UI lane — the latest pass
781
- - { path: "{{be_plan}}", optional: true } # architecture: where those elements live
782
- - { path: "{{fe_plan}}", optional: true }
783
- - { path: "{{feature_dir}}/interfaces.md", optional: true } # the published surface, cited by IF- id
784
- - { path: "{{feature_dir}}/adr.md", optional: true } # ratified decisions
785
- - id: trace
786
- action: agent_query
787
- tools: Read # follow a call out of the listed files when a trace needs it
788
- include_standards: false # the contract is the spec, not a rule corpus
789
- schema: { ok: true, bridge_ok: true, gap_count: 0, gap_refs: "<comma-separated spec ids the gaps touch>", coverage: "<one line per in-scope id — id | what the spec requires | the design element that maps it | the code that realises it | DELIVERED or GAP>", gaps: [ { severity: "high", spec_ref: "<FR/BR/CN/DC/IC/UX/SC id>", kind: "code-missing|code-wrong|bridge-missing|spec-wrong", where: "<file:symbol, or the document>", gap: "<what the spec requires and what is actually there>", route: "<the command that fixes it>" } ], bridge_gaps: "<one line per in-scope id no design maps, or empty>" }
790
- mock: { ok: true, bridge_ok: true, gap_count: 0, gap_refs: "", coverage: "", gaps: [], bridge_gaps: "" }
791
- task: |
792
- spec.md is the ORACLE. Judge the CODE against it and report every id it fails to
793
- deliver. The design is the BRIDGE that makes the question answerable — it says
794
- which elements realise which id — and it is never the standard: where the design
795
- and the spec disagree about an in-scope id, the SPEC is what the code is measured
796
- against, and the disagreement is itself a finding.
797
-
798
- {{scope_brief}}
799
-
800
- {{bridge_note}}
801
-
802
- THIS SLICE'S CODE, both lanes, from the OKF concept ({{all_count}} file(s)) — READ
803
- the ones each id's trace needs, and follow anything they call. They are named here
804
- rather than inlined, because a real codebase does not fit a payload:
805
- {{all_csv}}
806
- Do NOT modify a single file — this step is read-only by contract.
807
-
808
- {{coverage_digest}}
809
-
810
- HOW TO TRACE each in-scope id, in this order:
811
- (1) Resolve the id's wording from spec.md. That wording — not the design's
812
- restatement of it — is the standard.
813
- (2) Find the DESIGN element that maps it (the story pass's "### Changes" and
814
- "### Approach", or the feature plan's traceability section). If nothing maps
815
- it, that is a BRIDGE gap: record it in `bridge_gaps` with kind
816
- "bridge-missing". Then keep going — code may well deliver the id anyway, and
817
- an undocumented realisation is a documentation defect, not a broken contract.
818
- (3) Find the CODE that realises the element, and read it. Judge whether it
819
- delivers what the spec words, to the extent this scope claims.
820
- (4) Where the id has a user-facing surface, it is realised in BOTH lanes and needs
821
- a home in each. A UX- lives only in the UI lane; a BR- only in the service
822
- lane. Do not report a UI gap for a slice that has no UI lane in scope.
823
-
824
- SUCCESS CRITERIA (SC-) ARE SCORED AS OUTCOMES, NOT AS REALISATIONS. An SC- is a
825
- measurable result the whole feature is supposed to produce — "a customer can place
826
- an order in under two minutes", "90% of first-time users complete an order without
827
- assistance" — so no design element "maps" one and no file "realises" one. Four
828
- rules, and they override steps (2) and (3) and the ok/bridge_ok sentences below for
829
- this class alone:
830
- • Score each in-scope SC- as MET, NOT MET, or NOT ASSESSABLE FROM A STATIC READ.
831
- Most are the third: a latency budget, an adoption rate or a support-ticket
832
- reduction cannot be decided by reading code, and inventing a verdict is worse
833
- than recording that it needs measuring.
834
- • NOT ASSESSABLE is not a gap. It does not set ok=false and it is not counted in
835
- gap_count — say in its `coverage` line what would have to be measured, and by
836
- what, to settle it.
837
- • NOT MET does fail ok: reserve it for the case where the code plainly cannot
838
- satisfy the criterion (the feature has no such surface at all, or the design
839
- rules the outcome out).
840
- • An SC- NEVER sets bridge_ok=false and is NEVER a "bridge-missing" gap. There is
841
- no design element to be missing, so routing one to story-plan sends someone to
842
- design a document entry that should not exist.
843
- Every in-scope SC- still gets its `coverage` line. The record is the point: this is
844
- the phase the spec names as their verifier, and "measured how, and when" is the
845
- answer it is meant to leave behind.
846
-
847
- CLASSIFY every gap by `kind`, because the four routes are different commands:
848
- code-missing the design maps the id and no code realises it → /appweaver implement
849
- code-wrong code exists and does something else → /appweaver implement
850
- bridge-missing no design maps the id at all → /appweaver story-plan
851
- spec-wrong the code is right and the SPEC words it wrongly → /appweaver sync-spec
852
- Use `spec-wrong` sparingly and only with a reason: the spec is the oracle, and
853
- "the code disagrees" is evidence about the code first.
854
-
855
- THESE ARE NOT GAPS, and reporting them is noise that hides the real ones:
856
- - an id that is out of scope for this run — another story's work, or one nobody
857
- has claimed yet;
858
- - the part of a requirement this scope explicitly does not claim;
859
- - style, naming, structure, layering or test-coverage opinions — other gates own
860
- every one of those;
861
- - anything a design pass deliberately deferred and named under its risks;
862
- - a missing element you inferred from the design alone with no spec basis. The
863
- design is the bridge, not the contract.
864
-
865
- Fill `coverage` with ONE LINE PER IN-SCOPE ID, delivered or not — that table is the
866
- traceability record this gate exists to leave behind, and it is written on a green
867
- run too. Set gap_count and gap_refs from `gaps`. Set ok=true ONLY when every
868
- in-scope FR-/BR-/CN-/DC-/IC-/UX- is delivered as the spec words it and no in-scope
869
- SC- is NOT MET — an SC- scored not-assessable leaves ok exactly as it was. Set
870
- bridge_ok=false when any in-scope id OTHER THAN AN SC- has no design mapping it,
871
- INDEPENDENTLY of ok — working code with a documentation gap is a true state and must
872
- be reportable as one.
873
- {{stale_note}}
874
- outputs:
875
- - { name: trace_ok, from: "$.ok" }
876
- - { name: bridge_ok, from: "$.bridge_ok" }
877
- - { name: trace_gaps, from: "$.gaps" }
878
- - { name: trace_gap_count, from: "$.gap_count" }
879
- - { name: trace_gap_refs, from: "$.gap_refs" }
880
- - { name: trace_coverage, from: "$.coverage" }
881
- - { name: bridge_gaps, from: "$.bridge_gaps" }
882
-
883
- # The bridge's integrity is ANNOUNCED, not gated. An id the design never mapped, whose
884
- # code nonetheless delivers the spec, is a documentation defect — failing the slice for
885
- # it would refuse working, contract-satisfying code over a missing row in a document
886
- # the next story-plan pass rewrites anyway.
887
- - id: bridge_notice
888
- condition: "{{bridge_ok}} == false"
889
- action: verify_context
890
- require_value: bridge_ok
891
- equals: "true"
892
- on_mismatch: continue
893
- on_mismatch_message: |
894
- BRIDGE INCOMPLETE — in-scope ids that no design maps:
895
- {{bridge_gaps}}
896
- The code may well deliver them; what is missing is the record of WHERE. Nothing was
897
- failed for this. Append a pass that maps them — /appweaver story-plan for the story
898
- that owns the id, or /appweaver plan when the gap is feature-level — so the next run
899
- of this gate can trace them instead of inferring them.
900
-
901
- # ═══════ OKF KNOWLEDGE — keep the slice's concept true to what now exists ═══════
373
+ # ═══════ PASS 3 — OKF KNOWLEDGE: keep the slice's concept true to what now exists ═══════
902
374
  # analyze-slice is the LAST stage that changes a greenfield slice's files: both fix loops
903
375
  # above run with Write/Edit, and the conformance loop is explicitly asked to ADD missing
904
376
  # constraint tests. Those files did not exist when implement wrote the OKF concept, so its
@@ -906,18 +378,20 @@ workflow:
906
378
  # the concept holds the full set implement recorded, these two vars hold only what the fix
907
379
  # loops touched), then report cross-reference drift.
908
380
  #
909
- # --lane backend: both fix loops repair the code that failed a test or an overlay rule,
910
- # and that is service-lane code. A UI file edited here would land in the service list —
911
- # the same defect --lane was added to close — so if that ever becomes possible the fix
912
- # is a second, lane-split merge, not a wider default.
381
+ # These two steps are now the WHOLE write-back: the former `design-reconcile` step that
382
+ # preceded them was deleted (it copied on-disk file bodies into path-tagged design fences
383
+ # — meaningless once backend-plan.md became tech-agnostic prose, and it read the same
384
+ # fixed_files/paths these steps already own). Recording the files in the concept IS the
385
+ # current reconcile.
913
386
  #
914
387
  # Deliberately NOT re-running `record`: the Depends-On/Cross-References edges derive from
915
- # the feature plan's §1 + its External integrations table, and no step in this stage edits
916
- # that plan — the edge sources cannot have moved here. `--origin` is likewise omitted:
917
- # concept-upsert setdefaults it, so an adopted (brownfield) slice keeps its
918
- # `reverse-engineered` provenance instead of being restamped greenfield.
388
+ # backend-plan.md §1 + its External integrations table, and no step in this stage edits
389
+ # backend-plan.md — the edge sources
390
+ # cannot have moved here. `--origin` is likewise omitted: concept-upsert setdefaults it,
391
+ # so an adopted (brownfield) slice keeps its `reverse-engineered` provenance instead of
392
+ # being restamped greenfield.
919
393
  #
920
- # Advisory by design (on_error: continue, no gate): a green, contract-satisfying slice is
394
+ # Advisory by design (on_error: continue, no gate): a green, domain-conformant slice is
921
395
  # never failed over its knowledge index — drift is reported in the outputs and fixed by
922
396
  # `aw-xref validate --prune` / re-running the owning slice's `record`.
923
397
  - id: okf_merge_test_fixes
@@ -926,7 +400,7 @@ workflow:
926
400
  on_error: continue
927
401
  script: { ref: xref }
928
402
  subcommand: concept-upsert
929
- args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{fixed_files}}", "--merge", "--lane", "backend"]
403
+ args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{fixed_files}}", "--merge"]
930
404
  outputs:
931
405
  - { name: okf_concept, from: "$.concept" }
932
406
 
@@ -936,7 +410,7 @@ workflow:
936
410
  on_error: continue
937
411
  script: { ref: xref }
938
412
  subcommand: concept-upsert
939
- args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{conformance_fixes}}", "--merge", "--lane", "backend"]
413
+ args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{conformance_fixes}}", "--merge"]
940
414
  outputs:
941
415
  - { name: okf_concept, from: "$.concept" }
942
416
 
@@ -949,86 +423,6 @@ workflow:
949
423
  - { name: okf_ok, from: "$.ok" }
950
424
  - { name: okf_orphans, from: "$.orphan_count" }
951
425
 
952
- # ═══════ THE RECORD — written on EVERY run that gets here, green or red ═══════
953
- # A gate that leaves nothing behind when it passes gives a reviewer no evidence that it
954
- # ran at all, and the spec→design→code table is the whole point of tracing rather than
955
- # merely testing.
956
- - id: ctx_report
957
- action: build_context
958
- vars:
959
- scope_brief: "{{scope_brief}}"
960
- trace_coverage: "{{trace_coverage}}"
961
- trace_gaps: "{{trace_gaps}}"
962
- bridge_gaps: "{{bridge_gaps}}"
963
- tech_note: "{{tech_note}}"
964
- tdiff_digest: "{{tdiff_digest}}"
965
- - id: write_report
966
- action: agent_author
967
- include_standards: false
968
- output_target: { type: file, path: "{{report_path}}" }
969
- task: |
970
- Write the Slice Analyze Report to the output. It records what this gate found
971
- tracing spec.md through the design to the code.
972
-
973
- Verdict: contract satisfied={{trace_ok}}, bridge complete={{bridge_ok}}.
974
- Gaps: {{trace_gap_count}} on {{trace_gap_refs}}.
975
- Scope: {{scope}}-scoped, story "{{story}}" (empty on a feature-scoped run) ·
976
- spec version {{current_version}}.
977
- {{scope_brief}}
978
- Passes: tests green={{test_ok}} (tool {{test_tool}}); technical oracle —
979
- {{tech_note}}; domain conformance={{conformance_ok}}; integrity fence={{integrity_ok}}
980
- over {{referrer_count}} referrer(s) ({{referrer_names}}). Code analysed:
981
- {{be_count}} service file(s) + {{fe_count}} UI file(s) from the OKF concept
982
- {{okf_concept}}; declared but absent from disk: {{paths_missing}}.
983
- Coverage rows:
984
- {{trace_coverage}}
985
- Gaps:
986
- {{trace_gaps}}
987
- Bridge gaps:
988
- {{bridge_gaps}}
989
-
990
- Markdown format:
991
- - `# Slice Analyze Report — <aggregate>` and, under it, a line naming the scope
992
- (story id or "whole feature"), the spec version, and when this ran.
993
- - A verdict line: "verdict: DELIVERS ✓" or "verdict: GAPS ✗ · <n> open". Add a
994
- second line for the bridge when it is incomplete.
995
- - `## Traceability (spec → design → code)`: a table
996
- | spec id | what the spec requires | design element | code | status |
997
- one row per in-scope id, from the coverage rows above. This is the reason the
998
- report exists — write it on a green run too. If a row's design cell is empty the
999
- id is unbridged; say so in the cell rather than leaving it blank.
1000
- - `## Gaps`: a table | # | severity | spec ref | kind | where | gap | route |.
1001
- Omit the section when there were none.
1002
- - `## Passes`: one line each for behaviour, the technical-story oracle, domain
1003
- conformance and integrity, with the result given above. Say plainly when the
1004
- oracle was UNVERIFIED rather than passed.
1005
- - `## How to resolve`: group the gaps by route and give the exact command for
1006
- each — code-missing/code-wrong go to /appweaver implement (correct the design
1007
- pass first if the pass is what is wrong), bridge-missing to /appweaver story-plan,
1008
- spec-wrong to /appweaver sync-spec. Omit on a clean run.
1009
- Do not invent ids, versions, dates or findings beyond the values given above.
1010
- Return the complete report markdown only.
1011
-
1012
- # The machine-readable verdict, written beside the report by the SCRIPT — CI cannot
1013
- # branch on prose, and a verdict is exactly the kind of dated, derived record the model
1014
- # never writes. `ok` from this step means the file was written; the pass/fail verdict is
1015
- # `status` inside it.
1016
- - id: gate_result
1017
- action: execute_script
1018
- script: { ref: actions }
1019
- subcommand: gate-result
1020
- # The lenient loader needs a flow sequence on ONE line.
1021
- args: ["--gate", "analyze-slice", "--feature-dir", "{{feature_dir}}", "--ok", "{{trace_ok}}", "--spec-version", "{{current_version}}", "--story", "{{story}}", "--report", "{{report_path}}", "--gap-count", "{{trace_gap_count}}", "--gap-refs", "{{trace_gap_refs}}", "--field", "scope={{scope}}", "--field", "story_type={{story_type}}", "--field", "bridge_ok={{bridge_ok}}", "--field", "tests_green={{test_ok}}", "--field", "test_suite_diff={{oracle}}", "--field", "technical_oracle={{tech_note}}", "--field", "tests_changed={{tests_changed_csv}}", "--field", "domain_conformance={{conformance_ok}}", "--field", "integrity={{integrity_ok}}", "--field", "referrers={{referrer_names}}", "--field", "okf_concept={{okf_concept}}", "--field", "okf_index_ok={{okf_ok}}", "--field", "stale_ids={{stale_csv}}"]
1022
- outputs:
1023
- - { name: result_file, from: "$.result_file" }
1024
-
1025
- # THE GATE. Last, so the report and the verdict are on disk whichever way it goes.
1026
- - id: trace_gate
1027
- action: verify_context
1028
- require_true: trace_ok
1029
- on_false: stop
1030
- on_false_message: "The code does not deliver the contract — {{trace_gap_count}} gap(s) on {{trace_gap_refs}}. The traceability report is at {{report_path}} and the machine-readable verdict at {{result_file}}. Nothing was repaired: a missing requirement is feature work, and writing it inside a gate would produce code no design pass names and no gate ever conformed. Route by each gap's `kind` — code-missing / code-wrong: correct the design pass if it is what is wrong (/appweaver story-plan), then /appweaver implement; bridge-missing: /appweaver story-plan, which appends a pass that maps the id; spec-wrong: /appweaver sync-spec, which is the ONLY way the contract moves. Then re-run /appweaver analyze-slice."
1031
-
1032
426
  - id: resolve_post_hooks
1033
427
  action: execute_script
1034
428
  script: { ref: actions }
@@ -1042,24 +436,13 @@ workflow:
1042
436
 
1043
437
  outputs:
1044
438
  feature_dir: "{{feature_dir}}"
1045
- scope: "{{scope}} — story {{story}} ({{story_type}}), spec {{current_version}}"
1046
- delivers_contract: "{{trace_ok}}"
1047
- bridge_complete: "{{bridge_ok}}"
1048
- gaps: "{{trace_gap_count}} on {{trace_gap_refs}}"
1049
- gap_detail: "{{trace_gaps}}"
1050
- bridge_gaps: "{{bridge_gaps}}"
1051
- report: "{{report_path}}"
1052
- result_json: "{{result_file}}"
1053
- tests: "green={{test_ok}} (tool {{test_tool}})"
1054
- technical_oracle: "{{tech_note}}"
1055
- domain_conformance: "{{conformance_ok}}"
439
+ test_tool: "{{test_tool}}"
1056
440
  referrer_count: "{{referrer_count}}"
1057
441
  referrer_blast_radius: "{{referrer_names}}"
1058
- code_analysed: "{{be_count}} service + {{fe_count}} UI file(s) from {{okf_concept}}"
1059
- # Declared in the concept but absent on disk — advisory index drift, never gated.
1060
- stale_source_files: "{{paths_missing}}"
442
+ okf_concept: "{{okf_concept}}"
1061
443
  okf_index_ok: "{{okf_ok}}"
1062
444
  okf_orphans: "{{okf_orphans}}"
1063
- stale_ids: "{{stale_csv}}"
445
+ # Declared in the concept but absent on disk — advisory index drift, never gated.
446
+ stale_source_files: "{{paths_missing}}"
1064
447
  active_change: "{{active_change}}"
1065
- next: "CHANGE OPEN ({{active_change}}): /appweaver close-delta — the slice delivers the contract, is domain-conformant and broke no referrer, and its delta is already merged into the master docs, so the change may now be archived and the version bumped. NO OPEN CHANGE: /appweaver close-story {{story}} would ratify its ADRs, flip its interface entries live and archive its design passes — but it is ⌛ NOT BUILT YET, so nothing closes a story and this one stays OPEN. Until it lands, do not read a green run here as the story being delivered: its ids stay in flight, a feature-scoped analyze-slice keeps subtracting them, and its ADRs and interface entries stay `proposed`. Then /appweaver run."
448
+ next: "CHANGE OPEN ({{active_change}}): /appweaver close-delta — the slice is green, domain-conformant and did not break a referrer, and its delta is already merged into the master docs, so the change may now be archived and the version bumped. NO OPEN CHANGE: /appweaver run."