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.
- package/assets/.appweaver/DESIGN-FORMAT.md +18 -18
- package/assets/.appweaver/ORCH-DIALECT.md +1 -3
- package/assets/.appweaver/RULE-PARTITION.md +29 -13
- package/assets/.appweaver/STACK-PROFILES.md +10 -9
- package/assets/.appweaver/orchestrator/.state/smoke.state.json +32 -0
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +4 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +251 -0
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +104 -35
- package/assets/.appweaver/orchestrator/orchestrator.py +5 -4
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +94 -36
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md +115 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +2 -9
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +16 -40
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +38 -17
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +8 -8
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +10 -11
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +673 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +535 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +643 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +84 -701
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +7 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +16 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +243 -144
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +877 -578
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +32 -91
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +836 -514
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +132 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +38 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +271 -979
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1085 -3385
- package/assets/.appweaver/scripts/bash/aw-xref.sh +1 -2
- package/assets/.appweaver/scripts/bash/common.sh +47 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +914 -3220
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +19 -3
- package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
- package/assets/.appweaver/scripts/python/aw-xref.py +86 -350
- package/assets/.appweaver/templates/constitution-ui-template.md +1 -1
- package/assets/.appweaver/templates/delta-design-template.md +120 -30
- package/assets/.appweaver/templates/delta-domain-model-template.md +81 -0
- package/assets/.appweaver/templates/delta-spec-template.md +43 -17
- package/assets/.appweaver/templates/design-template.md +68 -98
- package/assets/.appweaver/templates/domain-model-template.md +130 -0
- package/assets/.appweaver/templates/okf-concept-template.md +2 -2
- package/assets/.appweaver/templates/source-map-template.md +3 -3
- package/assets/.appweaver/templates/spec-template.md +100 -122
- package/assets/.appweaver/templates/ui-delta-design-template.md +159 -0
- package/assets/.appweaver/templates/ui-design-template.md +46 -75
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +7 -18
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +34 -39
- package/assets/guardrails/requirement-spec.txt +2 -2
- package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +0 -105
- package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +0 -87
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -813
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -663
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -600
- package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -1147
- package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1121
- package/assets/.appweaver/templates/story-template.md +0 -178
|
@@ -4,57 +4,75 @@
|
|
|
4
4
|
# Spec-anchored pipeline, spec phase. feature = AGGREGATE (picked/created by
|
|
5
5
|
# /appweaver select-feature, which sets .appweaver/feature.json). specify does
|
|
6
6
|
# NOT guess or create the feature — it CONSUMES the active one, and is phase-pure
|
|
7
|
-
# (spec side only; the
|
|
7
|
+
# (spec side only; the single backend-plan.md is the `design` phase).
|
|
8
8
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
9
|
+
# TWO DOCUMENTS, ONE RUN. specify authors BEHAVIOUR and STRUCTURE together:
|
|
10
|
+
# • spec.md — what the system must do and why (per feature)
|
|
11
|
+
# • <context>/domain-model.md — what the domain IS (per BOUNDED CONTEXT)
|
|
12
|
+
# The aggregate root, its entities, events and invariants (BR-n) live in the MODEL, not
|
|
13
|
+
# in any spec; specs cite them by id. An aggregate is a fact about the context, and
|
|
14
|
+
# several specs act on one — a per-spec copy of the same root drifts until two specs
|
|
15
|
+
# assert different rules for it and nothing arbitrates. Authoring both here is also what
|
|
16
|
+
# keeps the model in step with the spec version: it never waits for plan or implement.
|
|
16
17
|
#
|
|
17
18
|
# Auto-detects first-vs-subsequent from spec.md:
|
|
18
|
-
# • BOOTSTRAP (no spec.md) → author the spec
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
19
|
+
# • BOOTSTRAP (no spec.md) → author the MASTER spec (US-1) + the context's MASTER
|
|
20
|
+
# domain model + init active_version.json. On the context's second feature the model
|
|
21
|
+
# already exists and gains a section rather than being replaced.
|
|
22
|
+
# • DELTA (spec.md exists) → delegate to the delta-spec module: open a change (cr-setup)
|
|
23
|
+
# and author the delta-spec AND that change's delta-domain-model. BOTH masters are
|
|
24
|
+
# left UNTOUCHED — the change stays OPEN; finalize-delta merges the delta into
|
|
25
|
+
# spec.md + domain-model.md (the two documents this phase authors, and the only two
|
|
26
|
+
# it merges), and the change is not closed until close-delta at the end of the chain.
|
|
22
27
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
28
|
+
# MANY CHANGES MAY BE OPEN AT ONCE. Every specify call on an established feature opens
|
|
29
|
+
# its OWN change — the single-active-change guard is gone. Each change's model delta sits
|
|
30
|
+
# BESIDE the context's master model, so every other open delta can see it while being
|
|
31
|
+
# authored, and WAVE orders them: same wave = independent, developable in parallel;
|
|
32
|
+
# a wave-N+1 delta is implemented only after every wave-N delta has landed. cr-setup
|
|
33
|
+
# defaults a new delta to the wave already in flight (parallel by default); `cr-wave`
|
|
34
|
+
# raises one that turns out to build on another.
|
|
25
35
|
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
36
|
+
# ⚠️ finalize-delta and close-delta still resolve "the" change through
|
|
37
|
+
# active_version.json.active_change — the most recently opened. plan, analyze-plan-delta
|
|
38
|
+
# and implement ASK which change and re-activate the answer, so the choice reaches those
|
|
39
|
+
# two; threading an explicit --cr-id through the whole chain is the next step and is not
|
|
40
|
+
# done yet.
|
|
30
41
|
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
+
# Both paths then run ONE converge block — checklist + clarify + validate — over the
|
|
43
|
+
# document THIS invocation authored, never the other one:
|
|
44
|
+
# • BOOTSTRAP → the master spec, graded by the spec checklist.
|
|
45
|
+
# • DELTA → the delta-spec, graded by a DELTA-SHAPED checklist written inside
|
|
46
|
+
# changes/<cr-id>/. The master is not read, not graded, NOT written.
|
|
47
|
+
# The pairing is bound from create-checklist's own output (checklist_file + target →
|
|
48
|
+
# quality_target), so the checklist and the document it grades can never diverge.
|
|
49
|
+
# Why it matters: the delta is authored relative to the master, so a "quality" rewrite
|
|
50
|
+
# of the master mid-change would leave the delta computed against a stale baseline —
|
|
51
|
+
# and in brownfield it would silently edit an adopted, code-derived spec that nothing
|
|
52
|
+
# downstream re-checks for fidelity. Narrow rewrites run include_standards:false.
|
|
53
|
+
# Accuracy of the delta AGAINST the master (id continuity, conflicts) stays where it
|
|
54
|
+
# belongs: /appweaver analyze-plan-delta.
|
|
42
55
|
#
|
|
43
|
-
#
|
|
56
|
+
# Forward paths:
|
|
57
|
+
# NEW : specify → plan → analyze-plan → implement
|
|
58
|
+
# CHANGE : specify (delta-spec + model delta) → plan (delta plans) → analyze-plan-delta →
|
|
59
|
+
# implement → build → analyze-slice-delta → finalize-delta → analyze-slice
|
|
60
|
+
# → close-delta
|
|
61
|
+
#
|
|
62
|
+
# Launch: /appweaver specify [strategy] <what the feature/change does>
|
|
44
63
|
# e.g. /appweaver specify Customers place an order for one or more items
|
|
45
64
|
# Prereq: /appweaver select-feature (sets the active aggregate)
|
|
46
|
-
# Next: /appweaver sync-stories (turns the new/changed requirements into stories)
|
|
47
65
|
# =============================================================================
|
|
48
66
|
|
|
49
67
|
name: aw-spec-specify
|
|
50
|
-
description: Author
|
|
51
|
-
version: 3.
|
|
68
|
+
description: Author the ACTIVE feature's specification AND its bounded context's domain model, together. The aggregate root, its entities, events and invariants (BR-n) live in specs/<Context>/domain-model.md — the one canonical definition every spec in the context cites by id and never restates. First call writes the master spec (US-1) plus the master model; a later call opens its OWN change (many may be open at once) and writes that change's delta-spec and delta-domain-model, leaving both masters untouched for finalize-delta to merge. Each change carries a WAVE — deltas sharing one are independent and developable in parallel, a higher wave is implemented after the lower ones land — and its model delta sits beside the master so every other open delta can see it while being authored. Spec phase only — plan/design is separate.
|
|
69
|
+
version: 3.0.0
|
|
52
70
|
agent_agnostic: true
|
|
53
71
|
dialect: aw-spec.orchestrator/v1
|
|
54
72
|
|
|
55
73
|
inputs:
|
|
56
74
|
user_input:
|
|
57
|
-
description: What the feature
|
|
75
|
+
description: What the feature (first call) or the change (later calls) should do, in natural language.
|
|
58
76
|
required: true
|
|
59
77
|
bind: USER_INPUT
|
|
60
78
|
|
|
@@ -82,44 +100,21 @@ vars:
|
|
|
82
100
|
spec_all_pass: "false" # validation-loop guard, flipped by the validate query
|
|
83
101
|
checklist_total: "0" # bound by checklist-read
|
|
84
102
|
checklist_passed: "0" # stays 0 when the checklist has no items to score
|
|
103
|
+
cr_id: "" # set on the DELTA path only
|
|
104
|
+
cr_dir: "" # set on the DELTA path only — changes/<cr-id>/
|
|
105
|
+
delta_model: "" # set on the DELTA path only — <context>/domain-model.<cr-id>.delta.md
|
|
106
|
+
wave: "" # set on the DELTA path only — the parallel-development level
|
|
107
|
+
open_count: "0" # how many changes are open on this feature after this call
|
|
108
|
+
domain_model: "" # the CONTEXT's canonical model — bound by context-paths
|
|
109
|
+
pending_model_files: [] # every OTHER open delta's model change, for build_context
|
|
110
|
+
pending_count: "0"
|
|
111
|
+
next_wave: "1"
|
|
112
|
+
quality_target: "" # the document the converge loops grade AND rewrite:
|
|
113
|
+
# spec.md (bootstrap) | delta-spec.md (delta). Bound from
|
|
114
|
+
# create-checklist's own output so it always agrees with
|
|
115
|
+
# the checklist. NEVER hand-set to spec_file on the delta
|
|
116
|
+
# path — that is what silently drifts an adopted spec.
|
|
85
117
|
version: "" # current active_version.json version (for the report)
|
|
86
|
-
ledger_ok: "true" # entry tripwire result; the bootstrap path has no ledger to check
|
|
87
|
-
ledger_state: "bootstrap" # clean | unstamped | ledgered_edit | unledgered_edit | rollback | delta_open
|
|
88
|
-
ledger_error: ""
|
|
89
|
-
open_change: "" # a delta change open on this feature — reported, and only
|
|
90
|
-
# blocking once its delta-spec has actually been authored
|
|
91
|
-
snapshot: "" # pre-authoring copy of spec.md; the diff's "before"
|
|
92
|
-
changed_csv: "" # the ids this run actually touched — what the ledger row records
|
|
93
|
-
story_ids_csv: "" # changed, narrowed to FR-/BR- — the only classes a story can cite
|
|
94
|
-
removed_csv: "" # ids this run DELETED — every story citing one is orphaned (E1)
|
|
95
|
-
modified_csv: "" # ids amended in place, as distinct from added or removed
|
|
96
|
-
disturbing_csv: "" # modified ∪ removed — the ids an existing story could feel
|
|
97
|
-
change_count: "0" # 0 on an extend that changed no requirement: no row is written
|
|
98
|
-
ledger_version: "" # the version the row was written at
|
|
99
|
-
covered_csv: "" # ids with OPEN stories, read before authoring as a preference
|
|
100
|
-
covered_note: "" # that preference, WORDED for the case at hand — see ext_covered_*
|
|
101
|
-
disturbed_any: "false" # did this run move a requirement someone is building against
|
|
102
|
-
disturbed_csv: "" # which of them
|
|
103
|
-
disturbed_ids: "" # the same, as a list — the options of the per-requirement ask
|
|
104
|
-
disturbed_stories: "" # the open stories citing those ids
|
|
105
|
-
disturbed_flying: "" # the subset already past story-plan — a follow-on, not an edit
|
|
106
|
-
may_change: "" # the requirements the author approved for amendment
|
|
107
|
-
approved_csv: "" # the same, normalised
|
|
108
|
-
preserve_csv: "" # disturbed minus approved — restored and re-expressed additively
|
|
109
|
-
preserve_count: "0" # 0 = everything was approved, so nothing is re-authored
|
|
110
|
-
marker_count: "0" # [NEEDS CLARIFICATION] markers, counted before the model is asked
|
|
111
|
-
clarifications: [] # what the model made of them; empty when there were none to make
|
|
112
|
-
reissued_csv: "" # ids this run re-used after the ledger had already spent them
|
|
113
|
-
held_csv: "" # the preserved ids the restore actually put back
|
|
114
|
-
violated_csv: "" # the preserved ids it did not — amended without approval
|
|
115
|
-
quality_accept: "" # the author's answer when the spec ships still failing
|
|
116
|
-
readdit_pass: "false" # guard for the restore's own quality loop
|
|
117
|
-
readdit_failing: "" # what that loop found wanting in the requirements it minted
|
|
118
|
-
readdit_verdict: "true" # that loop's verdict, or "nothing was re-authored" when it never ran
|
|
119
|
-
readdit_accept: "" # the author's answer when the re-authored spec ships still failing
|
|
120
|
-
boot_row_version: "" # non-empty once the v1.0 row has actually been written
|
|
121
|
-
stamp_hold: "false" # true = do not re-baseline the hash; the document is unfinished
|
|
122
|
-
uncovered_csv: "" # requirements with no story at all — nobody is building these
|
|
123
118
|
|
|
124
119
|
workflow:
|
|
125
120
|
|
|
@@ -130,14 +125,10 @@ workflow:
|
|
|
130
125
|
script: { ref: actions }
|
|
131
126
|
subcommand: feature-paths
|
|
132
127
|
outputs:
|
|
128
|
+
- { name: feature, from: "$.feature" }
|
|
133
129
|
- { name: feature_dir, from: "$.feature_dir" }
|
|
134
130
|
- { name: spec_file, from: "$.spec_file" }
|
|
135
131
|
- { name: spec_exists, from: "$.spec_exists" }
|
|
136
|
-
# needs_bootstrap = no spec.md, OR a first run that never finished. version-init raises
|
|
137
|
-
# a marker and only ledger-ids lowers it, so a bootstrap that died after writing spec.md
|
|
138
|
-
# is RE-RUN rather than mistaken for a spec to extend (which re-applied the same input
|
|
139
|
-
# over an unfinished v1.0 and bumped it to v1.1).
|
|
140
|
-
- { name: needs_bootstrap, from: "$.needs_bootstrap" }
|
|
141
132
|
|
|
142
133
|
- id: require_feature
|
|
143
134
|
action: verify_context
|
|
@@ -145,6 +136,24 @@ workflow:
|
|
|
145
136
|
on_empty: stop
|
|
146
137
|
on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run specify."
|
|
147
138
|
|
|
139
|
+
# 1b. Resolve the BOUNDED CONTEXT's domain model — the canonical home of the aggregate
|
|
140
|
+
# root, its entities and its invariants, which this spec references and never
|
|
141
|
+
# restates. Also lists every OTHER open delta's model change, so this run authors
|
|
142
|
+
# around what is already in flight instead of colliding with it at merge time.
|
|
143
|
+
- id: context_paths
|
|
144
|
+
action: execute_script
|
|
145
|
+
on_error: continue
|
|
146
|
+
script: { ref: actions }
|
|
147
|
+
subcommand: context-paths
|
|
148
|
+
outputs:
|
|
149
|
+
- { name: context, from: "$.context" }
|
|
150
|
+
- { name: context_dir, from: "$.context_dir" }
|
|
151
|
+
- { name: domain_model, from: "$.domain_model" }
|
|
152
|
+
- { name: domain_model_exists, from: "$.domain_model_exists" }
|
|
153
|
+
- { name: pending_model_files, from: "$.pending_files" }
|
|
154
|
+
- { name: pending_count, from: "$.pending_count" }
|
|
155
|
+
- { name: next_wave, from: "$.next_wave" }
|
|
156
|
+
|
|
148
157
|
# 2. TECHNICAL rule files → sticky standards (configuration, not hardcoded).
|
|
149
158
|
# tech-select reads guardrails/projects/<project>/tech-rule-bindings.md under
|
|
150
159
|
# "## /aw-spec-specify" and loads that set. Per the rule-partition that heading is
|
|
@@ -177,33 +186,42 @@ workflow:
|
|
|
177
186
|
hooks_from_variable: before_hooks
|
|
178
187
|
|
|
179
188
|
# ═══════════════════════ BOOTSTRAP — no spec.md yet ═══════════════════════
|
|
180
|
-
#
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
189
|
+
# Copy the spec template into the (already-existing) feature dir + init version.
|
|
190
|
+
- id: boot_scaffold
|
|
191
|
+
condition: "{{spec_exists}} == false"
|
|
192
|
+
action: execute_script
|
|
193
|
+
script: { ref: actions }
|
|
194
|
+
subcommand: scaffold
|
|
195
|
+
args: ["--feature-dir", "{{feature_dir}}", "--allow-existing"]
|
|
196
|
+
|
|
188
197
|
- id: boot_version_init
|
|
189
|
-
condition: "{{
|
|
198
|
+
condition: "{{spec_exists}} == false"
|
|
190
199
|
action: execute_script
|
|
191
200
|
script: { ref: actions }
|
|
192
201
|
subcommand: version-init
|
|
193
|
-
args: ["--feature-dir", "{{feature_dir}}"
|
|
202
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
194
203
|
|
|
195
|
-
-
|
|
196
|
-
|
|
204
|
+
# Copy the domain-model template into the CONTEXT (not the feature) when the context
|
|
205
|
+
# has no model yet. Idempotent and non-destructive: the second feature in a context
|
|
206
|
+
# finds the model already there and adds its aggregate to it rather than replacing it.
|
|
207
|
+
- id: boot_model_scaffold
|
|
208
|
+
condition: "{{spec_exists}} == false"
|
|
197
209
|
action: execute_script
|
|
198
210
|
script: { ref: actions }
|
|
199
|
-
subcommand: scaffold
|
|
200
|
-
args: ["--feature-dir", "{{feature_dir}}"
|
|
211
|
+
subcommand: model-scaffold
|
|
212
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
213
|
+
outputs:
|
|
214
|
+
- { name: domain_model, from: "$.domain_model" }
|
|
215
|
+
- { name: model_was_created, from: "$.created" }
|
|
201
216
|
|
|
202
217
|
- id: boot_ctx
|
|
203
|
-
condition: "{{
|
|
218
|
+
condition: "{{spec_exists}} == false"
|
|
204
219
|
action: build_context
|
|
205
220
|
context:
|
|
206
221
|
- "{{spec_file}}" # the copied template = section structure
|
|
222
|
+
- "{{domain_model}}" # the CONTEXT's domain model — freshly scaffolded when
|
|
223
|
+
# this is the context's first feature, already populated
|
|
224
|
+
# when it is not. The aggregate root goes HERE.
|
|
207
225
|
- .appweaver/memory/constitution.domain.md # the DOMAIN authority — specify's frame. Declared
|
|
208
226
|
# REQUIRED (bare string, not `optional: true`) ON PURPOSE:
|
|
209
227
|
# specify is the ONLY phase that reads the domain rules and
|
|
@@ -223,38 +241,31 @@ workflow:
|
|
|
223
241
|
# no file and no ux/ rules mapped, the section resolves to
|
|
224
242
|
# "None" and the spec is unchanged from a backend-only one.
|
|
225
243
|
- id: boot_generate
|
|
226
|
-
condition: "{{
|
|
244
|
+
condition: "{{spec_exists}} == false"
|
|
227
245
|
action: agent_author
|
|
228
246
|
output_target: { type: file, path: "{{spec_file}}" }
|
|
229
247
|
task: |
|
|
230
|
-
Author the specification for this aggregate by filling out the
|
|
231
|
-
context. Base everything on:
|
|
248
|
+
Author the MASTER specification for this aggregate by filling out the
|
|
249
|
+
template in context. This is the first user story, US-1. Base everything on:
|
|
232
250
|
|
|
233
251
|
{{USER_INPUT}}
|
|
234
252
|
|
|
235
253
|
Rules for the content you generate:
|
|
236
|
-
- The feature IS the aggregate; set the Aggregate field.
|
|
254
|
+
- The feature IS the aggregate slice; set the Aggregate field and keep US-1's id.
|
|
237
255
|
- Preserve the template's section order and headings.
|
|
238
|
-
- THIS DOCUMENT HOLDS NO USER STORIES. Every operation on the aggregate
|
|
239
|
-
(CreateOrder, UpdateOrder, …) is a REQUIREMENT (FR-n) here — never a user
|
|
240
|
-
story, never an "As a … I want …" block, never a Given/When/Then acceptance
|
|
241
|
-
scenario, and never a section for any of those. A story is an execution slice
|
|
242
|
-
that lives beside this file in stories/ and cites the ids below; it is written
|
|
243
|
-
by /appweaver sync-stories, not here. Design and code trace to the ids in
|
|
244
|
-
this document, which is what lets the backlog churn without disturbing them.
|
|
245
256
|
- SPECIFY IS THE ONLY PHASE THAT READS THE DOMAIN RULES. Design and implement
|
|
246
|
-
work from the spec
|
|
247
|
-
carrier of the domain into the pipeline. Model the domain COMPLETELY
|
|
248
|
-
it is lost downstream.
|
|
249
|
-
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
the
|
|
257
|
+
work from the spec + the domain model, never the domain docs — so this pair is
|
|
258
|
+
the SOLE carrier of the domain into the pipeline. Model the domain COMPLETELY
|
|
259
|
+
across the two, or it is lost downstream.
|
|
260
|
+
- THIS DOCUMENT IS BEHAVIOUR, NOT STRUCTURE. The aggregate boundary, entities,
|
|
261
|
+
value objects, domain events and invariants (BR-n) belong to the CONTEXT'S
|
|
262
|
+
DOMAIN MODEL (in context, authored by the next step) — not here. In the spec's
|
|
263
|
+
"Domain Model Reference" name the aggregate(s) these requirements act on and CITE
|
|
264
|
+
the invariants they rely on by id. Never restate what an aggregate owns or what an
|
|
265
|
+
invariant says: a per-spec copy drifts, and once two specs assert different rules
|
|
266
|
+
for one aggregate nothing arbitrates between them.
|
|
267
|
+
- Use the context's UBIQUITOUS LANGUAGE exactly as the domain model defines it —
|
|
268
|
+
the same terms, no synonyms.
|
|
258
269
|
- Focus on WHAT users need and WHY. Include NO implementation mechanics
|
|
259
270
|
(languages, frameworks, code structure, endpoint/adapter design). Note: an
|
|
260
271
|
external interoperability OBLIGATION — which standard/resources must be met —
|
|
@@ -262,13 +273,10 @@ workflow:
|
|
|
262
273
|
but never how the adapter is built.
|
|
263
274
|
- Write for non-technical business stakeholders.
|
|
264
275
|
- Write System Requirements in EARS as the template shows (THE SYSTEM SHALL
|
|
265
|
-
/ WHEN…/ IF…THEN / WHILE…/ WHERE…) — each atomic and testable.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
- Capture always-true BUSINESS invariants as Business Rules (BR-n), and state
|
|
270
|
-
the aggregate boundary (what it owns vs references by id, and out of scope).
|
|
271
|
-
Record reasonable defaults in Assumptions.
|
|
276
|
+
/ WHEN…/ IF…THEN / WHILE…/ WHERE…) — each atomic and testable.
|
|
277
|
+
- Always-true BUSINESS invariants are BR-n in the DOMAIN MODEL, not here — the next
|
|
278
|
+
step writes them. Cite the ids this spec's requirements rely on. Record reasonable
|
|
279
|
+
defaults in Assumptions.
|
|
272
280
|
- DOMAIN LIFT — when the active domain's rules impose them, lift into the three
|
|
273
281
|
dedicated sections (stable ids; cite the source rule where lifted):
|
|
274
282
|
• Constraints & NFRs (CN-n) — regulatory / retention / immutability /
|
|
@@ -278,7 +286,7 @@ workflow:
|
|
|
278
286
|
MUST NOT appear (public surface, logs, metrics, traces, projections).
|
|
279
287
|
• Integration Contracts (IC-n) — external interop obligations (outbound
|
|
280
288
|
projections, terminology / reference services, inbound feeds) at contract
|
|
281
|
-
level, mapped to the
|
|
289
|
+
level, mapped to the domain model's entities they concern.
|
|
282
290
|
If the domain imposes none, write "None (as of v1.0)" — never delete a section.
|
|
283
291
|
- EXPERIENCE LIFT — the same discipline, a different source and a different lane.
|
|
284
292
|
From constitution.ui.md (the UX-* principle series) and the mapped ux/ rules, lift:
|
|
@@ -287,7 +295,8 @@ workflow:
|
|
|
287
295
|
• Experience Standards (UX-n) — the PROJECT-WIDE standards its UI must satisfy:
|
|
288
296
|
accessibility conformance, interaction-state coverage, content and language,
|
|
289
297
|
journey integrity, responsiveness-as-a-limit.
|
|
290
|
-
Its own id class ON PURPOSE: prototype
|
|
298
|
+
Its own id class ON PURPOSE: the prototype phase and the frontend lanes of plan and
|
|
299
|
+
implement review and verify
|
|
291
300
|
UX- separately from the service lane, and the service-side gates trace CN- and never
|
|
292
301
|
read this section — so a bound the SERVICE must meet stays a CN-, cross-referenced.
|
|
293
302
|
HIGH-LEVEL ONLY, and this is the rule that matters most: every entry is a standard
|
|
@@ -298,279 +307,142 @@ workflow:
|
|
|
298
307
|
standard that governs every such behaviour is a UX-n.
|
|
299
308
|
Where neither a UI constitution nor ux/ rules are in context there is nothing to
|
|
300
309
|
lift: write "None (as of v1.0)" in both — never delete either section.
|
|
301
|
-
- This is v1.0:
|
|
302
|
-
"
|
|
303
|
-
from the finished document, so ids listed here are discarded — the same rule that
|
|
304
|
-
makes every later version row an observation rather than a claim. Leave its Stories cell as "—";
|
|
305
|
-
sync-stories fills that in when it raises the backlog.
|
|
306
|
-
- LEAVE THE "## Story Coverage" BLOCK ALONE, placeholder row and all. Everything
|
|
307
|
-
between the AW:COVERAGE markers is generated from the stories/ directory after
|
|
308
|
-
this step; anything written there by hand is discarded at the next write.
|
|
310
|
+
- This is v1.0: fill the initial Change History row's date and keep US-1's
|
|
311
|
+
"Added: v1.0" marker.
|
|
309
312
|
- ALWAYS include a "## Success Criteria" section with 2-4 measurable,
|
|
310
|
-
technology-agnostic, verifiable SC-nnn outcomes derived from the
|
|
311
|
-
It is MANDATORY — never drop it; when a metric must be inferred,
|
|
312
|
-
reasonable, testable target rather than omitting the section.
|
|
313
|
+
technology-agnostic, verifiable SC-nnn outcomes derived from the acceptance
|
|
314
|
+
criteria. It is MANDATORY — never drop it; when a metric must be inferred,
|
|
315
|
+
state a reasonable, testable target rather than omitting the section.
|
|
313
316
|
- Add at most 3 "[NEEDS CLARIFICATION: <question>]" markers, and only for
|
|
314
317
|
choices that materially affect scope/UX and have no reasonable default.
|
|
315
318
|
- Every section the template marks *(mandatory)* — at minimum System
|
|
316
|
-
Requirements and Success Criteria — plus Business Need
|
|
317
|
-
row MUST be present and populated. The four lift sections
|
|
318
|
-
Data Classification, Integration Contracts, Experience
|
|
319
|
-
Scope MUST also be present — populated when their rules
|
|
320
|
-
"None (as of v1.0)". You may
|
|
321
|
-
(e.g. the WHERE optional-feature
|
|
322
|
-
never a mandatory section, and
|
|
319
|
+
Requirements and Success Criteria — plus Business Need, User Stories, and
|
|
320
|
+
the Change History row MUST be present and populated. The four lift sections
|
|
321
|
+
(Constraints & NFRs, Data Classification, Integration Contracts, Experience
|
|
322
|
+
Standards) and Experience Scope MUST also be present — populated when their rules
|
|
323
|
+
apply, else "None (as of v1.0)". You may
|
|
324
|
+
omit only a genuinely inapplicable OPTIONAL item (e.g. the WHERE optional-feature
|
|
325
|
+
requirement, or an empty Assumptions list) — never a mandatory section, and
|
|
326
|
+
never write "N/A".
|
|
323
327
|
- Do NOT embed any checklist and NO design/implementation.
|
|
324
328
|
|
|
325
|
-
#
|
|
326
|
-
#
|
|
327
|
-
#
|
|
328
|
-
# writing would destroy information rather than add it:
|
|
329
|
-
#
|
|
330
|
-
# unledgered_edit spec.md was hand-edited and no Change History row was added.
|
|
331
|
-
# Appending this run's row would bump the version past that edit
|
|
332
|
-
# without ever naming its ids, so sync-stories could not
|
|
333
|
-
# reconcile it and every story citing it stays silently stale.
|
|
334
|
-
# delta_open a change is open AND its delta-spec has been authored, so a delta
|
|
335
|
-
# is measured against this master. Temporary — the guard retires with
|
|
336
|
-
# the delta family — but while both exist it is the one silent way
|
|
337
|
-
# the two ways of changing a spec collide. Narrowed to an AUTHORED
|
|
338
|
-
# delta because cr-setup scaffolds a delta-spec for every change it
|
|
339
|
-
# opens: gating on the open flag alone refused every extend for the
|
|
340
|
-
# whole life of a DESIGN-only change, which strands nothing, and the
|
|
341
|
-
# refusal named a chain the operator may be retiring. The message now
|
|
342
|
-
# also names the way OUT (cr-archive) rather than only the way through.
|
|
329
|
+
# The CONTEXT's domain model, authored in the SAME run as the master spec so the model
|
|
330
|
+
# is never behind the spec that depends on it. Writes the master directly — on the
|
|
331
|
+
# bootstrap path no change is open, so there is nothing to merge later.
|
|
343
332
|
#
|
|
344
|
-
#
|
|
345
|
-
#
|
|
346
|
-
#
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
# the gate's vars to nothing and produced the refusal message with its reason blanked out
|
|
350
|
-
# ("cannot write spec.md: (State: .)"), which is the right decision told unreadably.
|
|
351
|
-
- id: ext_ledger_check
|
|
352
|
-
condition: "{{needs_bootstrap}} == false"
|
|
353
|
-
action: execute_script
|
|
354
|
-
script: { ref: actions }
|
|
355
|
-
subcommand: spec-check
|
|
356
|
-
args: ["--feature-dir", "{{feature_dir}}"]
|
|
357
|
-
outputs:
|
|
358
|
-
- { name: ledger_ok, from: "$.ok" }
|
|
359
|
-
- { name: ledger_state, from: "$.state" }
|
|
360
|
-
- { name: ledger_error, from: "$.error" }
|
|
361
|
-
- { name: open_change, from: "$.open_change" }
|
|
362
|
-
- { name: version, from: "$.version" }
|
|
363
|
-
|
|
364
|
-
- id: ext_ledger_gate
|
|
365
|
-
condition: "{{needs_bootstrap}} == false"
|
|
366
|
-
action: verify_context
|
|
367
|
-
require_true: ledger_ok
|
|
368
|
-
on_false: stop
|
|
369
|
-
on_false_message: >
|
|
370
|
-
/appweaver specify cannot write spec.md: {{ledger_error}}
|
|
371
|
-
The Change History is how a story learns that a requirement it cites has moved —
|
|
372
|
-
a version that does not name the ids it changed leaves every affected story
|
|
373
|
-
stale with nothing to detect it. Resolve the state above, then re-run specify.
|
|
374
|
-
(State: {{ledger_state}}.)
|
|
375
|
-
|
|
376
|
-
# 6. Snapshot the document as it stands. This is the diff's "before", and it is what
|
|
377
|
-
# lets the Change History row be computed from what the run actually did rather than
|
|
378
|
-
# from what it claimed. Taken BEFORE authoring and consumed AFTER the converge loop,
|
|
379
|
-
# so a requirement the quality pass rewrote in passing is still recorded.
|
|
380
|
-
- id: ext_snapshot
|
|
381
|
-
condition: "{{needs_bootstrap}} == false"
|
|
382
|
-
action: execute_script
|
|
383
|
-
script: { ref: actions }
|
|
384
|
-
subcommand: spec-snapshot
|
|
385
|
-
args: ["--feature-dir", "{{feature_dir}}"]
|
|
386
|
-
outputs:
|
|
387
|
-
- { name: snapshot, from: "$.snapshot" }
|
|
388
|
-
|
|
389
|
-
# 6b. WHO IS BUILDING WHAT. A read-only scan of stories/ for the requirements that have
|
|
390
|
-
# open stories against them. specify writes no stories, but it is about to change a
|
|
391
|
-
# contract other people may already be building against, and knowing which ids those
|
|
392
|
-
# are is the difference between an amendment that costs a re-sync and one that costs
|
|
393
|
-
# nothing. Fed to the author as a preference, then checked against the diff below —
|
|
394
|
-
# the preference is a nudge, the check after the fact is the detector.
|
|
395
|
-
# Empty until the backlog exists, which degrades to "no preference, no ask".
|
|
396
|
-
# CARRIED, NOT COVERED. `covered_csv` is the DELIVERY relation — the FR-/BR- ids open
|
|
397
|
-
# stories are delivering — and a story also CARRIES the bounds it is built to satisfy.
|
|
398
|
-
# Amending or removing a CN-/DC-/IC-/UX- moves ground somebody is standing on exactly
|
|
399
|
-
# as amending an FR- does, and reading the narrower set here meant it raised no ask at
|
|
400
|
-
# all and printed "nobody has open work" while stories were being built against it.
|
|
401
|
-
# `carried_csv` is exercises: ∪ the `constraints:` frontmatter story-stamp lifts.
|
|
402
|
-
- id: ext_story_context
|
|
403
|
-
condition: "{{needs_bootstrap}} == false"
|
|
404
|
-
action: execute_script
|
|
405
|
-
on_error: continue
|
|
406
|
-
script: { ref: actions }
|
|
407
|
-
subcommand: story-refs
|
|
408
|
-
args: ["--feature-dir", "{{feature_dir}}"]
|
|
409
|
-
outputs:
|
|
410
|
-
- { name: covered_csv, from: "$.covered_csv" }
|
|
411
|
-
- { name: carried_csv, from: "$.carried_csv" }
|
|
412
|
-
- { name: backlog_scanned, from: "$.ok" }
|
|
413
|
-
|
|
414
|
-
# 6b-bis. WORD THE PREFERENCE FOR THE CASE AT HAND. The rule below is an argument for
|
|
415
|
-
# preferring an ADDITION over an amendment, and it only makes sense while somebody is
|
|
416
|
-
# actually building. Interpolating the carried set straight into it left a spec with no
|
|
417
|
-
# backlog reading an eight-line case for adding requirements under a blank list — the
|
|
418
|
-
# logic was vacuous ("one of those" names nothing) but the rhetoric was not, and the
|
|
419
|
-
# first extend of a spec nobody has stories against is exactly where amending in place
|
|
420
|
-
# is the honest thing to do. THREE ORDERED SETS rather than a compound condition, the
|
|
421
|
-
# same idiom as readdit_verdict_na/_real: the no-backlog wording is the default, the
|
|
422
|
-
# preference replaces it where there is open work to protect, and the scan-failed
|
|
423
|
-
# wording is last so it wins over both.
|
|
424
|
-
# Safe on the bootstrap path too — ext_story_context is skipped there, so nothing is
|
|
425
|
-
# bound and only the default can fire.
|
|
426
|
-
- id: ext_covered_none
|
|
427
|
-
action: set
|
|
428
|
-
vars:
|
|
429
|
-
covered_note: >
|
|
430
|
-
NOBODY HAS OPEN WORK AGAINST ANY REQUIREMENT IN THIS SPEC. No story cites any id in
|
|
431
|
-
this document, so nothing you do here can strand work already under way. Amend an
|
|
432
|
-
existing requirement where the change genuinely belongs to it, and add a new one
|
|
433
|
-
where the change is genuinely new — let the contract read the way it should read,
|
|
434
|
-
without inventing a companion requirement to avoid touching anything.
|
|
435
|
-
|
|
436
|
-
- id: ext_covered_some
|
|
437
|
-
condition: "{{carried_csv}} != ''"
|
|
438
|
-
action: set
|
|
439
|
-
vars:
|
|
440
|
-
covered_note: >
|
|
441
|
-
THESE REQUIREMENTS AND BOUNDS ARE BEING BUILT AGAINST RIGHT NOW: {{carried_csv}}.
|
|
442
|
-
(FR-/BR- are what open stories DELIVER; CN-/DC-/IC-/UX- are what they are built to
|
|
443
|
-
MEET — moving either moves ground someone is standing on.) Where the change can
|
|
444
|
-
be expressed EITHER as an amendment to one of those or as a new requirement
|
|
445
|
-
alongside it, prefer the new requirement — an amendment moves ground someone is
|
|
446
|
-
standing on, and a requirement that gains a companion leaves their work correct as
|
|
447
|
-
written. This is a preference, not a prohibition: when the change genuinely
|
|
448
|
-
contradicts what one of them says, amend it. A contract that states two
|
|
449
|
-
incompatible things to avoid disturbing someone is worse than the disturbance.
|
|
450
|
-
Requirements NOT in that list have no open work against them: amend those in place
|
|
451
|
-
wherever the change genuinely belongs to them.
|
|
452
|
-
|
|
453
|
-
# THE SCAN ITSELF CAN FAIL, and ext_story_context carries `on_error: continue` because a
|
|
454
|
-
# spec must stay editable when its backlog cannot be read. What it must not do is answer
|
|
455
|
-
# as though the scan had run and found nothing: nothing binds, ext_covered_some cannot
|
|
456
|
-
# fire, and the unconditional default above then tells the author NOBODY HAS OPEN WORK —
|
|
457
|
-
# the strongest claim available, on no evidence at all.
|
|
458
|
-
- id: ext_covered_unknown
|
|
459
|
-
condition: "{{backlog_scanned}} != true"
|
|
460
|
-
action: set
|
|
461
|
-
vars:
|
|
462
|
-
covered_note: >
|
|
463
|
-
THE BACKLOG COULD NOT BE READ THIS RUN, so this run does not know who is building
|
|
464
|
-
what. Treat every requirement as possibly under way: prefer adding a requirement to
|
|
465
|
-
amending one, and where you must amend, say in the Change History row exactly what
|
|
466
|
-
moved so the next sync can find every story that carries it.
|
|
467
|
-
|
|
468
|
-
- id: ext_ctx
|
|
469
|
-
condition: "{{needs_bootstrap}} == false"
|
|
333
|
+
# NOT gated on a backend lane. The domain model is ubiquitous language and invariants,
|
|
334
|
+
# which a frontend-only project has as much as any other; where an invariant is
|
|
335
|
+
# ENFORCED is a plan concern and is recorded in backend-plan.md.
|
|
336
|
+
- id: boot_model_ctx
|
|
337
|
+
condition: "{{spec_exists}} == false"
|
|
470
338
|
action: build_context
|
|
471
339
|
context:
|
|
472
|
-
- "{{
|
|
473
|
-
-
|
|
340
|
+
- "{{domain_model}}" # the model (template on a first feature, populated on a later one)
|
|
341
|
+
- "{{spec_file}}" # the spec just authored — the behaviour the model must support
|
|
342
|
+
- .appweaver/memory/constitution.domain.md
|
|
474
343
|
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
475
344
|
|
|
476
|
-
- id:
|
|
477
|
-
condition: "{{
|
|
345
|
+
- id: boot_model_generate
|
|
346
|
+
condition: "{{spec_exists}} == false"
|
|
478
347
|
action: agent_author
|
|
479
|
-
output_target: { type: file, path: "{{
|
|
348
|
+
output_target: { type: file, path: "{{domain_model}}" }
|
|
480
349
|
task: |
|
|
481
|
-
|
|
482
|
-
|
|
350
|
+
Author the DOMAIN MODEL for this bounded context — the canonical structure the spec
|
|
351
|
+
you just wrote (in context) references. Base it on that spec and on:
|
|
483
352
|
|
|
484
353
|
{{USER_INPUT}}
|
|
485
354
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
as a
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
#
|
|
518
|
-
#
|
|
519
|
-
#
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
355
|
+
- THE AGGREGATE ROOT LIVES HERE, and nowhere else. Fill one `## Aggregate: <Name>`
|
|
356
|
+
section per root: its boundary (owns / references by id / out of scope), entities,
|
|
357
|
+
value objects, domain events, and its invariants as stable BR-nnn ids. The spec
|
|
358
|
+
cites those ids; this document defines them.
|
|
359
|
+
- IF THE MODEL ALREADY HAS CONTENT, this is the context's SECOND (or later) feature.
|
|
360
|
+
ADD this feature's aggregate as a new section and EXTEND the ubiquitous language.
|
|
361
|
+
Do NOT rewrite, renumber or "tidy" the aggregates already there — other specs cite
|
|
362
|
+
their ids, and a renumbered BR breaks every citation silently. Add `{{feature}}` to
|
|
363
|
+
the `Serving specs` line of any existing aggregate this feature also acts on.
|
|
364
|
+
- DDD IS THE RATIFIED MODELLING STANDARD (NON-NEGOTIABLE → the modelling-methodology
|
|
365
|
+
standard in the sticky standards, wired per-project via tech-rule-bindings
|
|
366
|
+
"## /aw-spec-specify", plus the domain's own DDD overlay): a consistent ubiquitous
|
|
367
|
+
language; the aggregate and its root; entities vs value objects by IDENTITY;
|
|
368
|
+
invariants owned by the aggregate; cross-aggregate references BY ID ONLY, never a
|
|
369
|
+
navigable link.
|
|
370
|
+
- TECHNOLOGY-AGNOSTIC. No types, no persistence, no framework, no layering. WHERE an
|
|
371
|
+
invariant is enforced is a plan concern and belongs in backend-plan.md.
|
|
372
|
+
- Fill "## Cross-Aggregate Invariants" (XBR-n) with rules spanning two or more
|
|
373
|
+
aggregates — they belong to no single spec, which is why they can only live here.
|
|
374
|
+
"None (as of v1.0)" when there are none; never delete the section.
|
|
375
|
+
- Fill "## Context Map" with this context's relationships to others, or
|
|
376
|
+
"None (as of v1.0)" while it is the only one. Never delete the section.
|
|
377
|
+
- Do NOT write the Context version or the Change History version column — scripts
|
|
378
|
+
stamp those. Fill the initial row's date and aggregate only.
|
|
379
|
+
|
|
380
|
+
# ═════════════════════════ DELTA — spec.md exists ═════════════════════════
|
|
381
|
+
# A later requirement = a new user story on the SAME aggregate. Delegate to the
|
|
382
|
+
# delta-spec module: it opens the change (cr-setup, single-active-change guarded) and
|
|
383
|
+
# authors the delta-spec ONLY — the master spec.md is left UNTOUCHED. The change
|
|
384
|
+
# stays OPEN; finalize-delta merges the delta into spec.md later in the chain. Runs in
|
|
385
|
+
# this context (shares vars + the DDD + domain sticky standards). The former standalone
|
|
386
|
+
# `cr` capture's spec half now lives here. Downstream: plan (the delta plans) →
|
|
387
|
+
# analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice →
|
|
388
|
+
# close-delta (close-delta is what finally archives the change and bumps the version).
|
|
389
|
+
- id: delta_spec_module
|
|
390
|
+
condition: "{{spec_exists}} == true"
|
|
391
|
+
action: include
|
|
392
|
+
module: .appweaver/orchestrator/modules/delta-spec.orch.yaml
|
|
393
|
+
|
|
394
|
+
# ══════ CONVERGE — checklist + clarify + validate, ON THE DOCUMENT JUST WRITTEN ══════
|
|
395
|
+
# These loops run on WHICHEVER document this invocation authored — the master spec on
|
|
396
|
+
# the bootstrap path, the delta-spec on the change path. They must NEVER write the
|
|
397
|
+
# master while a change is open: the delta is authored relative to the master, so
|
|
398
|
+
# mutating the master here would leave the delta computed against a stale baseline
|
|
399
|
+
# AND (brownfield) silently edit an adopted, code-derived spec that no downstream
|
|
400
|
+
# stage re-checks for fidelity. Hence quality_target — bound from the SAME script call
|
|
401
|
+
# that creates the checklist, so the checklist and the graded document cannot diverge.
|
|
523
402
|
#
|
|
524
|
-
#
|
|
525
|
-
#
|
|
526
|
-
#
|
|
527
|
-
# domain rules on every iteration. The converge steps each declare what they read, so
|
|
528
|
-
# emptying the envelope here costs nothing; the two that genuinely judge the domain
|
|
529
|
-
# lift (validate and its patch) name the constitutions themselves.
|
|
530
|
-
- id: converge_ctx
|
|
531
|
-
action: build_context
|
|
532
|
-
context: []
|
|
533
|
-
|
|
534
|
-
# 7. Quality checklist (shared script — static content, NO model). Idempotent in content.
|
|
403
|
+
# 5. Quality checklist (shared script — static content, NO model). Idempotent in
|
|
404
|
+
# content; note it rewrites the file, so it is deliberately scoped to the path that
|
|
405
|
+
# authored the document (a delta run no longer resets the master's scored boxes).
|
|
535
406
|
- id: create_checklist
|
|
407
|
+
condition: "{{spec_exists}} == false"
|
|
536
408
|
action: execute_script
|
|
537
409
|
script: { ref: actions }
|
|
538
410
|
subcommand: create-checklist
|
|
539
411
|
args: ["--feature-dir", "{{feature_dir}}"]
|
|
540
412
|
outputs:
|
|
541
|
-
- { name: checklist_file,
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
#
|
|
545
|
-
#
|
|
546
|
-
#
|
|
547
|
-
#
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
- id: spec_markers
|
|
413
|
+
- { name: checklist_file, from: "$.checklist_file" }
|
|
414
|
+
- { name: quality_target, from: "$.target" } # → spec.md
|
|
415
|
+
|
|
416
|
+
# Delta twin: a DELTA-SHAPED checklist (ADDED/MODIFIED/REMOVED completeness, merge
|
|
417
|
+
# readiness) written INSIDE changes/<cr-id>/, grading the delta-spec. The master
|
|
418
|
+
# checklist's items assume a full spec and would mis-fire here, which is exactly why
|
|
419
|
+
# this is a separate checklist rather than the same one re-pointed.
|
|
420
|
+
- id: create_delta_checklist
|
|
421
|
+
condition: "{{spec_exists}} == true"
|
|
551
422
|
action: execute_script
|
|
552
423
|
script: { ref: actions }
|
|
553
|
-
subcommand:
|
|
554
|
-
args: ["--feature-dir", "{{feature_dir}}"]
|
|
424
|
+
subcommand: create-checklist
|
|
425
|
+
args: ["--feature-dir", "{{feature_dir}}", "--kind", "delta", "--change-dir", "{{cr_dir}}"]
|
|
555
426
|
outputs:
|
|
556
|
-
- { name:
|
|
427
|
+
- { name: checklist_file, from: "$.checklist_file" }
|
|
428
|
+
- { name: quality_target, from: "$.target" } # → changes/<cr-id>/delta-spec.md
|
|
557
429
|
|
|
430
|
+
# 6. Resolve [NEEDS CLARIFICATION] markers interactively (≤3). Non-interactive
|
|
431
|
+
# runs answer with the first/recommended option (a documented default).
|
|
558
432
|
- id: extract_clarifications
|
|
559
|
-
condition: "{{marker_count}} != 0"
|
|
560
433
|
action: agent_query
|
|
561
|
-
context: ["{{
|
|
434
|
+
context: ["{{quality_target}}"]
|
|
562
435
|
include_standards: false
|
|
563
436
|
task: |
|
|
564
|
-
Extract up to 3 unresolved "[NEEDS CLARIFICATION: <question>]" markers from
|
|
565
|
-
|
|
566
|
-
{"clarifications":[]} if there are none.
|
|
437
|
+
Extract up to 3 unresolved "[NEEDS CLARIFICATION: <question>]" markers from
|
|
438
|
+
the document in context (a feature spec, or one change's delta-spec), most
|
|
439
|
+
scope/security/UX-impacting first. Return {"clarifications":[]} if there are none.
|
|
567
440
|
schema: { clarifications: [ { id: c1, question: "<the question>", options: ["<answer A>", "<answer B>", "<answer C>"] } ] }
|
|
568
441
|
mock: { clarifications: [] }
|
|
569
442
|
outputs:
|
|
570
443
|
- { name: clarifications, from: "$.clarifications" }
|
|
571
444
|
|
|
572
445
|
- id: clarify_loop
|
|
573
|
-
condition: "{{marker_count}} != 0"
|
|
574
446
|
action: loop
|
|
575
447
|
over: "{{clarifications}}"
|
|
576
448
|
as: c
|
|
@@ -584,18 +456,18 @@ workflow:
|
|
|
584
456
|
answer_var: c_answer
|
|
585
457
|
- id: resolve_clar
|
|
586
458
|
action: agent_author
|
|
587
|
-
context: ["{{
|
|
588
|
-
include_standards: false # rewrite-in-place: the
|
|
459
|
+
context: ["{{quality_target}}"]
|
|
460
|
+
include_standards: false # rewrite-in-place: the graded document only
|
|
589
461
|
task: |
|
|
590
|
-
Replace the matching "[NEEDS CLARIFICATION]" marker in the
|
|
591
|
-
with this answer and update the affected requirement/section
|
|
592
|
-
Preserve structure
|
|
593
|
-
|
|
462
|
+
Replace the matching "[NEEDS CLARIFICATION]" marker in the document in
|
|
463
|
+
context with this answer and update the affected requirement/section
|
|
464
|
+
accordingly. Preserve structure and headings. If it is a delta-spec, stay
|
|
465
|
+
within the delta — keep every item's ADDED / MODIFIED / REMOVED placement.
|
|
594
466
|
Question: {{c.question}}
|
|
595
467
|
Answer: {{c_answer}}
|
|
596
|
-
output_target: { type: file, path: "{{
|
|
468
|
+
output_target: { type: file, path: "{{quality_target}}" }
|
|
597
469
|
|
|
598
|
-
#
|
|
470
|
+
# 7. Spec-quality validation loop — evaluate the spec against the checklist,
|
|
599
471
|
# fix failing items, re-check (max 3), then score the checklist file.
|
|
600
472
|
- id: checklist_read
|
|
601
473
|
action: execute_script
|
|
@@ -612,22 +484,15 @@ workflow:
|
|
|
612
484
|
steps:
|
|
613
485
|
- id: validate
|
|
614
486
|
action: agent_query
|
|
615
|
-
|
|
616
|
-
# block: the Domain Lift and Experience Lift items ask whether the spec carries what
|
|
617
|
-
# those files impose, and that is not judgeable from the spec alone. Optional, not
|
|
618
|
-
# required — ext_ctx/boot_ctx already announce a missing domain constitution once,
|
|
619
|
-
# and repeating that announcement per iteration is noise, not signal.
|
|
620
|
-
context:
|
|
621
|
-
- "{{checklist_file}}"
|
|
622
|
-
- "{{spec_file}}"
|
|
623
|
-
- { path: .appweaver/memory/constitution.domain.md, optional: true }
|
|
624
|
-
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
487
|
+
context: ["{{checklist_file}}", "{{quality_target}}"]
|
|
625
488
|
include_standards: false
|
|
626
489
|
task: |
|
|
627
|
-
Evaluate the
|
|
628
|
-
context, 1-based in order).
|
|
629
|
-
|
|
630
|
-
|
|
490
|
+
Evaluate the document under review (in context) against each checklist item
|
|
491
|
+
(in context, 1-based in order). The checklist's own title states which kind
|
|
492
|
+
of document it grades — a feature spec, or one change's delta-spec; judge it
|
|
493
|
+
as that kind and nothing more. An item passes when the document satisfies it.
|
|
494
|
+
(An item about remaining [NEEDS CLARIFICATION] markers passes once none
|
|
495
|
+
remain.) In "failing", quote the offending/absent part per failed item.
|
|
631
496
|
schema: { all_pass: false, results: { "1": true, "2": false }, failing: [ { n: 2, issue: "<quote>" } ] }
|
|
632
497
|
mock: { all_pass: true, results: {}, failing: [] }
|
|
633
498
|
outputs:
|
|
@@ -637,612 +502,41 @@ workflow:
|
|
|
637
502
|
- id: pass_break
|
|
638
503
|
action: break
|
|
639
504
|
condition: "{{spec_all_pass}} == true"
|
|
640
|
-
# Only while another pass remains. The loop is validate -> break-if-pass -> patch, so
|
|
641
|
-
# hitting max_iterations always landed on a PATCH nobody re-read: the document shipped
|
|
642
|
-
# with ungraded text and score_checklist wrote the pre-patch verdict into the checklist
|
|
643
|
-
# file. Skipping the last patch costs one model call less and makes the last thing that
|
|
644
|
-
# happened to the document a grading.
|
|
645
505
|
- id: patch_spec
|
|
646
|
-
condition: "{{iteration}} != 2"
|
|
647
506
|
action: agent_author
|
|
648
|
-
context:
|
|
649
|
-
- "{{spec_file}}"
|
|
650
|
-
- { path: .appweaver/memory/constitution.domain.md, optional: true }
|
|
651
|
-
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
507
|
+
context: ["{{quality_target}}"]
|
|
652
508
|
include_standards: false
|
|
653
509
|
task: |
|
|
654
|
-
Fix these quality failures in the
|
|
655
|
-
order
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
510
|
+
Fix these quality failures in the document in context, preserving its
|
|
511
|
+
section order and headings. Do NOT add implementation detail; keep it
|
|
512
|
+
business-facing and testable.
|
|
513
|
+
If the document is a DELTA-SPEC: it describes one change, not the whole
|
|
514
|
+
feature. Fix it AS a delta — keep the ADDED / MODIFIED / REMOVED structure,
|
|
515
|
+
never expand it into a full specification, never copy in unchanged master
|
|
516
|
+
content to satisfy an item, and never edit the master spec.
|
|
659
517
|
Failures: {{spec_failing}}
|
|
660
|
-
output_target: { type: file, path: "{{
|
|
518
|
+
output_target: { type: file, path: "{{quality_target}}" }
|
|
661
519
|
|
|
662
520
|
- id: score_checklist
|
|
663
521
|
action: execute_script
|
|
664
522
|
condition: "{{checklist_total}} != 0"
|
|
665
523
|
script: { ref: actions }
|
|
666
524
|
subcommand: checklist-apply
|
|
667
|
-
|
|
668
|
-
# with a partial map or none at all, which scored a passing spec as "0/25 items passing".
|
|
669
|
-
args: ["--file", "{{checklist_file}}", "--results", "{{spec_results}}", "--all-pass", "{{spec_all_pass}}"]
|
|
525
|
+
args: ["--file", "{{checklist_file}}", "--results", "{{spec_results}}"]
|
|
670
526
|
outputs:
|
|
671
527
|
- { name: checklist_passed, from: "$.after_checked" }
|
|
672
528
|
|
|
673
|
-
#
|
|
674
|
-
|
|
675
|
-
# on as if it had passed — appending a version row for a spec the run itself judged red,
|
|
676
|
-
# with a checklist score beside it that need not agree.
|
|
677
|
-
#
|
|
678
|
-
# Asked, not decided, and RECORD IS FIRST on purpose. spec.md is already written by this
|
|
679
|
-
# point: a run that refuses the ledger row leaves a document with no row, which is the
|
|
680
|
-
# `unledgered_edit` state the next run refuses outright. Recording a red spec is honest
|
|
681
|
-
# and recoverable — the row names what moved, so sync-stories still works, and the
|
|
682
|
-
# failures are in the checklist file. Stopping is the safer-feeling option that costs
|
|
683
|
-
# more, so the prompt says what it costs and how to undo it.
|
|
684
|
-
- id: quality_ask
|
|
685
|
-
condition: "{{spec_all_pass}} != true"
|
|
686
|
-
action: ask
|
|
687
|
-
prompt: |
|
|
688
|
-
The spec still fails checklist items after the quality loop gave up
|
|
689
|
-
({{checklist_passed}}/{{checklist_total}} passing). The failures are listed in
|
|
690
|
-
{{checklist_file}}.
|
|
691
|
-
Outstanding: {{spec_failing}}
|
|
692
|
-
spec.md is ALREADY WRITTEN either way — the only question is whether this run records
|
|
693
|
-
what it changed.
|
|
694
|
-
options:
|
|
695
|
-
- "Record it — the version row is what keeps the stories in step; fix the spec next"
|
|
696
|
-
- "Stop — write no version row, and repair spec.md by hand first"
|
|
697
|
-
answer_key: quality_accept
|
|
698
|
-
answer_var: quality_accept
|
|
699
|
-
|
|
700
|
-
- id: quality_gate
|
|
701
|
-
condition: "{{spec_all_pass}} != true"
|
|
702
|
-
action: verify_context
|
|
703
|
-
require_value: quality_accept
|
|
704
|
-
equals: "Record it — the version row is what keeps the stories in step; fix the spec next"
|
|
705
|
-
on_mismatch: stop
|
|
706
|
-
on_mismatch_message: >
|
|
707
|
-
Stopped at your request with no version row written. The quality failures are listed in
|
|
708
|
-
{{checklist_file}}.
|
|
709
|
-
On a FIRST spec nothing else is needed: the bootstrap is still marked unfinished, so the
|
|
710
|
-
next specify re-authors it rather than extending it.
|
|
711
|
-
On an EXISTING spec, spec.md HAS been rewritten and the next specify will refuse it as an
|
|
712
|
-
unledgered edit until one of two things is true — add a Change History row naming the ids
|
|
713
|
-
you changed and re-run spec-stamp, or revert the document from the copy taken before this
|
|
714
|
-
run, which is beside it at {{snapshot}}.
|
|
715
|
-
|
|
716
|
-
# ═══════════════ THE FIRST LEDGER ROW — bootstrap only ═════════════════════
|
|
717
|
-
# 9b. The v1.0 row, OBSERVED. Every other version row is computed from a before/after
|
|
718
|
-
# diff for the reason this file opens with: a row the model DECLARED can omit an id it
|
|
719
|
-
# moved, and an omitted id is silent. v1.0 was the one exception — written by the
|
|
720
|
-
# author from a template whose placeholder reads "FR-001…FR-00n", so nothing
|
|
721
|
-
# downstream could trust it and sync-stories had to be told to enumerate from the
|
|
722
|
-
# document instead. Diffing against no snapshot yields every id the finished spec
|
|
723
|
-
# carries, which is exactly the v1.0 row's content, so the first row is now derived
|
|
724
|
-
# the same way as the rest and the special case downstream goes away.
|
|
725
|
-
#
|
|
726
|
-
# on_error: continue — spec.md is already written, and a spec so empty that the diff
|
|
727
|
-
# found no id at all must not be stranded on disk with no ledger row. It keeps what
|
|
728
|
-
# the author wrote and says so in the run log.
|
|
729
|
-
- id: boot_diff
|
|
730
|
-
condition: "{{needs_bootstrap}} == true"
|
|
731
|
-
action: execute_script
|
|
732
|
-
script: { ref: actions }
|
|
733
|
-
subcommand: spec-diff
|
|
734
|
-
args: ["--feature-dir", "{{feature_dir}}", "--snapshot", ""]
|
|
735
|
-
outputs:
|
|
736
|
-
- { name: changed_csv, from: "$.changed_csv" }
|
|
737
|
-
- { name: story_ids_csv, from: "$.story_ids_csv" }
|
|
738
|
-
- { name: change_count, from: "$.change_count" }
|
|
739
|
-
|
|
740
|
-
# The hold is the other half of that on_error. spec-stamp runs on every path and
|
|
741
|
-
# re-baselines the hash, so a soft-failed ledger-ids used to leave a first draft with
|
|
742
|
-
# NO version row and a hash saying the document was in step — the next run's tripwire
|
|
743
|
-
# reads `clean` and every tool that trusts the hash believes the spec is finished.
|
|
744
|
-
# Held here and released only by a row that actually landed, so a bootstrap that did
|
|
745
|
-
# not complete stays unstamped AND still marked pending: an honest re-author.
|
|
746
|
-
- id: boot_stamp_hold
|
|
747
|
-
condition: "{{needs_bootstrap}} == true"
|
|
748
|
-
action: set
|
|
749
|
-
vars: { stamp_hold: "true", version: "v1.0 (draft — no version row yet)" }
|
|
750
|
-
|
|
751
|
-
- id: boot_ledger_ids
|
|
752
|
-
condition: "{{needs_bootstrap}} == true"
|
|
753
|
-
action: execute_script
|
|
754
|
-
on_error: continue
|
|
755
|
-
script: { ref: actions }
|
|
756
|
-
subcommand: ledger-ids
|
|
757
|
-
args: ["--feature-dir", "{{feature_dir}}", "--ids", "{{changed_csv}}"]
|
|
758
|
-
outputs:
|
|
759
|
-
- { name: boot_row_version, from: "$.version" }
|
|
760
|
-
|
|
761
|
-
- id: boot_stamp_release
|
|
762
|
-
condition: "{{boot_row_version}} != ''"
|
|
763
|
-
action: set
|
|
764
|
-
vars: { stamp_hold: "false" }
|
|
765
|
-
|
|
766
|
-
# ═════════════════════════ THE LEDGER ROW — extend only ════════════════════
|
|
767
|
-
# 10. What changed, observed. Diffing the finished document against the snapshot taken
|
|
768
|
-
# before authoring gives the requirement ids this run added, amended or removed —
|
|
769
|
-
# including any the converge loop touched on its way past, which is the whole
|
|
770
|
-
# reason the diff is taken here and not before the loop.
|
|
771
|
-
- id: ext_diff
|
|
772
|
-
condition: "{{needs_bootstrap}} == false"
|
|
773
|
-
action: execute_script
|
|
774
|
-
script: { ref: actions }
|
|
775
|
-
subcommand: spec-diff
|
|
776
|
-
args: ["--feature-dir", "{{feature_dir}}", "--snapshot", "{{snapshot}}"]
|
|
777
|
-
outputs:
|
|
778
|
-
- { name: changed_csv, from: "$.changed_csv" }
|
|
779
|
-
- { name: story_ids_csv, from: "$.story_ids_csv" }
|
|
780
|
-
- { name: removed_csv, from: "$.removed_csv" }
|
|
781
|
-
- { name: modified_csv, from: "$.modified_csv" }
|
|
782
|
-
- { name: disturbing_csv, from: "$.disturbing_csv" }
|
|
783
|
-
- { name: reissued_csv, from: "$.reissued_csv" }
|
|
784
|
-
- { name: change_count, from: "$.change_count" }
|
|
785
|
-
- { name: diff_warnings, from: "$.warnings_note" }
|
|
786
|
-
|
|
787
|
-
# 10a-bis. IDS THE LEDGER CANNOT KEY TO A DEFINITION. The diff keys an id to the line that
|
|
788
|
-
# DEFINES it and hashes the block under it; three authoring forms fall outside that,
|
|
789
|
-
# and each one is silent — the requirement moves, change_count stays 0, no row is
|
|
790
|
-
# appended, and the stamp below re-baselines the new body as clean. A standing
|
|
791
|
-
# property of the document rather than a verdict on this run, so it is reported every
|
|
792
|
-
# time until the requirement is rewritten in the shape the template uses.
|
|
793
|
-
- id: diff_warning_notice
|
|
794
|
-
condition: "{{diff_warnings}} != ''"
|
|
795
|
-
action: verify_context
|
|
796
|
-
require_value: diff_warnings
|
|
797
|
-
equals: ""
|
|
798
|
-
on_mismatch: continue
|
|
799
|
-
on_mismatch_message: >
|
|
800
|
-
THE LEDGER CANNOT SEE PART OF THIS SPEC. {{spec_file}} states requirements in a form
|
|
801
|
-
the version diff cannot key to a definition, so an edit to one of them raises no
|
|
802
|
-
Change History row and no story citing it is ever told: {{diff_warnings}}. Rewrite them
|
|
803
|
-
in the template's shape — `- **FR-001**: …`, defined ABOVE the Change History table,
|
|
804
|
-
with any continuation on the lines directly under the id and no blank line between.
|
|
805
|
-
|
|
806
|
-
# 10b. DID THIS MOVE GROUND SOMEONE IS STANDING ON? The diff is fact, so this is the
|
|
807
|
-
# detector the pre-authoring preference lacked: of the requirements this run
|
|
808
|
-
# MODIFIED or REMOVED, which have open stories against them. An ADDED requirement
|
|
809
|
-
# is never in this set — no story cites an id that did not exist a moment ago.
|
|
810
|
-
# NO `on_error: continue` here, unlike the pre-authoring scan above. That one is a
|
|
811
|
-
# preference and degrades honestly to "no preference"; this one is the only thing
|
|
812
|
-
# standing between an amendment and the people building against it. Continuing past a
|
|
813
|
-
# failure bound disturbed_any to nothing, which is falsy, which SKIPS the ask — the
|
|
814
|
-
# check designed to stop a silent amendment failing open into a silent amendment.
|
|
815
|
-
- id: ext_disturbed
|
|
816
|
-
condition: "{{change_count}} != 0"
|
|
817
|
-
action: execute_script
|
|
818
|
-
script: { ref: actions }
|
|
819
|
-
subcommand: story-refs
|
|
820
|
-
args: ["--feature-dir", "{{feature_dir}}", "--ids", "{{disturbing_csv}}"]
|
|
821
|
-
outputs:
|
|
822
|
-
- { name: disturbed_any, from: "$.any" }
|
|
823
|
-
- { name: disturbed_csv, from: "$.affected_csv" }
|
|
824
|
-
- { name: disturbed_ids, from: "$.affected_ids" }
|
|
825
|
-
- { name: disturbed_stories, from: "$.story_csv" }
|
|
826
|
-
- { name: disturbed_flying, from: "$.in_flight_csv" }
|
|
827
|
-
|
|
828
|
-
# 10c. THE ASK. Changing a requirement someone is building against is a decision with a
|
|
829
|
-
# cost the machine cannot weigh: it may be the only honest way to state the new
|
|
830
|
-
# contract, or it may be an amendment that could equally have been an addition. The
|
|
831
|
-
# two outcomes differ sharply for the people downstream —
|
|
832
|
-
#
|
|
833
|
-
# amend the contract moves. Every open story citing it goes stale, and
|
|
834
|
-
# sync-stories reconciles them: edited in place if no story plan
|
|
835
|
-
# exists yet, carried by a follow-on if one does (§5.5).
|
|
836
|
-
# add the amended requirements go back to how they read, and the change is
|
|
837
|
-
# expressed as new requirements alongside them. Nobody's work goes
|
|
838
|
-
# stale, because nothing they cite has moved, and the new requirements
|
|
839
|
-
# become new stories.
|
|
840
|
-
#
|
|
841
|
-
# Asked rather than decided, for the same reason §5.4 asks on indirect impact:
|
|
842
|
-
# there is no oracle for "should this have been additive", and a wrong silent
|
|
843
|
-
# answer either strands work or splits one contract across two requirements.
|
|
844
|
-
# Amend is first, so a non-interactive run keeps what the author actually wrote.
|
|
845
|
-
- id: ask_disturb
|
|
846
|
-
condition: "{{disturbed_any}} == true"
|
|
847
|
-
action: ask
|
|
848
|
-
multi: true
|
|
849
|
-
prompt: |
|
|
850
|
-
This change MODIFIED or REMOVED requirements that people have open stories against.
|
|
851
|
-
Requirements: {{disturbed_csv}}
|
|
852
|
-
Open stories: {{disturbed_stories}}
|
|
853
|
-
Already in flight (story plan exists): {{disturbed_flying}}
|
|
854
|
-
Pick the ones this change MAY alter. Each one you pick moves the contract: its open
|
|
855
|
-
stories go stale, and sync-stories reconciles them — editing a story in place, or
|
|
856
|
-
minting a follow-on where a story plan already exists. Each one you LEAVE OUT is put
|
|
857
|
-
back exactly as it read, and the change it was carrying is expressed as a new
|
|
858
|
-
requirement alongside it, so the work already under way stays correct.
|
|
859
|
-
Which may be changed?
|
|
860
|
-
options_from: "{{disturbed_ids}}"
|
|
861
|
-
answer_key: may_change
|
|
862
|
-
answer_var: may_change
|
|
863
|
-
|
|
864
|
-
# 10d. The complement: disturbed minus approved is what must go back. Computed by a
|
|
865
|
-
# script because the orchestrator binds values and never computes them.
|
|
866
|
-
- id: ext_preserve
|
|
867
|
-
condition: "{{disturbed_any}} == true"
|
|
529
|
+
# 8. Read the current version for the report (both paths).
|
|
530
|
+
- id: version_read
|
|
868
531
|
action: execute_script
|
|
869
532
|
script: { ref: actions }
|
|
870
|
-
subcommand:
|
|
871
|
-
args: ["--disturbing", "{{disturbed_csv}}", "--approved", "{{may_change}}"]
|
|
872
|
-
outputs:
|
|
873
|
-
- { name: preserve_csv, from: "$.preserve_csv" }
|
|
874
|
-
- { name: preserve_count, from: "$.preserve_count" }
|
|
875
|
-
- { name: approved_csv, from: "$.approved_csv" }
|
|
876
|
-
|
|
877
|
-
# 10e. Restore what was not approved. The snapshot is still on disk, so both versions
|
|
878
|
-
# are in context: put those requirements back exactly as they read, and carry what
|
|
879
|
-
# they were expressing in new requirements instead. Requirements the author DID
|
|
880
|
-
# approve stay amended — this is per requirement, not all-or-nothing.
|
|
881
|
-
- id: ext_readdit
|
|
882
|
-
condition: "{{preserve_count}} != 0"
|
|
883
|
-
action: agent_author
|
|
884
|
-
# The constitutions its own grader and patcher already read. Minting requirements
|
|
885
|
-
# without them meant the step that WRITES the new contract was the only one in the
|
|
886
|
-
# chain flying blind, and the two steps after it then graded and patched what it
|
|
887
|
-
# produced against rules it had never seen.
|
|
888
|
-
context:
|
|
889
|
-
- "{{spec_file}}"
|
|
890
|
-
- "{{snapshot}}"
|
|
891
|
-
- { path: .appweaver/memory/constitution.domain.md, optional: true }
|
|
892
|
-
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
893
|
-
include_standards: false
|
|
894
|
-
output_target: { type: file, path: "{{spec_file}}" }
|
|
895
|
-
task: |
|
|
896
|
-
Two versions of this specification are in context: the CURRENT one at
|
|
897
|
-
{{spec_file}}, and the SNAPSHOT of it taken before this run edited it, at
|
|
898
|
-
{{snapshot}}. Some of the requirements this run changed
|
|
899
|
-
were NOT approved for change, because people have open stories against them.
|
|
900
|
-
|
|
901
|
-
Restore, and re-express additively: {{preserve_csv}}
|
|
902
|
-
Leave amended as they now stand: {{approved_csv}}
|
|
903
|
-
|
|
904
|
-
Return the COMPLETE document. Rules:
|
|
905
|
-
- Restore each requirement in the FIRST list to EXACTLY its snapshot wording. If one
|
|
906
|
-
was removed, put it back where it was, with its original id.
|
|
907
|
-
- Carry whatever those restored requirements were expressing in NEW requirements
|
|
908
|
-
instead, each taking the next free id in its class. Where a new requirement
|
|
909
|
-
narrows, extends or supersedes a restored one, say so in its text and name the
|
|
910
|
-
id — the pair has to read as one coherent contract, not two rules that contradict
|
|
911
|
-
each other.
|
|
912
|
-
- Requirements in the SECOND list were approved for change. Leave them exactly as
|
|
913
|
-
the current document has them; do not restore them and do not re-express them.
|
|
914
|
-
- Where a restored requirement's change genuinely cannot be expressed without
|
|
915
|
-
contradicting it, leave that ONE amended as it now stands and add a note naming
|
|
916
|
-
it. A contract that states two incompatible things is worse than an amendment
|
|
917
|
-
somebody has to reconcile.
|
|
918
|
-
- Every other part of the document stays exactly as it currently reads. Do not
|
|
919
|
-
touch the "**Version**" header or the "## Change History" table, and leave everything between the AW:COVERAGE markers exactly as it is.
|
|
920
|
-
|
|
921
|
-
# 10f. GRADE WHAT THE RESTORE WROTE. ext_readdit runs after the converge loop has
|
|
922
|
-
# finished, so the requirements it mints to carry the change would otherwise be the
|
|
923
|
-
# only ones in the document never checked for EARS form, testability or coherence —
|
|
924
|
-
# the additive path would produce weaker requirements than the ordinary one, which
|
|
925
|
-
# is precisely backwards, since it is the path taken when people are already
|
|
926
|
-
# building against this contract.
|
|
927
|
-
#
|
|
928
|
-
# Graded over the WHOLE document rather than only the new ids, because the point of
|
|
929
|
-
# a restored requirement plus its new companion is that the pair reads as ONE
|
|
930
|
-
# contract; only a whole-document pass can see that they do. Two iterations, and
|
|
931
|
-
# the patch is forbidden from touching either the restored or the approved
|
|
932
|
-
# requirements — otherwise the fix could re-disturb what the author just protected.
|
|
933
|
-
- id: readdit_check_loop
|
|
934
|
-
condition: "{{preserve_count}} != 0"
|
|
935
|
-
action: loop
|
|
936
|
-
while: "{{readdit_pass}} != true"
|
|
937
|
-
max_iterations: 2
|
|
938
|
-
steps:
|
|
939
|
-
- id: readdit_validate
|
|
940
|
-
action: agent_query
|
|
941
|
-
# The same sources the main validate gets. Without them this pass could not judge the
|
|
942
|
-
# Domain Lift or Experience Lift items at all, so the requirements minted by the
|
|
943
|
-
# ADDITIVE path — the path taken precisely when people are already building against
|
|
944
|
-
# this contract — were graded more thinly than the ones the ordinary path writes.
|
|
945
|
-
context:
|
|
946
|
-
- "{{checklist_file}}"
|
|
947
|
-
- "{{spec_file}}"
|
|
948
|
-
- { path: .appweaver/memory/constitution.domain.md, optional: true }
|
|
949
|
-
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
950
|
-
include_standards: false
|
|
951
|
-
task: |
|
|
952
|
-
The spec in context was just re-authored: some requirements were restored to
|
|
953
|
-
their earlier wording, and NEW requirements were added to carry the change they
|
|
954
|
-
were expressing. That happened after the quality pass, so the new requirements
|
|
955
|
-
have never been graded.
|
|
956
|
-
Restored (must read exactly as they did): {{preserve_csv}}
|
|
957
|
-
Amended with approval: {{approved_csv}}
|
|
958
|
-
Evaluate the document against each checklist item (in context, 1-based in
|
|
959
|
-
order), paying particular attention to the requirements added by the
|
|
960
|
-
re-authoring: EARS form, atomic and testable, stable ids, no implementation
|
|
961
|
-
mechanics. Judge also whether each restored requirement and the new one carrying
|
|
962
|
-
its change read as ONE coherent contract rather than two rules that contradict
|
|
963
|
-
each other. In "failing", quote the offending part per failed item.
|
|
964
|
-
schema: { all_pass: false, failing: [ { n: 2, issue: "<quote>" } ] }
|
|
965
|
-
mock: { all_pass: true, failing: [] }
|
|
966
|
-
outputs:
|
|
967
|
-
- { name: readdit_pass, from: "$.all_pass" }
|
|
968
|
-
- { name: readdit_failing, from: "$.failing" }
|
|
969
|
-
- id: readdit_break
|
|
970
|
-
action: break
|
|
971
|
-
condition: "{{readdit_pass}} == true"
|
|
972
|
-
# Same reason as patch_spec: max_iterations is 2, so iteration 1 grades and stops.
|
|
973
|
-
- id: readdit_patch
|
|
974
|
-
condition: "{{iteration}} != 1"
|
|
975
|
-
action: agent_author
|
|
976
|
-
context:
|
|
977
|
-
- "{{spec_file}}"
|
|
978
|
-
- { path: .appweaver/memory/constitution.domain.md, optional: true }
|
|
979
|
-
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
980
|
-
include_standards: false
|
|
981
|
-
task: |
|
|
982
|
-
Fix these quality failures in the spec in context.
|
|
983
|
-
Failures: {{readdit_failing}}
|
|
984
|
-
Constraints that override the failures if they ever conflict:
|
|
985
|
-
- Do NOT modify these restored requirements — their wording is deliberate and
|
|
986
|
-
people are building against it: {{preserve_csv}}
|
|
987
|
-
- Do NOT modify these approved requirements either: {{approved_csv}}
|
|
988
|
-
- Fix the NEW requirements instead: put them in EARS form, make them atomic and
|
|
989
|
-
testable, and keep each one's stated relationship to the requirement whose
|
|
990
|
-
change it carries.
|
|
991
|
-
- Preserve section order, headings and every requirement id. Do not touch the
|
|
992
|
-
"**Version**" header or the "## Change History" table, and leave everything
|
|
993
|
-
between the AW:COVERAGE markers exactly as it is.
|
|
994
|
-
output_target: { type: file, path: "{{spec_file}}" }
|
|
995
|
-
|
|
996
|
-
# 10f-bis. THE RESTORE'S SPEC STILL FAILS. Same shape, and the same stakes, as the ask
|
|
997
|
-
# after the main quality loop — with one difference that makes it matter more: this
|
|
998
|
-
# is the path taken when people are ALREADY BUILDING against these requirements, and
|
|
999
|
-
# what the loop grades is the brand-new text minted to carry a change the author
|
|
1000
|
-
# refused to make in place. Without this, a red restore appended a version row and
|
|
1001
|
-
# told those authors their work was reconciled.
|
|
1002
|
-
#
|
|
1003
|
-
# Two ordered sets rather than a compound condition: the verdict is "nothing to
|
|
1004
|
-
# grade" by default, and becomes the loop's own answer only on the path where the
|
|
1005
|
-
# loop actually ran. Record is first for the reason it is first above — spec.md is
|
|
1006
|
-
# written by now, and a run that refuses the row leaves the unledgered_edit state.
|
|
1007
|
-
- id: readdit_verdict_na
|
|
1008
|
-
action: set
|
|
1009
|
-
vars: { readdit_verdict: "true" }
|
|
1010
|
-
- id: readdit_verdict_real
|
|
1011
|
-
condition: "{{preserve_count}} != 0"
|
|
1012
|
-
action: set
|
|
1013
|
-
vars: { readdit_verdict: "{{readdit_pass}}" }
|
|
1014
|
-
|
|
1015
|
-
- id: readdit_ask
|
|
1016
|
-
condition: "{{readdit_verdict}} != true"
|
|
1017
|
-
action: ask
|
|
1018
|
-
prompt: |
|
|
1019
|
-
The re-authored spec still fails its quality pass. These requirements were restored
|
|
1020
|
-
to their earlier wording and NEW ones were minted to carry the change instead, and
|
|
1021
|
-
the grader is not satisfied with the result:
|
|
1022
|
-
Restored: {{preserve_csv}}
|
|
1023
|
-
Failing: {{readdit_failing}}
|
|
1024
|
-
spec.md is ALREADY WRITTEN either way — the only question is whether this run records
|
|
1025
|
-
what it changed.
|
|
1026
|
-
options:
|
|
1027
|
-
- "Record it — the version row is what keeps the stories in step; fix the spec next"
|
|
1028
|
-
- "Stop — write no version row, and repair spec.md by hand first"
|
|
1029
|
-
answer_key: readdit_accept
|
|
1030
|
-
answer_var: readdit_accept
|
|
1031
|
-
|
|
1032
|
-
- id: readdit_gate
|
|
1033
|
-
condition: "{{readdit_verdict}} != true"
|
|
1034
|
-
action: verify_context
|
|
1035
|
-
require_value: readdit_accept
|
|
1036
|
-
equals: "Record it — the version row is what keeps the stories in step; fix the spec next"
|
|
1037
|
-
on_mismatch: stop
|
|
1038
|
-
on_mismatch_message: >
|
|
1039
|
-
Stopped at your request with no version row written. What the grader found is above,
|
|
1040
|
-
and the checklist is at {{checklist_file}}.
|
|
1041
|
-
spec.md HAS been rewritten, so the next specify will refuse it as an unledgered edit
|
|
1042
|
-
until one of two things is true — add a Change History row naming the ids you changed
|
|
1043
|
-
and re-run spec-stamp, or revert the document from the copy taken before this run,
|
|
1044
|
-
which is beside it at {{snapshot}}.
|
|
1045
|
-
|
|
1046
|
-
# 10g. Re-diff after re-authoring AND its quality pass, so the ledger row records what
|
|
1047
|
-
# finally landed rather than what the first pass wrote. Skipped when everything was
|
|
1048
|
-
# approved. Must run after the gate above: a patch there can add or reword a
|
|
1049
|
-
# requirement, and a row computed before it would miss that.
|
|
1050
|
-
- id: ext_rediff
|
|
1051
|
-
condition: "{{preserve_count}} != 0"
|
|
1052
|
-
action: execute_script
|
|
1053
|
-
script: { ref: actions }
|
|
1054
|
-
subcommand: spec-diff
|
|
1055
|
-
args: ["--feature-dir", "{{feature_dir}}", "--snapshot", "{{snapshot}}"]
|
|
1056
|
-
outputs:
|
|
1057
|
-
- { name: changed_csv, from: "$.changed_csv" }
|
|
1058
|
-
- { name: story_ids_csv, from: "$.story_ids_csv" }
|
|
1059
|
-
- { name: removed_csv, from: "$.removed_csv" }
|
|
1060
|
-
- { name: modified_csv, from: "$.modified_csv" }
|
|
1061
|
-
- { name: disturbing_csv, from: "$.disturbing_csv" }
|
|
1062
|
-
- { name: reissued_csv, from: "$.reissued_csv" }
|
|
1063
|
-
- { name: change_count, from: "$.change_count" }
|
|
1064
|
-
|
|
1065
|
-
# 10g-bis. DID THE RESTORE HOLD. ext_readdit is a model asked to put requirements back to
|
|
1066
|
-
# their earlier wording, and until now nothing checked that it did. The re-diff is the
|
|
1067
|
-
# proof: a requirement the author refused to move must have dropped out of the
|
|
1068
|
-
# disturbing set. One still in it was amended WITHOUT approval — and the run would
|
|
1069
|
-
# otherwise close by telling that author, in the same sentence, that it was left alone.
|
|
1070
|
-
#
|
|
1071
|
-
# It announces rather than stops. spec.md is already written by this point, so a stop
|
|
1072
|
-
# here leaves a document on disk with no ledger row, which is the state the next run's
|
|
1073
|
-
# tripwire refuses. Announcing keeps the ledger row honest — the violated ids ARE in
|
|
1074
|
-
# it, so sync-stories still reconciles their stories — and moves them out of the
|
|
1075
|
-
# "preserved" column of the report into a column that says what actually happened.
|
|
1076
|
-
- id: ext_restore_check
|
|
1077
|
-
condition: "{{preserve_count}} != 0"
|
|
1078
|
-
action: execute_script
|
|
1079
|
-
script: { ref: actions }
|
|
1080
|
-
subcommand: spec-preserve
|
|
1081
|
-
args: ["--verify", "--preserved", "{{preserve_csv}}", "--disturbing", "{{disturbing_csv}}"]
|
|
1082
|
-
outputs:
|
|
1083
|
-
- { name: held_csv, from: "$.held_csv" }
|
|
1084
|
-
- { name: violated_csv, from: "$.violated_csv" }
|
|
1085
|
-
- { name: restore_ok, from: "$.restore_ok" }
|
|
1086
|
-
|
|
1087
|
-
- id: ext_restore_gate
|
|
1088
|
-
condition: "{{preserve_count}} != 0"
|
|
1089
|
-
action: verify_context
|
|
1090
|
-
require_true: restore_ok
|
|
1091
|
-
on_false: continue
|
|
1092
|
-
on_false_message: >
|
|
1093
|
-
The re-authoring did NOT restore {{violated_csv}} — you declined to change these and
|
|
1094
|
-
they moved anyway. They are recorded in this version's Change History row, so
|
|
1095
|
-
/appweaver sync-stories will reconcile the stories citing them rather than leaving
|
|
1096
|
-
them silently stale; check that the wording it landed on is one you can live with.
|
|
1097
|
-
|
|
1098
|
-
# 10h. Re-read the affected stories from the FINAL document. After a restore, the
|
|
1099
|
-
# requirements that were put back disturb nobody any more, and their stories must
|
|
1100
|
-
# drop out of the report — otherwise the run tells people to go and reconcile work
|
|
1101
|
-
# that this run deliberately left alone. Same call as 10b, over what actually landed.
|
|
1102
|
-
# Keyed on disturbed_any, not change_count: a restore that put everything back can leave
|
|
1103
|
-
# the run with nothing changed at all, and on that path a change_count gate skipped this
|
|
1104
|
-
# step and left the report naming the stories from BEFORE the restore — telling people to
|
|
1105
|
-
# go and reconcile work this run deliberately left alone.
|
|
1106
|
-
- id: ext_final_refs
|
|
1107
|
-
condition: "{{disturbed_any}} == true"
|
|
1108
|
-
action: execute_script
|
|
1109
|
-
on_error: continue
|
|
1110
|
-
script: { ref: actions }
|
|
1111
|
-
subcommand: story-refs
|
|
1112
|
-
args: ["--feature-dir", "{{feature_dir}}", "--ids", "{{disturbing_csv}}"]
|
|
1113
|
-
outputs:
|
|
1114
|
-
- { name: disturbed_stories, from: "$.story_csv" }
|
|
1115
|
-
- { name: final_refs_ok, from: "$.ok" }
|
|
1116
|
-
|
|
1117
|
-
# 10h-bis. THE SAME HONESTY THE PRE-AUTHORING SCAN NOW HAS. This step carries
|
|
1118
|
-
# `on_error: continue` so a failed re-scan cannot strand a written document, and on
|
|
1119
|
-
# failure disturbed_stories binds to nothing — which the post-restore report (law 45)
|
|
1120
|
-
# then reads as "nobody was disturbed", the one claim it must not make on no evidence.
|
|
1121
|
-
- id: final_refs_unknown_notice
|
|
1122
|
-
condition: "{{final_refs_ok}} != true"
|
|
1123
|
-
action: verify_context
|
|
1124
|
-
require_value: final_refs_ok
|
|
1125
|
-
equals: "true"
|
|
1126
|
-
on_mismatch: continue
|
|
1127
|
-
on_mismatch_message: >
|
|
1128
|
-
The final backlog re-scan did not complete, so the disturbed-stories list below is
|
|
1129
|
-
NOT a list of nobody — it is unknown. The requirements this run moved are
|
|
1130
|
-
{{disturbing_csv}}; run /appweaver sync-stories to find and reconcile the stories
|
|
1131
|
-
carrying them.
|
|
1132
|
-
|
|
1133
|
-
# 10i. AN ID THE LEDGER HAS ALREADY SPENT. Ids are permanent: re-issuing a retired one
|
|
1134
|
-
# silently re-points stories, designs and code that have already shipped, which is
|
|
1135
|
-
# why every authoring prompt forbids it — and until now nothing checked. Announced,
|
|
1136
|
-
# not blocked, for the same reason as the restore gate: the document is written by
|
|
1137
|
-
# the time this is visible, and refusing here would strand it. Deciding WHAT to do
|
|
1138
|
-
# about a re-issued id is still the author's, and is not yet automated.
|
|
1139
|
-
- id: ext_reissue_gate
|
|
1140
|
-
condition: "{{change_count}} != 0"
|
|
1141
|
-
action: verify_context
|
|
1142
|
-
require_value: reissued_csv
|
|
1143
|
-
equals: ""
|
|
1144
|
-
on_mismatch: continue
|
|
1145
|
-
on_mismatch_message: >
|
|
1146
|
-
{{reissued_csv}} was introduced by this run, but the Change History has already
|
|
1147
|
-
spent it. An id is permanent — whatever cited it before now points at something
|
|
1148
|
-
else. Renumber the new requirement to the next free id in its class before anyone
|
|
1149
|
-
builds against it.
|
|
1150
|
-
|
|
1151
|
-
# 11. One call writes the row, bumps the version, stamps the header and re-hashes, so
|
|
1152
|
-
# a row cannot exist without the hash that proves the body it describes. Skipped
|
|
1153
|
-
# when the run changed no requirement — a version that names no ids is a bump
|
|
1154
|
-
# sync-stories can do nothing with.
|
|
1155
|
-
- id: ext_ledger_append
|
|
1156
|
-
condition: "{{change_count}} != 0"
|
|
1157
|
-
action: execute_script
|
|
1158
|
-
script: { ref: actions }
|
|
1159
|
-
subcommand: ledger-append
|
|
1160
|
-
args: ["--feature-dir", "{{feature_dir}}", "--ids", "{{changed_csv}}", "--summary", "{{USER_INPUT}}"]
|
|
1161
|
-
outputs:
|
|
1162
|
-
- { name: ledger_version, from: "$.version" }
|
|
1163
|
-
|
|
1164
|
-
# 11a-bis. A PROSE-ONLY EDIT RE-BASELINES WITH NO ROW, AND SAYS SO. ledger-append is
|
|
1165
|
-
# conditional on change_count, because law 5 requires a version row to NAME at least
|
|
1166
|
-
# one id and an edit that touched no requirement has none to give. The stamp below is
|
|
1167
|
-
# not conditional, so this run rewrites the baseline and the document's own history
|
|
1168
|
-
# records nothing — which is right, and was silent. Aggregate Boundary, Experience
|
|
1169
|
-
# Scope and the rest of the load-bearing prose are read by every later phase and have
|
|
1170
|
-
# no staleness signal of their own; the least this can do is say that the baseline
|
|
1171
|
-
# moved and nothing recorded why.
|
|
1172
|
-
- id: prose_only_notice
|
|
1173
|
-
condition: "{{change_count}} == 0"
|
|
1174
|
-
action: verify_context
|
|
1175
|
-
require_value: needs_bootstrap
|
|
1176
|
-
equals: "true"
|
|
1177
|
-
on_mismatch: continue
|
|
1178
|
-
on_mismatch_message: >
|
|
1179
|
-
This run changed {{spec_file}} but moved no requirement: the diff found no id added,
|
|
1180
|
-
modified or removed, so no Change History row was appended — a row must name at least
|
|
1181
|
-
one id, and there is none to name. The document is still RE-BASELINED below, so the
|
|
1182
|
-
next run reads it as clean. Nothing else is: if the edit touched load-bearing prose
|
|
1183
|
-
(Aggregate Boundary, Experience Scope, Scope & Assumptions), no story is stale, no
|
|
1184
|
-
sync will notice, and the only record is this message and your commit. Say what moved
|
|
1185
|
-
in the commit message.
|
|
1186
|
-
|
|
1187
|
-
# ═══════════════ STORY COVERAGE — both paths, before the baseline ══════════
|
|
1188
|
-
# 11b. Rewrite the spec's AW:COVERAGE block: every FR-/BR- in the document and the
|
|
1189
|
-
# stories delivering it, read out of the stories/ frontmatter. The relation is
|
|
1190
|
-
# stored once, in the story; this is that relation the other way round, which is
|
|
1191
|
-
# why the block is regenerated wholesale and hand edits to it are discarded.
|
|
1192
|
-
#
|
|
1193
|
-
# Deliberately OUTSIDE the ledger. The hash guards the CONTRACT, and coverage is a
|
|
1194
|
-
# projection of the backlog — spec-check, spec-diff and ledger-append all cut this
|
|
1195
|
-
# block out before they look. That is what lets add-story and sync-stories write
|
|
1196
|
-
# it too without a story creation reading as an unledgered spec edit, or a story
|
|
1197
|
-
# raised against FR-003 reading as FR-003 having changed.
|
|
1198
|
-
#
|
|
1199
|
-
# Run on the bootstrap path as well, where it lists every requirement as uncovered:
|
|
1200
|
-
# a new spec that nobody is building yet is exactly what it should say.
|
|
1201
|
-
- id: coverage_refresh
|
|
1202
|
-
action: execute_script
|
|
1203
|
-
on_error: continue
|
|
1204
|
-
script: { ref: actions }
|
|
1205
|
-
subcommand: spec-coverage
|
|
533
|
+
subcommand: version-read
|
|
1206
534
|
args: ["--feature-dir", "{{feature_dir}}"]
|
|
1207
535
|
outputs:
|
|
1208
|
-
- { name:
|
|
1209
|
-
|
|
1210
|
-
# ═══════════════════ RE-BASELINE — both paths, always last ═════════════════
|
|
1211
|
-
# 12. Record the finished document's hash. Everything above may have rewritten spec.md
|
|
1212
|
-
# after the ledger row was appended (the converge loop on the bootstrap path, and
|
|
1213
|
-
# nothing on the extend path — but the order is what guarantees that), so the hash
|
|
1214
|
-
# is taken here, once, over the document as it will be read next. Without this the
|
|
1215
|
-
# next run's entry tripwire would see a changed body and refuse a spec that is
|
|
1216
|
-
# perfectly in step.
|
|
1217
|
-
# Skipped when the bootstrap's v1.0 row never landed: a hash is a claim that the
|
|
1218
|
-
# document on disk is the one the ledger describes, and there is no ledger row to
|
|
1219
|
-
# describe it. Leaving it unstamped keeps that honest and costs nothing — the
|
|
1220
|
-
# bootstrap marker is still up, so the next run re-authors rather than extends.
|
|
1221
|
-
- id: spec_stamp
|
|
1222
|
-
condition: "{{stamp_hold}} != true"
|
|
1223
|
-
action: execute_script
|
|
1224
|
-
script: { ref: actions }
|
|
1225
|
-
subcommand: spec-stamp
|
|
1226
|
-
args: ["--feature-dir", "{{feature_dir}}"]
|
|
1227
|
-
outputs:
|
|
1228
|
-
- { name: version, from: "$.version" }
|
|
1229
|
-
|
|
1230
|
-
# 13. Post-execution hooks: resolve (script) then dispatch (invoking agent).
|
|
1231
|
-
# 12b. Say so when it was held. Everything downstream reads `version`, and a first draft
|
|
1232
|
-
# with no row and no hash is not a baseline anything should build on.
|
|
1233
|
-
- id: boot_incomplete_notice
|
|
1234
|
-
condition: "{{stamp_hold}} == true"
|
|
1235
|
-
action: verify_context
|
|
1236
|
-
require_value: stamp_hold
|
|
1237
|
-
equals: "false"
|
|
1238
|
-
on_mismatch: continue
|
|
1239
|
-
on_mismatch_message: >
|
|
1240
|
-
The first draft was written to {{spec_file}}, but its v1.0 Change History row could not
|
|
1241
|
-
be added — so the document is deliberately left UNSTAMPED and still marked an unfinished
|
|
1242
|
-
bootstrap. Do not treat it as a baseline. Re-run /appweaver specify with the same input
|
|
1243
|
-
(it re-authors the draft rather than extending it), or add the v1.0 row by hand naming
|
|
1244
|
-
the requirement ids and re-run spec-stamp.
|
|
536
|
+
- { name: version, from: "$.version" }
|
|
537
|
+
- { name: active_change, from: "$.active_change" }
|
|
1245
538
|
|
|
539
|
+
# 9. Post-execution hooks: resolve (script) then dispatch (invoking agent).
|
|
1246
540
|
- id: resolve_post_hooks
|
|
1247
541
|
action: execute_script
|
|
1248
542
|
script: { ref: actions }
|
|
@@ -1257,17 +551,15 @@ workflow:
|
|
|
1257
551
|
outputs:
|
|
1258
552
|
feature_directory: "{{feature_dir}}"
|
|
1259
553
|
spec_file: "{{spec_file}}"
|
|
554
|
+
bounded_context: "{{context}}"
|
|
555
|
+
domain_model: "{{domain_model}}"
|
|
556
|
+
delta_model: "{{delta_model}}" # DELTA path only — this change's model half
|
|
557
|
+
wave: "{{wave}}" # DELTA path only — its parallel-development level
|
|
558
|
+
open_changes: "{{open_count}}" # how many changes are now open on this feature
|
|
1260
559
|
version: "{{version}}"
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
requirements_removed: "{{removed_csv}}"
|
|
1264
|
-
requirements_amended: "{{modified_csv}}" # what finally stands amended, ask or no ask
|
|
1265
|
-
requirements_preserved: "{{held_csv}}" # the ones the restore actually put back
|
|
1266
|
-
requirements_not_restored: "{{violated_csv}}" # declined, but moved anyway
|
|
1267
|
-
requirements_reissued: "{{reissued_csv}}" # ids the ledger had already spent
|
|
1268
|
-
open_change: "{{open_change}}" # a delta change open on this feature, if any
|
|
1269
|
-
requirements_uncovered: "{{uncovered_csv}}"
|
|
1270
|
-
stories_disturbed: "{{disturbed_stories}}"
|
|
560
|
+
change_id: "{{cr_id}}"
|
|
561
|
+
authored_document: "{{quality_target}}" # spec.md (bootstrap) | delta-spec.md (delta)
|
|
1271
562
|
checklist_file: "{{checklist_file}}"
|
|
1272
563
|
checklist_quality: "{{checklist_passed}}/{{checklist_total}} items passing"
|
|
1273
|
-
|
|
564
|
+
note: "Behaviour and structure were authored together: the spec says what the system must do, {{domain_model}} says what the domain IS. The aggregate root, its entities and its invariants (BR-n) live in the model and are cited from the spec by id — never restated there, because several specs act on one aggregate and a per-spec copy drifts. On the DELTA path neither master was touched: this run wrote changes/{{cr_id}}/delta-spec.md and {{delta_model}}, and that model delta sits BESIDE the master where every other open delta can see it while being authored. WAVE {{wave}} is its parallel-development level — deltas sharing a wave are independent, a higher wave is implemented only after the lower ones land; `cr-wave --cr-id {{cr_id}} --wave <n>` moves it. {{open_count}} change(s) are open on this feature."
|
|
565
|
+
next: "/appweaver plan — BOOTSTRAP authors backend-plan.md from the spec + the domain model; a DELTA (this run opened change {{cr_id}}, wave {{wave}}) authors the delta plans for it. A change then flows: analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta (finalize-delta merges the delta into spec.md + domain-model.md — not the lane plans, which are derived from those two — but leaves the change open; analyze-slice verifies the slice against the merged master; close-delta archives the change and bumps the version). ⚠️ With more than one change open, plan, analyze-plan-delta and implement ASK which one they are for (pass the id as the argument to skip the prompt) and activate the answer; finalize-delta and close-delta still act on whichever change is active — so finish or close the others deliberately."
|