aw-spec-cli 0.1.13 → 0.1.15
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/assets/.appweaver/STACK-PROFILES.md +1 -1
- package/assets/.appweaver/orchestrator/orchestrator.py +5 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +10 -3
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +38 -11
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +2 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +607 -123
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +625 -66
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +407 -134
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +452 -143
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +78 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +597 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +688 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +23 -23
- package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +945 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +1040 -0
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +2069 -28
- package/assets/.appweaver/scripts/bash/aw-xref.sh +2 -1
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +1973 -13
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +2 -1
- package/assets/.appweaver/scripts/python/aw-xref.py +246 -20
- package/assets/.appweaver/templates/design-template.md +88 -54
- package/assets/.appweaver/templates/spec-template.md +12 -10
- package/assets/.appweaver/templates/story-template.md +48 -8
- package/assets/.appweaver/templates/ui-design-template.md +71 -39
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +19 -7
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +35 -26
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design-ui.orch.yaml +0 -386
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design.orch.yaml +0 -382
|
@@ -1,41 +1,84 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
|
-
# aw-spec-analyze-slice (orchestrator) — the
|
|
2
|
+
# aw-spec-analyze-slice (orchestrator) — the spec ↔ CODE conformance GATE
|
|
3
3
|
#
|
|
4
4
|
# The second of the two discrete conformance gates on the forward path, and the
|
|
5
5
|
# sibling of analyze-design:
|
|
6
|
-
# analyze-design gates spec ↔
|
|
7
|
-
# analyze-slice gates
|
|
8
|
-
#
|
|
9
|
-
#
|
|
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:
|
|
10
36
|
# 1. BEHAVIOUR run the tests (paired to the spec's acceptance criteria by the
|
|
11
37
|
# design docs); on red, fix up to 3×.
|
|
12
|
-
# 2.
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
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).
|
|
19
50
|
#
|
|
20
51
|
# TWO POSITIONS, one gate — and in BOTH the master spec.md is the whole truth:
|
|
21
52
|
# BOOTSTRAP implement → build → analyze-slice → run
|
|
22
53
|
# CHANGE implement-delta → build → finalize-delta → analyze-slice → close-delta
|
|
23
54
|
# On the CHANGE path this gate deliberately runs AFTER finalize-delta, which has already
|
|
24
|
-
# merged the delta into spec.md +
|
|
55
|
+
# merged the delta into spec.md + the plans — so the master carries the change and needs
|
|
25
56
|
# no delta-awareness here. It runs BEFORE close-delta, so the change is still OPEN: a red
|
|
26
57
|
# result is recoverable ("fix and re-run"), not a defect on an already-shipped version.
|
|
27
58
|
# The only thing this gate reads the open change for is where to send you next.
|
|
28
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.
|
|
65
|
+
#
|
|
66
|
+
# Launch: /appweaver analyze-slice [<story-id>]
|
|
30
67
|
# Prereq: /appweaver build (compiled code for the active feature).
|
|
31
68
|
# =============================================================================
|
|
32
69
|
|
|
33
70
|
name: aw-spec-analyze-slice
|
|
34
|
-
description: The
|
|
35
|
-
version:
|
|
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
|
|
36
73
|
agent_agnostic: true
|
|
37
74
|
dialect: aw-spec.orchestrator/v1
|
|
38
75
|
|
|
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
|
+
|
|
39
82
|
scripts:
|
|
40
83
|
actions:
|
|
41
84
|
sh: .appweaver/scripts/bash/aw-spec-actions.sh
|
|
@@ -45,8 +88,8 @@ scripts:
|
|
|
45
88
|
ps1: .appweaver/scripts/powershell/aw-xref.ps1
|
|
46
89
|
|
|
47
90
|
# No hardcoded technical standards seed. The technical rule files the code/conformance
|
|
48
|
-
# passes check against (testing + coding-standards) are
|
|
49
|
-
#
|
|
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"
|
|
50
93
|
# by the tech-select module (see the tech_select step below). The DOMAIN gate overlays are
|
|
51
94
|
# still loaded separately, as before (resolve_gate_overlays → load_gate_overlays). This gate
|
|
52
95
|
# does not reference constitution.technical.md. The seed below is behaviour-only — the kit's
|
|
@@ -56,24 +99,99 @@ standards:
|
|
|
56
99
|
- { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
|
|
57
100
|
|
|
58
101
|
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 ---------------------------------------------------
|
|
59
147
|
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 ------------------------------------------
|
|
60
164
|
conformance_ok: "false"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
#
|
|
64
|
-
# are
|
|
65
|
-
|
|
66
|
-
has_change: "false"
|
|
67
|
-
# Integrity pass (referrer blast radius) defaults — safe values if xref/test-run is
|
|
68
|
-
# skipped or errors, so the gate reads a real verdict rather than an empty var.
|
|
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.
|
|
69
170
|
integrity_ok: "false"
|
|
70
171
|
referrer_csv: ""
|
|
71
172
|
referrer_count: "0"
|
|
72
173
|
referrer_names: ""
|
|
73
|
-
|
|
74
|
-
#
|
|
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) --------------------------------------------
|
|
75
184
|
okf_ok: "unknown"
|
|
76
185
|
okf_orphans: "0"
|
|
186
|
+
# --- reporting ------------------------------------------------------------
|
|
187
|
+
report_path: ""
|
|
188
|
+
result_file: ""
|
|
189
|
+
# NAVIGATION ONLY. Whether a change is open decides where this gate sends you next
|
|
190
|
+
# (close-delta vs run) — nothing else in this workflow reads them. On the CHANGE path
|
|
191
|
+
# finalize-delta has already merged the delta into the master docs, so the delta files
|
|
192
|
+
# are redundant here and are deliberately NOT loaded into any context.
|
|
193
|
+
active_change: ""
|
|
194
|
+
has_change: "false"
|
|
77
195
|
|
|
78
196
|
workflow:
|
|
79
197
|
|
|
@@ -94,8 +212,13 @@ workflow:
|
|
|
94
212
|
script: { ref: actions }
|
|
95
213
|
subcommand: feature-paths
|
|
96
214
|
outputs:
|
|
97
|
-
- { name: feature_dir,
|
|
98
|
-
- { name: spec_file,
|
|
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" }
|
|
99
222
|
|
|
100
223
|
- id: require_feature
|
|
101
224
|
action: verify_context
|
|
@@ -103,6 +226,138 @@ workflow:
|
|
|
103
226
|
on_empty: stop
|
|
104
227
|
on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run analyze-slice."
|
|
105
228
|
|
|
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
|
+
# 1. WHICH STORY, if any. An id in the input scopes the gate to that story; anything
|
|
238
|
+
# else widens it to the feature. Deliberately NO ask: this gate has to be runnable
|
|
239
|
+
# unattended, and a gate that stops to ask cannot run in CI. Naming a story narrows
|
|
240
|
+
# it, naming none widens it, and both are legitimate runs.
|
|
241
|
+
- id: story_scan
|
|
242
|
+
action: execute_script
|
|
243
|
+
on_error: continue
|
|
244
|
+
script: { ref: actions }
|
|
245
|
+
subcommand: story-plan-check
|
|
246
|
+
args: ["--feature-dir", "{{feature_dir}}", "--input", "{{USER_INPUT}}"]
|
|
247
|
+
outputs:
|
|
248
|
+
- { name: story_resolved, from: "$.resolved" }
|
|
249
|
+
- { name: story, from: "$.story" }
|
|
250
|
+
- { name: story_path, from: "$.story_path" }
|
|
251
|
+
- { name: story_title, from: "$.story_title" }
|
|
252
|
+
- { name: story_type, from: "$.story_type" }
|
|
253
|
+
- { name: story_status, from: "$.story_status" }
|
|
254
|
+
- { name: story_dir, from: "$.story_dir" }
|
|
255
|
+
- { name: is_technical, from: "$.is_technical" }
|
|
256
|
+
- { name: exercises_csv, from: "$.exercises_csv" }
|
|
257
|
+
- { name: constraints_csv, from: "$.constraints_csv" }
|
|
258
|
+
- { name: current_version, from: "$.current_version" }
|
|
259
|
+
- { name: stale_csv, from: "$.stale_csv" }
|
|
260
|
+
- { name: stale_note, from: "$.stale_note" }
|
|
261
|
+
- { name: backlog_digest, from: "$.digest" }
|
|
262
|
+
- { name: story_be_doc, from: "$.be_doc_path" }
|
|
263
|
+
- { name: story_be_exists, from: "$.be_doc_exists" }
|
|
264
|
+
- { name: story_be_pass, from: "$.be_last_pass" }
|
|
265
|
+
- { name: story_fe_doc, from: "$.fe_doc_path" }
|
|
266
|
+
- { name: story_fe_exists, from: "$.fe_doc_exists" }
|
|
267
|
+
- { name: story_fe_pass, from: "$.fe_last_pass" }
|
|
268
|
+
|
|
269
|
+
# WHICH IDS ARE IN FLIGHT. On a feature-scoped run these are subtracted from the
|
|
270
|
+
# question: a requirement with an OPEN story is work someone is doing, and reporting
|
|
271
|
+
# it as missing code turns every partially-built feature red (law 39). Story-scoped
|
|
272
|
+
# runs subtract nothing — the story under analysis IS the work in flight.
|
|
273
|
+
- id: inflight_ids
|
|
274
|
+
action: execute_script
|
|
275
|
+
on_error: continue
|
|
276
|
+
script: { ref: actions }
|
|
277
|
+
subcommand: story-refs
|
|
278
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
279
|
+
outputs:
|
|
280
|
+
- { name: inflight_csv, from: "$.covered_csv" }
|
|
281
|
+
|
|
282
|
+
# 2. SCOPE + BRIDGE, stated once as the two sentences the trace pass reads. Both vars
|
|
283
|
+
# default to the feature-scoped wording (see `vars:`) and are overwritten only when
|
|
284
|
+
# a story actually resolved, so nothing here can half-apply.
|
|
285
|
+
- id: scope_story
|
|
286
|
+
condition: "{{story_resolved}} == true"
|
|
287
|
+
action: set
|
|
288
|
+
vars:
|
|
289
|
+
scope: "story"
|
|
290
|
+
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."
|
|
291
|
+
# A technical story delivers no requirement, so `exercises:` is empty BY DESIGN and there
|
|
292
|
+
# is nothing to trace forward. Its real check already ran — the unchanged-suite oracle —
|
|
293
|
+
# and what is left for trace is the bounds it is still held to. Without this the pass
|
|
294
|
+
# would be handed "in scope: (nothing)" and would go looking for something to report.
|
|
295
|
+
- id: scope_story_technical
|
|
296
|
+
condition: "{{is_technical}} == true"
|
|
297
|
+
action: set
|
|
298
|
+
vars:
|
|
299
|
+
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."
|
|
300
|
+
|
|
301
|
+
- id: scope_feature_note
|
|
302
|
+
condition: "{{story_resolved}} == false"
|
|
303
|
+
action: set
|
|
304
|
+
vars:
|
|
305
|
+
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."
|
|
306
|
+
- id: bridge_story
|
|
307
|
+
condition: "{{story_be_exists}} == true"
|
|
308
|
+
action: set
|
|
309
|
+
vars:
|
|
310
|
+
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."
|
|
311
|
+
- id: bridge_story_ui
|
|
312
|
+
condition: "{{story_fe_exists}} == true"
|
|
313
|
+
action: set
|
|
314
|
+
vars:
|
|
315
|
+
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."
|
|
316
|
+
|
|
317
|
+
# 3. WHERE THE VERDICT GOES. A story-scoped verdict written at feature level would be
|
|
318
|
+
# overwritten by the next story's run and then read as the feature's, so each scope
|
|
319
|
+
# keeps its own file. gate-result writes the JSON beside the markdown and names it
|
|
320
|
+
# after it, so a job holding either path holds the other.
|
|
321
|
+
# A named story that matched nothing would otherwise widen the run to feature scope in
|
|
322
|
+
# silence, which is a different gate than the one that was asked for. Announced, never
|
|
323
|
+
# refused: the input may equally have been guidance that names no story at all.
|
|
324
|
+
- id: mark_unresolved_input
|
|
325
|
+
condition: "{{story_resolved}} == false"
|
|
326
|
+
action: set
|
|
327
|
+
vars: { unresolved_input: "{{USER_INPUT}}" }
|
|
328
|
+
- id: unresolved_input_notice
|
|
329
|
+
action: verify_context
|
|
330
|
+
require_value: unresolved_input
|
|
331
|
+
equals: ""
|
|
332
|
+
on_mismatch: continue
|
|
333
|
+
on_mismatch_message: |
|
|
334
|
+
No story matched "{{unresolved_input}}", so this run is FEATURE-SCOPED — every FR- and
|
|
335
|
+
BR- in the spec except the ids with open stories. If you meant to gate one story, re-run
|
|
336
|
+
naming it exactly as its file does. The open backlog:
|
|
337
|
+
{{backlog_digest}}
|
|
338
|
+
|
|
339
|
+
- id: report_paths
|
|
340
|
+
action: set
|
|
341
|
+
vars: { report_path: "{{feature_dir}}/slice-analyze-report.md" }
|
|
342
|
+
- id: report_paths_story
|
|
343
|
+
condition: "{{story_resolved}} == true"
|
|
344
|
+
action: set
|
|
345
|
+
vars: { report_path: "{{story_dir}}/slice-analyze-report.md" }
|
|
346
|
+
|
|
347
|
+
# 3a. INVALIDATE any previous verdict, before anything downstream can stop. A gate that
|
|
348
|
+
# writes its result only on completion leaves the LAST run's verdict on disk for
|
|
349
|
+
# every run that aborts — a red test loop, a broken referrer — and a pipeline
|
|
350
|
+
# reading that file would act on a certification this run never made. Every exit
|
|
351
|
+
# from here on therefore leaves at least a red; the real verdict overwrites it.
|
|
352
|
+
- id: gate_result_open
|
|
353
|
+
action: execute_script
|
|
354
|
+
script: { ref: actions }
|
|
355
|
+
subcommand: gate-result
|
|
356
|
+
# The lenient loader needs a flow sequence on ONE line.
|
|
357
|
+
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"]
|
|
358
|
+
outputs:
|
|
359
|
+
- { name: result_file, from: "$.result_file" }
|
|
360
|
+
|
|
106
361
|
# Is a change OPEN on this feature? Read for NAVIGATION only — see the vars note. Safe
|
|
107
362
|
# on the bootstrap path: with no active_version.json, version-read returns
|
|
108
363
|
# active_change="" / has_change=false and exits 0, so nothing here is conditional on it.
|
|
@@ -115,21 +370,27 @@ workflow:
|
|
|
115
370
|
- { name: active_change, from: "$.active_change" }
|
|
116
371
|
- { name: has_change, from: "$.has_change" }
|
|
117
372
|
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
-
|
|
373
|
+
# 4. THE CODE UNDER ANALYSIS, from the slice's OKF concept — BOTH LANES. Every pass
|
|
374
|
+
# below works over this list. `concept-paths` reads `source_files` alone, so from
|
|
375
|
+
# the moment implement-ui started writing `ui_source_files` a UI-bearing requirement
|
|
376
|
+
# had no UI code in view to be traced to; `all_paths` is the union of the two.
|
|
377
|
+
# A story narrows the QUESTION, not the file set: a requirement can only be judged
|
|
378
|
+
# against everything that could realise it, and the concept records no per-story
|
|
379
|
+
# split. Read-only, exits 0 with empty lists when there is no concept yet;
|
|
380
|
+
# on_error: continue so a missing bundle degrades the gate rather than blocking it.
|
|
381
|
+
- id: concept_coverage
|
|
125
382
|
action: execute_script
|
|
126
383
|
on_error: continue
|
|
127
384
|
script: { ref: xref }
|
|
128
|
-
subcommand: concept-
|
|
129
|
-
args: ["--feature-dir", "{{feature_dir}}"]
|
|
385
|
+
subcommand: concept-coverage
|
|
386
|
+
args: ["--feature-dir", "{{feature_dir}}", "--ids", "{{exercises_csv}}"]
|
|
130
387
|
outputs:
|
|
131
|
-
- { name: all_paths,
|
|
132
|
-
- { name: paths_missing,
|
|
388
|
+
- { name: all_paths, from: "$.all_paths" }
|
|
389
|
+
- { name: paths_missing, from: "$.all_missing_csv" }
|
|
390
|
+
- { name: coverage_digest, from: "$.digest" }
|
|
391
|
+
- { name: be_count, from: "$.backend_count" }
|
|
392
|
+
- { name: fe_count, from: "$.frontend_count" }
|
|
393
|
+
- { name: okf_concept, from: "$.concept" }
|
|
133
394
|
|
|
134
395
|
- id: resolve_pre_hooks
|
|
135
396
|
action: execute_script
|
|
@@ -144,6 +405,15 @@ workflow:
|
|
|
144
405
|
|
|
145
406
|
# ═══════ PASS 1 — BEHAVIOUR: the slice's tests vs the spec's acceptance criteria ═══════
|
|
146
407
|
# Run tests → (if red) fix → re-run, up to 3×.
|
|
408
|
+
#
|
|
409
|
+
# For a TECHNICAL story the fix loop is told not to touch a test, because a test it
|
|
410
|
+
# edited to reach green is itself the behaviour change pass 2 exists to catch — and
|
|
411
|
+
# pass 2 counts this loop's edits, so the rule and the oracle agree.
|
|
412
|
+
- id: arm_no_test_edit
|
|
413
|
+
condition: "{{is_technical}} == true"
|
|
414
|
+
action: set
|
|
415
|
+
vars:
|
|
416
|
+
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."
|
|
147
417
|
- id: test_loop
|
|
148
418
|
action: loop
|
|
149
419
|
while: "{{test_ok}} != true"
|
|
@@ -172,6 +442,7 @@ workflow:
|
|
|
172
442
|
action: build_context
|
|
173
443
|
vars:
|
|
174
444
|
test_log: "{{test_log}}"
|
|
445
|
+
no_test_edit_rule: "{{no_test_edit_rule}}"
|
|
175
446
|
context:
|
|
176
447
|
- { path: "{{spec_file}}" }
|
|
177
448
|
context_from: all_paths
|
|
@@ -182,7 +453,7 @@ workflow:
|
|
|
182
453
|
task: |
|
|
183
454
|
Tests failed. Make them pass by fixing the code (or a genuinely wrong
|
|
184
455
|
test) in the files in context, keeping behaviour aligned with the spec's
|
|
185
|
-
acceptance criteria (spec in context). Test log:
|
|
456
|
+
acceptance criteria (spec in context). {{no_test_edit_rule}} Test log:
|
|
186
457
|
{{test_log}}
|
|
187
458
|
outputs:
|
|
188
459
|
- { name: fixed_files, from: "$.files" }
|
|
@@ -201,7 +472,87 @@ workflow:
|
|
|
201
472
|
ones this feature did not cause; matched is not meaningful in that case. Last log:
|
|
202
473
|
{{test_log}}
|
|
203
474
|
|
|
204
|
-
# ═══════ PASS 2 —
|
|
475
|
+
# ═══════ PASS 2 — THE TECHNICAL-STORY ORACLE: the suite must be UNCHANGED ═══════
|
|
476
|
+
# A technical story (a refactor, a dependency upgrade, a performance fix) delivers no
|
|
477
|
+
# requirement, so `exercises:` is empty and there is no FR- to trace it to. The
|
|
478
|
+
# existing test suite is its whole oracle, and the rule is stronger than "the tests are
|
|
479
|
+
# green": a test that had to be MODIFIED means behaviour moved, and the work was a
|
|
480
|
+
# functional story citing a requirement (law 32). Run AFTER pass 1 on purpose, so the
|
|
481
|
+
# fix loop's own edits are counted — a gate that had to rewrite a test to reach green
|
|
482
|
+
# has proved the same thing.
|
|
483
|
+
#
|
|
484
|
+
# A new test is not a changed one: adding characterisation tests around a refactor is
|
|
485
|
+
# exactly the work a technical story exists to do. Deletions count as changes.
|
|
486
|
+
- id: test_diff
|
|
487
|
+
action: execute_script
|
|
488
|
+
on_error: continue
|
|
489
|
+
script: { ref: actions }
|
|
490
|
+
subcommand: test-diff
|
|
491
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
492
|
+
outputs:
|
|
493
|
+
- { name: oracle, from: "$.oracle" }
|
|
494
|
+
- { name: tests_changed_csv, from: "$.tests_changed_csv" }
|
|
495
|
+
- { name: tests_changed_count, from: "$.tests_changed_count" }
|
|
496
|
+
- { name: tdiff_digest, from: "$.digest" }
|
|
497
|
+
- { name: tdiff_base, from: "$.base" }
|
|
498
|
+
- { name: tdiff_base_kind, from: "$.base_kind" }
|
|
499
|
+
|
|
500
|
+
# The story's type and the diff's state, combined into ONE string so each case is a
|
|
501
|
+
# single comparison the evaluator can make. Both halves are strings, so this needs no
|
|
502
|
+
# boolean gymnastics — a bool rendered by {{ }} and a bool read by require_value never
|
|
503
|
+
# compare equal (law 43), which is why the type arrives as a word rather than a flag.
|
|
504
|
+
- id: oracle_case_set
|
|
505
|
+
action: set
|
|
506
|
+
vars: { oracle_case: "{{story_type}}/{{oracle}}" }
|
|
507
|
+
- id: oracle_fail
|
|
508
|
+
condition: "{{oracle_case}} == technical/changed"
|
|
509
|
+
action: set
|
|
510
|
+
vars:
|
|
511
|
+
tech_gate_ok: "false"
|
|
512
|
+
tech_note: "FAILED — {{tests_changed_count}} test(s) modified or deleted: {{tests_changed_csv}}"
|
|
513
|
+
- id: oracle_unverified
|
|
514
|
+
condition: "{{oracle_case}} == technical/unverified"
|
|
515
|
+
action: set
|
|
516
|
+
vars:
|
|
517
|
+
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."
|
|
518
|
+
- id: oracle_clean
|
|
519
|
+
condition: "{{oracle_case}} == technical/clean"
|
|
520
|
+
action: set
|
|
521
|
+
vars:
|
|
522
|
+
tech_note: "PASSED — no test was modified or deleted against {{tdiff_base_kind}} baseline {{tdiff_base}}."
|
|
523
|
+
|
|
524
|
+
# An oracle that could not be read never fails the gate — it announces. Refusing on the
|
|
525
|
+
# strength of a check that did not run is the mirror image of passing on one.
|
|
526
|
+
- id: oracle_unverified_notice
|
|
527
|
+
condition: "{{oracle_case}} == technical/unverified"
|
|
528
|
+
action: verify_context
|
|
529
|
+
require_value: oracle
|
|
530
|
+
equals: "clean"
|
|
531
|
+
on_mismatch: continue
|
|
532
|
+
on_mismatch_message: |
|
|
533
|
+
TECHNICAL-STORY ORACLE UNVERIFIED for story {{story}}. A technical story delivers no
|
|
534
|
+
requirement, so the unchanged test suite is the only thing that can say whether its
|
|
535
|
+
behaviour moved — and that history is not readable here (this is not a git working
|
|
536
|
+
copy). Everything else in this gate ran; this one check did not, and it is recorded
|
|
537
|
+
as unverified rather than as a pass in {{result_file}}.
|
|
538
|
+
|
|
539
|
+
- id: oracle_gate
|
|
540
|
+
action: verify_context
|
|
541
|
+
require_true: tech_gate_ok
|
|
542
|
+
on_false: stop
|
|
543
|
+
on_false_message: |
|
|
544
|
+
TECHNICAL-STORY ORACLE FAILED — story {{story}} is type `technical`, and a technical
|
|
545
|
+
story's oracle is the UNCHANGED test suite. These tests were modified or deleted:
|
|
546
|
+
{{tests_changed_csv}}
|
|
547
|
+
{{tdiff_digest}}
|
|
548
|
+
A test that had to change means behaviour changed, and behaviour that changes belongs
|
|
549
|
+
to a FUNCTIONAL story citing the requirement it moves. Two ways out, and they are not
|
|
550
|
+
interchangeable: restore the tests and make the refactor behaviour-preserving, or
|
|
551
|
+
accept that this work changes behaviour — close this story as cancelled, raise a
|
|
552
|
+
functional story for the change through /appweaver sync-spec (if the contract moves)
|
|
553
|
+
or /appweaver add-story, and build it there where an FR- can be traced to it.
|
|
554
|
+
|
|
555
|
+
# ═══════ PASS 3 — DOMAIN CONFORMANCE: code vs the gate overlay checklist ═══════
|
|
205
556
|
# With tests green, scan the feature's CODE against the domain gate overlays (the
|
|
206
557
|
# forbidden-patterns / refusal conditions / CI gates). The overlays load as the
|
|
207
558
|
# checklist — VALIDATION only, never an authoring input to feature code. No-op for a
|
|
@@ -225,6 +576,8 @@ workflow:
|
|
|
225
576
|
steps:
|
|
226
577
|
- id: ctx_conformance
|
|
227
578
|
action: build_context
|
|
579
|
+
vars:
|
|
580
|
+
no_test_edit_rule: "{{no_test_edit_rule}}"
|
|
228
581
|
context:
|
|
229
582
|
- { path: "{{spec_file}}" }
|
|
230
583
|
context_from: all_paths
|
|
@@ -259,7 +612,8 @@ workflow:
|
|
|
259
612
|
Fix these domain-conformance violations in the feature's code (in context),
|
|
260
613
|
following the gate checklist rules (in the standards) and keeping the design's
|
|
261
614
|
behaviour intact. Do NOT weaken or delete a test to hide a violation — fix the
|
|
262
|
-
code. After fixing, re-run the tests (Bash) to confirm
|
|
615
|
+
code. {{no_test_edit_rule}} After fixing, re-run the tests (Bash) to confirm
|
|
616
|
+
they stay green.
|
|
263
617
|
Violations: {{conformance_violations}}
|
|
264
618
|
outputs:
|
|
265
619
|
- { name: conformance_fixes, from: "$.files" }
|
|
@@ -268,9 +622,9 @@ workflow:
|
|
|
268
622
|
action: verify_context
|
|
269
623
|
require_true: conformance_ok
|
|
270
624
|
on_false: stop
|
|
271
|
-
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 →
|
|
625
|
+
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)."
|
|
272
626
|
|
|
273
|
-
# ═══════
|
|
627
|
+
# ═══════ PASS 4 — INTEGRITY: did this change break any aggregate that USES it? ═══════
|
|
274
628
|
# The slice is green + domain-conformant on its own. But a change to a feature can break
|
|
275
629
|
# any OTHER aggregate that references it. Ask the OKF Cross-References index "who refers to
|
|
276
630
|
# me?" (the transitive referrer closure — C→B→A all count), then RE-RUN the feature's tests
|
|
@@ -288,7 +642,7 @@ workflow:
|
|
|
288
642
|
outputs:
|
|
289
643
|
- { name: referrer_csv, from: "$.referrer_closure_csv" }
|
|
290
644
|
- { name: referrer_count, from: "$.referrer_closure_count" }
|
|
291
|
-
- { name: referrer_names, from: "$.
|
|
645
|
+
- { name: referrer_names, from: "$.referrer_closure_csv" }
|
|
292
646
|
|
|
293
647
|
- id: run_integrity
|
|
294
648
|
action: execute_script
|
|
@@ -359,7 +713,123 @@ workflow:
|
|
|
359
713
|
on_false: stop
|
|
360
714
|
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}}"
|
|
361
715
|
|
|
362
|
-
# ═══════ PASS
|
|
716
|
+
# ═══════ PASS 5 — TRACE: spec ↔ code, through the design ═══════════════════
|
|
717
|
+
# The question this gate exists to answer, asked last because everything before it is a
|
|
718
|
+
# precondition: the code runs, it changed no oracle it should not have, it violates no
|
|
719
|
+
# domain constraint, and it broke nobody. What is left is the only thing that matters —
|
|
720
|
+
# is this the thing the contract asked for?
|
|
721
|
+
#
|
|
722
|
+
# READ-ONLY. It reports and never repairs: a missing requirement is feature work, and
|
|
723
|
+
# writing it here would produce code no design pass names and no gate ever conformed.
|
|
724
|
+
# Nothing between this step and the report can stop the run, so the verdict always
|
|
725
|
+
# reaches both the markdown and the JSON.
|
|
726
|
+
- id: ctx_trace
|
|
727
|
+
action: build_context
|
|
728
|
+
vars:
|
|
729
|
+
scope_brief: "{{scope_brief}}"
|
|
730
|
+
bridge_note: "{{bridge_note}}"
|
|
731
|
+
coverage_digest: "{{coverage_digest}}"
|
|
732
|
+
stale_note: "{{stale_note}}"
|
|
733
|
+
context:
|
|
734
|
+
- { path: "{{spec_file}}" } # THE ORACLE — the contract's own wording
|
|
735
|
+
- { path: "{{story_path}}", optional: true } # the extent: what this slice claims of each id
|
|
736
|
+
- { path: "{{story_be_doc}}", optional: true } # the BRIDGE, service lane — the latest pass
|
|
737
|
+
- { path: "{{story_fe_doc}}", optional: true } # the BRIDGE, UI lane — the latest pass
|
|
738
|
+
- { path: "{{be_plan}}", optional: true } # architecture: where those elements live
|
|
739
|
+
- { path: "{{fe_plan}}", optional: true }
|
|
740
|
+
- { path: "{{feature_dir}}/interfaces.md", optional: true } # the published surface, cited by IF- id
|
|
741
|
+
- { path: "{{feature_dir}}/adr.md", optional: true } # ratified decisions
|
|
742
|
+
context_from: all_paths # the CODE — both lanes, from the OKF concept
|
|
743
|
+
- id: trace
|
|
744
|
+
action: agent_query
|
|
745
|
+
tools: Read # follow a call out of the listed files when a trace needs it
|
|
746
|
+
include_standards: false # the contract is the spec, not a rule corpus
|
|
747
|
+
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 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>" }
|
|
748
|
+
mock: { ok: true, bridge_ok: true, gap_count: 0, gap_refs: "", coverage: "", gaps: [], bridge_gaps: "" }
|
|
749
|
+
task: |
|
|
750
|
+
spec.md is the ORACLE. Judge the CODE against it and report every id it fails to
|
|
751
|
+
deliver. The design is the BRIDGE that makes the question answerable — it says
|
|
752
|
+
which elements realise which id — and it is never the standard: where the design
|
|
753
|
+
and the spec disagree about an in-scope id, the SPEC is what the code is measured
|
|
754
|
+
against, and the disagreement is itself a finding.
|
|
755
|
+
|
|
756
|
+
{{scope_brief}}
|
|
757
|
+
|
|
758
|
+
{{bridge_note}}
|
|
759
|
+
|
|
760
|
+
The code in context is this slice's whole OKF file list, both lanes. Use Read to
|
|
761
|
+
follow anything it calls that a trace needs. Do NOT modify a single file — this
|
|
762
|
+
step is read-only by contract.
|
|
763
|
+
|
|
764
|
+
{{coverage_digest}}
|
|
765
|
+
|
|
766
|
+
HOW TO TRACE each in-scope id, in this order:
|
|
767
|
+
(1) Resolve the id's wording from spec.md. That wording — not the design's
|
|
768
|
+
restatement of it — is the standard.
|
|
769
|
+
(2) Find the DESIGN element that maps it (the story pass's "### Changes" and
|
|
770
|
+
"### Approach", or the feature plan's traceability section). If nothing maps
|
|
771
|
+
it, that is a BRIDGE gap: record it in `bridge_gaps` with kind
|
|
772
|
+
"bridge-missing". Then keep going — code may well deliver the id anyway, and
|
|
773
|
+
an undocumented realisation is a documentation defect, not a broken contract.
|
|
774
|
+
(3) Find the CODE that realises the element, and read it. Judge whether it
|
|
775
|
+
delivers what the spec words, to the extent this scope claims.
|
|
776
|
+
(4) Where the id has a user-facing surface, it is realised in BOTH lanes and needs
|
|
777
|
+
a home in each. A UX- lives only in the UI lane; a BR- only in the service
|
|
778
|
+
lane. Do not report a UI gap for a slice that has no UI lane in scope.
|
|
779
|
+
|
|
780
|
+
CLASSIFY every gap by `kind`, because the four routes are different commands:
|
|
781
|
+
code-missing the design maps the id and no code realises it → /appweaver implement
|
|
782
|
+
code-wrong code exists and does something else → /appweaver implement
|
|
783
|
+
bridge-missing no design maps the id at all → /appweaver story-plan
|
|
784
|
+
spec-wrong the code is right and the SPEC words it wrongly → /appweaver sync-spec
|
|
785
|
+
Use `spec-wrong` sparingly and only with a reason: the spec is the oracle, and
|
|
786
|
+
"the code disagrees" is evidence about the code first.
|
|
787
|
+
|
|
788
|
+
THESE ARE NOT GAPS, and reporting them is noise that hides the real ones:
|
|
789
|
+
- an id that is out of scope for this run — another story's work, or one nobody
|
|
790
|
+
has claimed yet;
|
|
791
|
+
- the part of a requirement this scope explicitly does not claim;
|
|
792
|
+
- style, naming, structure, layering or test-coverage opinions — other gates own
|
|
793
|
+
every one of those;
|
|
794
|
+
- anything a design pass deliberately deferred and named under its risks;
|
|
795
|
+
- a missing element you inferred from the design alone with no spec basis. The
|
|
796
|
+
design is the bridge, not the contract.
|
|
797
|
+
|
|
798
|
+
Fill `coverage` with ONE LINE PER IN-SCOPE ID, delivered or not — that table is the
|
|
799
|
+
traceability record this gate exists to leave behind, and it is written on a green
|
|
800
|
+
run too. Set gap_count and gap_refs from `gaps`. Set ok=true ONLY when every
|
|
801
|
+
in-scope id is delivered as the spec words it. Set bridge_ok=false when any
|
|
802
|
+
in-scope id has no design mapping it, INDEPENDENTLY of ok — working code with a
|
|
803
|
+
documentation gap is a true state and must be reportable as one.
|
|
804
|
+
{{stale_note}}
|
|
805
|
+
outputs:
|
|
806
|
+
- { name: trace_ok, from: "$.ok" }
|
|
807
|
+
- { name: bridge_ok, from: "$.bridge_ok" }
|
|
808
|
+
- { name: trace_gaps, from: "$.gaps" }
|
|
809
|
+
- { name: trace_gap_count, from: "$.gap_count" }
|
|
810
|
+
- { name: trace_gap_refs, from: "$.gap_refs" }
|
|
811
|
+
- { name: trace_coverage, from: "$.coverage" }
|
|
812
|
+
- { name: bridge_gaps, from: "$.bridge_gaps" }
|
|
813
|
+
|
|
814
|
+
# The bridge's integrity is ANNOUNCED, not gated. An id the design never mapped, whose
|
|
815
|
+
# code nonetheless delivers the spec, is a documentation defect — failing the slice for
|
|
816
|
+
# it would refuse working, contract-satisfying code over a missing row in a document
|
|
817
|
+
# the next story-plan pass rewrites anyway.
|
|
818
|
+
- id: bridge_notice
|
|
819
|
+
condition: "{{bridge_ok}} == false"
|
|
820
|
+
action: verify_context
|
|
821
|
+
require_value: bridge_ok
|
|
822
|
+
equals: "true"
|
|
823
|
+
on_mismatch: continue
|
|
824
|
+
on_mismatch_message: |
|
|
825
|
+
BRIDGE INCOMPLETE — in-scope ids that no design maps:
|
|
826
|
+
{{bridge_gaps}}
|
|
827
|
+
The code may well deliver them; what is missing is the record of WHERE. Nothing was
|
|
828
|
+
failed for this. Append a pass that maps them — /appweaver story-plan for the story
|
|
829
|
+
that owns the id, or /appweaver plan when the gap is feature-level — so the next run
|
|
830
|
+
of this gate can trace them instead of inferring them.
|
|
831
|
+
|
|
832
|
+
# ═══════ OKF KNOWLEDGE — keep the slice's concept true to what now exists ═══════
|
|
363
833
|
# analyze-slice is the LAST stage that changes a greenfield slice's files: both fix loops
|
|
364
834
|
# above run with Write/Edit, and the conformance loop is explicitly asked to ADD missing
|
|
365
835
|
# constraint tests. Those files did not exist when implement wrote the OKF concept, so its
|
|
@@ -367,20 +837,18 @@ workflow:
|
|
|
367
837
|
# the concept holds the full set implement recorded, these two vars hold only what the fix
|
|
368
838
|
# loops touched), then report cross-reference drift.
|
|
369
839
|
#
|
|
370
|
-
#
|
|
371
|
-
#
|
|
372
|
-
#
|
|
373
|
-
#
|
|
374
|
-
# current reconcile.
|
|
840
|
+
# --lane backend: both fix loops repair the code that failed a test or an overlay rule,
|
|
841
|
+
# and that is service-lane code. A UI file edited here would land in the service list —
|
|
842
|
+
# the same defect --lane was added to close — so if that ever becomes possible the fix
|
|
843
|
+
# is a second, lane-split merge, not a wider default.
|
|
375
844
|
#
|
|
376
845
|
# Deliberately NOT re-running `record`: the Depends-On/Cross-References edges derive from
|
|
377
|
-
#
|
|
378
|
-
#
|
|
379
|
-
#
|
|
380
|
-
#
|
|
381
|
-
# being restamped greenfield.
|
|
846
|
+
# the feature plan's §1 + its External integrations table, and no step in this stage edits
|
|
847
|
+
# that plan — the edge sources cannot have moved here. `--origin` is likewise omitted:
|
|
848
|
+
# concept-upsert setdefaults it, so an adopted (brownfield) slice keeps its
|
|
849
|
+
# `reverse-engineered` provenance instead of being restamped greenfield.
|
|
382
850
|
#
|
|
383
|
-
# Advisory by design (on_error: continue, no gate): a green,
|
|
851
|
+
# Advisory by design (on_error: continue, no gate): a green, contract-satisfying slice is
|
|
384
852
|
# never failed over its knowledge index — drift is reported in the outputs and fixed by
|
|
385
853
|
# `aw-xref validate --prune` / re-running the owning slice's `record`.
|
|
386
854
|
- id: okf_merge_test_fixes
|
|
@@ -389,7 +857,7 @@ workflow:
|
|
|
389
857
|
on_error: continue
|
|
390
858
|
script: { ref: xref }
|
|
391
859
|
subcommand: concept-upsert
|
|
392
|
-
args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{fixed_files}}", "--merge"]
|
|
860
|
+
args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{fixed_files}}", "--merge", "--lane", "backend"]
|
|
393
861
|
outputs:
|
|
394
862
|
- { name: okf_concept, from: "$.concept" }
|
|
395
863
|
|
|
@@ -399,7 +867,7 @@ workflow:
|
|
|
399
867
|
on_error: continue
|
|
400
868
|
script: { ref: xref }
|
|
401
869
|
subcommand: concept-upsert
|
|
402
|
-
args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{conformance_fixes}}", "--merge"]
|
|
870
|
+
args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{conformance_fixes}}", "--merge", "--lane", "backend"]
|
|
403
871
|
outputs:
|
|
404
872
|
- { name: okf_concept, from: "$.concept" }
|
|
405
873
|
|
|
@@ -412,6 +880,86 @@ workflow:
|
|
|
412
880
|
- { name: okf_ok, from: "$.ok" }
|
|
413
881
|
- { name: okf_orphans, from: "$.orphan_count" }
|
|
414
882
|
|
|
883
|
+
# ═══════ THE RECORD — written on EVERY run that gets here, green or red ═══════
|
|
884
|
+
# A gate that leaves nothing behind when it passes gives a reviewer no evidence that it
|
|
885
|
+
# ran at all, and the spec→design→code table is the whole point of tracing rather than
|
|
886
|
+
# merely testing.
|
|
887
|
+
- id: ctx_report
|
|
888
|
+
action: build_context
|
|
889
|
+
vars:
|
|
890
|
+
scope_brief: "{{scope_brief}}"
|
|
891
|
+
trace_coverage: "{{trace_coverage}}"
|
|
892
|
+
trace_gaps: "{{trace_gaps}}"
|
|
893
|
+
bridge_gaps: "{{bridge_gaps}}"
|
|
894
|
+
tech_note: "{{tech_note}}"
|
|
895
|
+
tdiff_digest: "{{tdiff_digest}}"
|
|
896
|
+
- id: write_report
|
|
897
|
+
action: agent_author
|
|
898
|
+
include_standards: false
|
|
899
|
+
output_target: { type: file, path: "{{report_path}}" }
|
|
900
|
+
task: |
|
|
901
|
+
Write the Slice Analyze Report to the output. It records what this gate found
|
|
902
|
+
tracing spec.md through the design to the code.
|
|
903
|
+
|
|
904
|
+
Verdict: contract satisfied={{trace_ok}}, bridge complete={{bridge_ok}}.
|
|
905
|
+
Gaps: {{trace_gap_count}} on {{trace_gap_refs}}.
|
|
906
|
+
Scope: {{scope}}-scoped, story "{{story}}" (empty on a feature-scoped run) ·
|
|
907
|
+
spec version {{current_version}}.
|
|
908
|
+
{{scope_brief}}
|
|
909
|
+
Passes: tests green={{test_ok}} (tool {{test_tool}}); technical oracle —
|
|
910
|
+
{{tech_note}}; domain conformance={{conformance_ok}}; integrity fence={{integrity_ok}}
|
|
911
|
+
over {{referrer_count}} referrer(s) ({{referrer_names}}). Code analysed:
|
|
912
|
+
{{be_count}} service file(s) + {{fe_count}} UI file(s) from the OKF concept
|
|
913
|
+
{{okf_concept}}; declared but absent from disk: {{paths_missing}}.
|
|
914
|
+
Coverage rows:
|
|
915
|
+
{{trace_coverage}}
|
|
916
|
+
Gaps:
|
|
917
|
+
{{trace_gaps}}
|
|
918
|
+
Bridge gaps:
|
|
919
|
+
{{bridge_gaps}}
|
|
920
|
+
|
|
921
|
+
Markdown format:
|
|
922
|
+
- `# Slice Analyze Report — <aggregate>` and, under it, a line naming the scope
|
|
923
|
+
(story id or "whole feature"), the spec version, and when this ran.
|
|
924
|
+
- A verdict line: "verdict: DELIVERS ✓" or "verdict: GAPS ✗ · <n> open". Add a
|
|
925
|
+
second line for the bridge when it is incomplete.
|
|
926
|
+
- `## Traceability (spec → design → code)`: a table
|
|
927
|
+
| spec id | what the spec requires | design element | code | status |
|
|
928
|
+
one row per in-scope id, from the coverage rows above. This is the reason the
|
|
929
|
+
report exists — write it on a green run too. If a row's design cell is empty the
|
|
930
|
+
id is unbridged; say so in the cell rather than leaving it blank.
|
|
931
|
+
- `## Gaps`: a table | # | severity | spec ref | kind | where | gap | route |.
|
|
932
|
+
Omit the section when there were none.
|
|
933
|
+
- `## Passes`: one line each for behaviour, the technical-story oracle, domain
|
|
934
|
+
conformance and integrity, with the result given above. Say plainly when the
|
|
935
|
+
oracle was UNVERIFIED rather than passed.
|
|
936
|
+
- `## How to resolve`: group the gaps by route and give the exact command for
|
|
937
|
+
each — code-missing/code-wrong go to /appweaver implement (correct the design
|
|
938
|
+
pass first if the pass is what is wrong), bridge-missing to /appweaver story-plan,
|
|
939
|
+
spec-wrong to /appweaver sync-spec. Omit on a clean run.
|
|
940
|
+
Do not invent ids, versions, dates or findings beyond the values given above.
|
|
941
|
+
Return the complete report markdown only.
|
|
942
|
+
|
|
943
|
+
# The machine-readable verdict, written beside the report by the SCRIPT — CI cannot
|
|
944
|
+
# branch on prose, and a verdict is exactly the kind of dated, derived record the model
|
|
945
|
+
# never writes. `ok` from this step means the file was written; the pass/fail verdict is
|
|
946
|
+
# `status` inside it.
|
|
947
|
+
- id: gate_result
|
|
948
|
+
action: execute_script
|
|
949
|
+
script: { ref: actions }
|
|
950
|
+
subcommand: gate-result
|
|
951
|
+
# The lenient loader needs a flow sequence on ONE line.
|
|
952
|
+
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}}"]
|
|
953
|
+
outputs:
|
|
954
|
+
- { name: result_file, from: "$.result_file" }
|
|
955
|
+
|
|
956
|
+
# THE GATE. Last, so the report and the verdict are on disk whichever way it goes.
|
|
957
|
+
- id: trace_gate
|
|
958
|
+
action: verify_context
|
|
959
|
+
require_true: trace_ok
|
|
960
|
+
on_false: stop
|
|
961
|
+
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."
|
|
962
|
+
|
|
415
963
|
- id: resolve_post_hooks
|
|
416
964
|
action: execute_script
|
|
417
965
|
script: { ref: actions }
|
|
@@ -425,13 +973,24 @@ workflow:
|
|
|
425
973
|
|
|
426
974
|
outputs:
|
|
427
975
|
feature_dir: "{{feature_dir}}"
|
|
428
|
-
|
|
976
|
+
scope: "{{scope}} — story {{story}} ({{story_type}}), spec {{current_version}}"
|
|
977
|
+
delivers_contract: "{{trace_ok}}"
|
|
978
|
+
bridge_complete: "{{bridge_ok}}"
|
|
979
|
+
gaps: "{{trace_gap_count}} on {{trace_gap_refs}}"
|
|
980
|
+
gap_detail: "{{trace_gaps}}"
|
|
981
|
+
bridge_gaps: "{{bridge_gaps}}"
|
|
982
|
+
report: "{{report_path}}"
|
|
983
|
+
result_json: "{{result_file}}"
|
|
984
|
+
tests: "green={{test_ok}} (tool {{test_tool}})"
|
|
985
|
+
technical_oracle: "{{tech_note}}"
|
|
986
|
+
domain_conformance: "{{conformance_ok}}"
|
|
429
987
|
referrer_count: "{{referrer_count}}"
|
|
430
988
|
referrer_blast_radius: "{{referrer_names}}"
|
|
431
|
-
|
|
432
|
-
okf_index_ok: "{{okf_ok}}"
|
|
433
|
-
okf_orphans: "{{okf_orphans}}"
|
|
989
|
+
code_analysed: "{{be_count}} service + {{fe_count}} UI file(s) from {{okf_concept}}"
|
|
434
990
|
# Declared in the concept but absent on disk — advisory index drift, never gated.
|
|
435
991
|
stale_source_files: "{{paths_missing}}"
|
|
992
|
+
okf_index_ok: "{{okf_ok}}"
|
|
993
|
+
okf_orphans: "{{okf_orphans}}"
|
|
994
|
+
stale_ids: "{{stale_csv}}"
|
|
436
995
|
active_change: "{{active_change}}"
|
|
437
|
-
next: "CHANGE OPEN ({{active_change}}): /appweaver close-delta — the slice
|
|
996
|
+
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}} to ratify its ADRs, flip its interface entries live and archive its design passes — then /appweaver run."
|