aw-spec-cli 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/.appweaver/STACK-PROFILES.md +1 -1
- package/assets/.appweaver/orchestrator/orchestrator.py +5 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +10 -3
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +38 -11
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +2 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +607 -123
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +625 -66
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +407 -134
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +452 -143
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +78 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +597 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +688 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +23 -23
- package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +945 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +1040 -0
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +2069 -28
- package/assets/.appweaver/scripts/bash/aw-xref.sh +2 -1
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +1973 -13
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +2 -1
- package/assets/.appweaver/scripts/python/aw-xref.py +246 -20
- package/assets/.appweaver/templates/design-template.md +88 -54
- package/assets/.appweaver/templates/spec-template.md +12 -10
- package/assets/.appweaver/templates/story-template.md +48 -8
- package/assets/.appweaver/templates/ui-design-template.md +71 -39
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +19 -7
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +35 -26
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design-ui.orch.yaml +0 -386
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design.orch.yaml +0 -382
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# aw-spec-plan (orchestrator) — the spec-anchored Plan phase (service lane)
|
|
3
|
+
#
|
|
4
|
+
# Authors and MAINTAINS one high-level plan per feature and lane. This workflow owns
|
|
5
|
+
# the SERVICE lane's document — specs/<Aggregate>/backend-plan.md — the tech-agnostic
|
|
6
|
+
# HLD that MEDIATES between the spec and the code (formerly design.md; the scaffold
|
|
7
|
+
# migrates a legacy design.md by renaming it). The frontend lane's twin,
|
|
8
|
+
# frontend-plan.md, is owned by /appweaver plan-ui — a SEPARATE command because the
|
|
9
|
+
# two lanes are governed by different rule sets (tech-rule-bindings gives each lane
|
|
10
|
+
# its own set; one command would hand this phase both lanes' whole rule surface).
|
|
11
|
+
# `implement` GENERATES the code from spec + plan (model-driven, not a deterministic
|
|
12
|
+
# extract). See .appweaver/DESIGN-FORMAT.md + .appweaver/templates/design-template.md.
|
|
13
|
+
# (`plan` re-uses the name of a long-retired phase whose plan.md had zero consumers.)
|
|
14
|
+
#
|
|
15
|
+
# WHERE IT SITS. specify/sync-spec move the spec; the developer then runs plan, which
|
|
16
|
+
# conforms this feature's design to what moved. Story designs (/appweaver story-plan)
|
|
17
|
+
# READ this document for the ids they carry and NEVER write back — nothing merges into
|
|
18
|
+
# it, so it keeps exactly one writer and cannot be silently rewritten. It is the
|
|
19
|
+
# FEATURE-LEVEL ARCHITECTURAL CONFIRMATION: what the feature's structure IS to be,
|
|
20
|
+
# tech-agnostic, with the concrete file-level work left to the per-story passes.
|
|
21
|
+
#
|
|
22
|
+
# IT READS THE CODE. concept-paths resolves the feature's OKF concept to the source
|
|
23
|
+
# files realising it and the authoring steps declare `tools: Read`, so a plan over an
|
|
24
|
+
# existing codebase ADJUSTS to the shape that is actually there. The code is the
|
|
25
|
+
# what-exists authority; the spec stays the only authority on behaviour and the only
|
|
26
|
+
# domain input. source_count 0 = greenfield, and the phase never blocks on it.
|
|
27
|
+
#
|
|
28
|
+
# IT PUBLISHES THE INTERFACES. The feature's published surface lives in
|
|
29
|
+
# specs/<Aggregate>/interfaces.md — APPEND-ONLY, script-numbered (IF-nnn, dated,
|
|
30
|
+
# status: proposed). This phase writes it and §3 of the plan cites the ids; /appweaver
|
|
31
|
+
# plan-ui and the FE story designs CONSUME the latest entry per id. A changed interface
|
|
32
|
+
# is a superseding entry, never an edit — one wording, one lifecycle, no second copy.
|
|
33
|
+
#
|
|
34
|
+
# THREE MODES, resolved deterministically (self-detecting on the plan doc's own
|
|
35
|
+
# existence, symmetric with how specify keys off spec.md):
|
|
36
|
+
# • bootstrap_gf (default) — no backend-plan.md yet: author the master from the
|
|
37
|
+
# whole spec, four-layer greenfield template (Domain·Application·API·Infra).
|
|
38
|
+
# • bootstrap_bf — same, but onboard installed the code-derived design-template
|
|
39
|
+
# override: author with the project's AS-BUILT structure instead.
|
|
40
|
+
# • update — backend-plan.md exists: conform it to what the spec's Change History
|
|
41
|
+
# ledger says moved SINCE THIS PLAN WAS LAST DERIVED, and nothing else. This
|
|
42
|
+
# replaces the former delta-design branch (delta-design + finalize-delta merge);
|
|
43
|
+
# in the story flow the spec moves via sync-spec and the plan follows here.
|
|
44
|
+
#
|
|
45
|
+
# TRACKING (the lockstep made real). Every write ends with plan-stamp, which is the
|
|
46
|
+
# only writer of the plan's version identity:
|
|
47
|
+
# • the doc's **Version** header := the spec version this plan now derives from,
|
|
48
|
+
# • an update appends the plan's own Change History row naming the conformed ids,
|
|
49
|
+
# • .aw-plan-track.backend-plan.json (beside the spec) records {spec_version,
|
|
50
|
+
# sha256} — one track file per plan document, so the two lanes never contend.
|
|
51
|
+
# plan-check reads that record back: BEHIND (ledger rows above the derived version →
|
|
52
|
+
# the exact ids to conform), EDITED (hash mismatch — the doc was hand-edited since
|
|
53
|
+
# the stamp; refused unless accept_manual folds the edits in), UNTRACKED (a migrated
|
|
54
|
+
# legacy design.md with no baseline — the update reconciles the WHOLE plan once and
|
|
55
|
+
# baselines it). The model writes NO version anywhere; the script stamps.
|
|
56
|
+
#
|
|
57
|
+
# LANE-AWARE. Lanes come from .appweaver/stacks.json (select-stack declares them):
|
|
58
|
+
# • backend lane declared → author backend-plan.md. This is a REQUIREMENT, not a
|
|
59
|
+
# default: with no backend stack declared the phase stops and says so, because a
|
|
60
|
+
# service plan written against no declared service invents its own stack.
|
|
61
|
+
# • frontend lane also declared → the handoff names /appweaver plan-ui.
|
|
62
|
+
# • frontend-only, or nothing declared at all → stop, each with its own message.
|
|
63
|
+
#
|
|
64
|
+
# AUTHORS only — the gates are SEPARATE stages: /appweaver analyze-design proves
|
|
65
|
+
# spec ↔ plan conformance after every bootstrap AND every update.
|
|
66
|
+
#
|
|
67
|
+
# Launch: /appweaver plan [guidance] (accept_manual:true folds hand edits in)
|
|
68
|
+
# Prereq: /appweaver specify
|
|
69
|
+
# =============================================================================
|
|
70
|
+
|
|
71
|
+
name: aw-spec-plan
|
|
72
|
+
description: "Author or update the service lane's high-level plan — specs/<Feature>/backend-plan.md — from the spec. Self-detects: no plan yet ⇒ bootstrap the master (greenfield four-layer template, or the code-derived structure when onboard installed the design-template override); plan exists ⇒ UPDATE — conform exactly the sections serving the ids the spec's Change History moved since this plan's derivation stamp, preserving the Assumptions/Workarounds/Change History ledgers. Tracking is script-owned: plan-stamp moves the **Version** header to the derived spec version, appends the plan's history row and records the body hash; plan-check refuses hand-edited documents unless accept_manual folds them in. Reads the CODE as well as the spec (concept-paths + tools: Read), so a plan over an existing codebase conforms to the as-built shape; the spec remains the only domain and behaviour authority. Publishes the feature's PUBLISHED SURFACE to the append-only interfaces.md (script-assigned IF-nnn, status: proposed) — the anchor /appweaver plan-ui and the FE story designs consume; the plan's API section cites those ids rather than restating the contract. Lane-aware via stacks.json — a declared frontend lane routes its twin to /appweaver plan-ui; frontend-only projects are refused. Authoring only — gate every write at /appweaver analyze-design."
|
|
73
|
+
version: 4.1.0
|
|
74
|
+
agent_agnostic: true
|
|
75
|
+
dialect: aw-spec.orchestrator/v1
|
|
76
|
+
|
|
77
|
+
inputs:
|
|
78
|
+
user_input:
|
|
79
|
+
description: Optional plan guidance (architectural constraints, trade-off preferences).
|
|
80
|
+
required: false
|
|
81
|
+
bind: USER_INPUT
|
|
82
|
+
accept_manual:
|
|
83
|
+
description: "true ⇒ a hand-edited plan (tracking state edited/edited_behind) is folded in instead of refused: the update keeps the manual edits' intent, reconciles them with the spec, and the stamp re-baselines."
|
|
84
|
+
required: false
|
|
85
|
+
bind: ACCEPT_MANUAL
|
|
86
|
+
|
|
87
|
+
scripts:
|
|
88
|
+
actions:
|
|
89
|
+
sh: .appweaver/scripts/bash/aw-spec-actions.sh
|
|
90
|
+
ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
|
|
91
|
+
xref:
|
|
92
|
+
sh: .appweaver/scripts/bash/aw-xref.sh
|
|
93
|
+
ps1: .appweaver/scripts/powershell/aw-xref.ps1
|
|
94
|
+
|
|
95
|
+
# Sticky standards seed = the UNIVERSAL always-on rule only (ai-rules). The TECHNICAL/
|
|
96
|
+
# architecture rule files are CONFIGURATION, loaded per-command from
|
|
97
|
+
# guardrails/projects/<project>/tech-rule-bindings.md under "## /aw-spec-plan" by the
|
|
98
|
+
# tech-select module. No domain rule file is loaded here — domain reaches the plan
|
|
99
|
+
# solely as spec ids (BR-/CN-/DC-/IC-). The technology base itself arrives via
|
|
100
|
+
# constitution.technical.md (referred to directly, as-is — NOT mapped).
|
|
101
|
+
standards:
|
|
102
|
+
- { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
|
|
103
|
+
|
|
104
|
+
vars:
|
|
105
|
+
lanes_declared: "false"
|
|
106
|
+
be_declared: "false"
|
|
107
|
+
fe_declared: "false"
|
|
108
|
+
be_gate_msg: "No backend lane is declared in .appweaver/stacks.json, so there is no service to plan. Run /appweaver select-stack and declare the backend stack plus its source path, then re-run plan. A project that is genuinely UI-only plans the frontend lane alone with /appweaver plan-ui."
|
|
109
|
+
brownfield_plan: "false" # design-scaffold.override_active — onboard installed the code-derived template
|
|
110
|
+
plan_created: "false" # scaffold.created — true ⇒ the doc was absent ⇒ bootstrap
|
|
111
|
+
plan_migrated: "false" # scaffold renamed a legacy design.md into backend-plan.md this run
|
|
112
|
+
plan_mode: "bootstrap_gf" # resolved below → bootstrap_gf | bootstrap_bf | update
|
|
113
|
+
track_state: "" # plan-check → fresh | untracked | clean | behind | edited | edited_behind
|
|
114
|
+
derived_version: "" # the spec version the plan last derived from (empty when untracked)
|
|
115
|
+
current_version: "" # the spec's current ledger version
|
|
116
|
+
changed_csv: "" # ids the ledger moved above the derived version
|
|
117
|
+
changed_summary: "" # the moved rows' summaries, for the update prompt
|
|
118
|
+
manual_ok: "true" # false ⇒ hand edits present and accept_manual not given
|
|
119
|
+
plan_stale: "true" # false ⇒ update mode with nothing to do (clean) — friendly stop
|
|
120
|
+
stamp_ok: "false"
|
|
121
|
+
stamp_error: ""
|
|
122
|
+
fe_next: "" # filled when a frontend lane is declared — the plan-ui handoff
|
|
123
|
+
source_csv: "" # concept-paths — the feature's source files, as a PATH LIST
|
|
124
|
+
source_count: "0" # 0 ⇒ greenfield: no code realises this feature yet
|
|
125
|
+
okf_concept: "" # the feature's OKF concept doc (use-case log, source map)
|
|
126
|
+
if_ok: "true" # log-append — the interface publication
|
|
127
|
+
if_error: ""
|
|
128
|
+
if_appended: "0"
|
|
129
|
+
if_ids_csv: ""
|
|
130
|
+
if_unknown_csv: ""
|
|
131
|
+
if_badsup_csv: ""
|
|
132
|
+
|
|
133
|
+
workflow:
|
|
134
|
+
|
|
135
|
+
# 0. TECHNICAL rule files → sticky standards (configuration, not hardcoded).
|
|
136
|
+
# tech-select reads tech-rule-bindings.md under "## /aw-spec-plan". No blocking
|
|
137
|
+
# gate: an unconfigured project soft-continues (constitution.technical.md still
|
|
138
|
+
# carries the tech base).
|
|
139
|
+
- id: tech_select
|
|
140
|
+
action: include
|
|
141
|
+
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
142
|
+
with: { command: aw-spec-plan }
|
|
143
|
+
|
|
144
|
+
# 1. The BACKEND LANE MUST EXIST. This phase writes a service design document, so
|
|
145
|
+
# it asks the declaration whether there is a service lane at all — not the
|
|
146
|
+
# lane-gate module, whose on-disk check would refuse a greenfield project that
|
|
147
|
+
# has declared `src/` but not scaffolded it yet. A plan is authored before the
|
|
148
|
+
# code exists; that is the point of planning. Two distinct refusals (the
|
|
149
|
+
# evaluator takes single comparisons only, so the message is picked by an
|
|
150
|
+
# ordered set): nothing declared at all, vs a frontend-only project.
|
|
151
|
+
- id: lane_resolve
|
|
152
|
+
action: execute_script
|
|
153
|
+
on_error: continue # never crash raw on an undeclared lane — the gate below explains it
|
|
154
|
+
script: { ref: actions }
|
|
155
|
+
subcommand: lane-resolve
|
|
156
|
+
outputs:
|
|
157
|
+
- { name: lanes_declared, from: "$.declared" }
|
|
158
|
+
- { name: be_declared, from: "$.backend_declared" }
|
|
159
|
+
- { name: fe_declared, from: "$.frontend_declared" }
|
|
160
|
+
- id: lane_msg_frontend_only
|
|
161
|
+
condition: "{{fe_declared}} == true"
|
|
162
|
+
action: set
|
|
163
|
+
vars: { be_gate_msg: "Frontend-only project: a frontend lane is declared in .appweaver/stacks.json, a backend lane is not. There is no service to plan here, and authoring backend-plan.md anyway would invent one. Run /appweaver plan-ui for frontend-plan.md; if a service lane does exist, declare it with /appweaver select-stack and re-run plan." }
|
|
164
|
+
- id: require_backend_lane
|
|
165
|
+
action: verify_context
|
|
166
|
+
require_true: be_declared
|
|
167
|
+
on_false: stop
|
|
168
|
+
on_false_message: "{{be_gate_msg}}"
|
|
169
|
+
- id: fe_handoff
|
|
170
|
+
condition: "{{fe_declared}} == true"
|
|
171
|
+
action: set
|
|
172
|
+
vars: { fe_next: "A frontend lane is declared — run /appweaver plan-ui to author/refresh frontend-plan.md against this same spec version." }
|
|
173
|
+
|
|
174
|
+
# 2. Resolve the current feature and require its spec.
|
|
175
|
+
- id: feature_paths
|
|
176
|
+
action: execute_script
|
|
177
|
+
on_error: continue # never crash raw on 'no feature set' — the gate below explains it
|
|
178
|
+
script: { ref: actions }
|
|
179
|
+
subcommand: feature-paths
|
|
180
|
+
outputs:
|
|
181
|
+
- { name: feature_dir, from: "$.feature_dir" }
|
|
182
|
+
- { name: spec_file, from: "$.spec_file" }
|
|
183
|
+
- { name: spec_exists, from: "$.spec_exists" }
|
|
184
|
+
|
|
185
|
+
- id: require_feature
|
|
186
|
+
action: verify_context
|
|
187
|
+
require_non_empty: feature_dir
|
|
188
|
+
on_empty: stop
|
|
189
|
+
on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run plan."
|
|
190
|
+
|
|
191
|
+
- id: verify_spec
|
|
192
|
+
action: verify_context
|
|
193
|
+
require_true: spec_exists
|
|
194
|
+
on_false: stop
|
|
195
|
+
on_false_message: "No spec.md for the current feature. Run /appweaver specify first."
|
|
196
|
+
|
|
197
|
+
# 3. The spec's own ledger tripwire. A plan derived from an unratified contract is
|
|
198
|
+
# conformance theatre: an edit that skipped the Change History has no version, so
|
|
199
|
+
# the derivation stamp this run writes would claim a baseline the ledger cannot
|
|
200
|
+
# name. spec-check's only ok:false is exactly that case.
|
|
201
|
+
- id: spec_guard_read
|
|
202
|
+
action: execute_script
|
|
203
|
+
script: { ref: actions }
|
|
204
|
+
subcommand: spec-check
|
|
205
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
206
|
+
outputs:
|
|
207
|
+
- { name: spec_ok, from: "$.ok" }
|
|
208
|
+
- { name: spec_state, from: "$.state" }
|
|
209
|
+
- id: spec_guard
|
|
210
|
+
action: verify_context
|
|
211
|
+
require_true: spec_ok
|
|
212
|
+
on_false: stop
|
|
213
|
+
on_false_message: "spec.md has moved outside the ledger (state: {{spec_state}}). Record what changed in a Change History row (or fold it back through /appweaver sync-spec), re-run spec-stamp to re-baseline, then re-run plan — a plan derived from an unledgered spec stamps a baseline the ledger cannot name."
|
|
214
|
+
|
|
215
|
+
# 4. Scaffold backend-plan.md (idempotent). --lane backend resolves the pair
|
|
216
|
+
# design-template → backend-plan.md, and MIGRATES a legacy design.md by renaming
|
|
217
|
+
# it (created=false, migrated=true) — one document, never two side by side.
|
|
218
|
+
- id: plan_scaffold
|
|
219
|
+
action: execute_script
|
|
220
|
+
script: { ref: actions }
|
|
221
|
+
subcommand: design-scaffold
|
|
222
|
+
args: ["--feature-dir", "{{feature_dir}}", "--lane", "backend"]
|
|
223
|
+
outputs:
|
|
224
|
+
- { name: plan_doc, from: "$.design_doc" }
|
|
225
|
+
- { name: brownfield_plan, from: "$.override_active" } # true ⇒ onboard installed the code-derived template
|
|
226
|
+
- { name: plan_created, from: "$.created" } # true ⇒ the doc was ABSENT (just scaffolded) ⇒ bootstrap
|
|
227
|
+
- { name: plan_migrated, from: "$.migrated" } # true ⇒ a legacy design.md became backend-plan.md this run
|
|
228
|
+
|
|
229
|
+
# 4a. Resolve the MODE (ordered override — the evaluator has no compound
|
|
230
|
+
# conditions): greenfield default; the code-derived override flips it; an
|
|
231
|
+
# EXISTING document flips either into an update.
|
|
232
|
+
- id: mode_brownfield
|
|
233
|
+
condition: "{{brownfield_plan}} == true"
|
|
234
|
+
action: set
|
|
235
|
+
vars: { plan_mode: "bootstrap_bf" }
|
|
236
|
+
- id: mode_update
|
|
237
|
+
condition: "{{plan_created}} == false"
|
|
238
|
+
action: set
|
|
239
|
+
vars: { plan_mode: "update" }
|
|
240
|
+
|
|
241
|
+
# 5. Tracking. plan-check diffs the doc's derivation record against the spec's
|
|
242
|
+
# ledger: WHAT moved since this plan was derived (the update's exact work list),
|
|
243
|
+
# and whether the doc itself was hand-edited since its stamp.
|
|
244
|
+
- id: plan_track
|
|
245
|
+
action: execute_script
|
|
246
|
+
script: { ref: actions }
|
|
247
|
+
subcommand: plan-check
|
|
248
|
+
args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{plan_doc}}"]
|
|
249
|
+
outputs:
|
|
250
|
+
- { name: track_state, from: "$.state" }
|
|
251
|
+
- { name: derived_version, from: "$.derived_version" }
|
|
252
|
+
- { name: current_version, from: "$.current_version" }
|
|
253
|
+
- { name: changed_csv, from: "$.changed_csv" }
|
|
254
|
+
- { name: changed_summary, from: "$.changed_summary" }
|
|
255
|
+
|
|
256
|
+
# 5a. Hand-edit fence (LOUD, and unblockable by design): a hash mismatch means the
|
|
257
|
+
# document moved outside this workflow. Refuse — unless accept_manual:true asks
|
|
258
|
+
# the update to fold the edits in, which keeps their intent, reconciles them
|
|
259
|
+
# with the spec, and re-baselines at the stamp.
|
|
260
|
+
- id: manual_flag_edited
|
|
261
|
+
condition: "{{track_state}} == edited"
|
|
262
|
+
action: set
|
|
263
|
+
vars: { manual_ok: "false" }
|
|
264
|
+
- id: manual_flag_edited_behind
|
|
265
|
+
condition: "{{track_state}} == edited_behind"
|
|
266
|
+
action: set
|
|
267
|
+
vars: { manual_ok: "false" }
|
|
268
|
+
- id: manual_accept
|
|
269
|
+
condition: "{{ACCEPT_MANUAL}} == true"
|
|
270
|
+
action: set
|
|
271
|
+
vars: { manual_ok: "true" }
|
|
272
|
+
- id: manual_fence
|
|
273
|
+
action: verify_context
|
|
274
|
+
require_true: manual_ok
|
|
275
|
+
on_false: stop
|
|
276
|
+
on_false_message: "backend-plan.md was hand-edited since its last stamp (tracking state: {{track_state}}). Re-run plan with accept_manual:true to fold the edits in (kept, reconciled with the spec, recorded in the ledgers, re-baselined) — or restore the document to its stamped state."
|
|
277
|
+
|
|
278
|
+
# 5b. Idempotence: an update with nothing to update stops here, friendly.
|
|
279
|
+
- id: clean_flag
|
|
280
|
+
condition: "{{track_state}} == clean"
|
|
281
|
+
action: set
|
|
282
|
+
vars: { plan_stale: "false" }
|
|
283
|
+
- id: clean_stop
|
|
284
|
+
action: verify_context
|
|
285
|
+
require_true: plan_stale
|
|
286
|
+
on_false: stop
|
|
287
|
+
on_false_message: "backend-plan.md is in step with spec {{current_version}} — nothing to update."
|
|
288
|
+
|
|
289
|
+
# 5b. THE CODE. The plan is an architectural confirmation, so it must know what the
|
|
290
|
+
# feature already looks like on disk: concept-paths resolves the feature's OKF
|
|
291
|
+
# concept to the source files realising it, and the authoring steps declare
|
|
292
|
+
# `tools: Read` so they can open the ones that matter. A PATH LIST, not inlined
|
|
293
|
+
# content — a real codebase does not fit a payload (E60). on_error: continue,
|
|
294
|
+
# because a greenfield feature has no concept doc and must not be blocked by it:
|
|
295
|
+
# source_count 0 is the greenfield signal the tasks read.
|
|
296
|
+
- id: concept_paths
|
|
297
|
+
action: execute_script
|
|
298
|
+
on_error: continue
|
|
299
|
+
script: { ref: xref }
|
|
300
|
+
subcommand: concept-paths
|
|
301
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
302
|
+
outputs:
|
|
303
|
+
- { name: source_csv, from: "$.csv" }
|
|
304
|
+
- { name: source_count, from: "$.count" }
|
|
305
|
+
- { name: okf_concept, from: "$.concept" }
|
|
306
|
+
|
|
307
|
+
# 6. Pre-execution hooks. (Phase keys keep their historical names — they are the
|
|
308
|
+
# hooks CONFIG contract, orthogonal to the command's name.)
|
|
309
|
+
- id: resolve_pre_hooks
|
|
310
|
+
action: execute_script
|
|
311
|
+
script: { ref: actions }
|
|
312
|
+
subcommand: resolve-hooks
|
|
313
|
+
args: ["--phase", "before_design"]
|
|
314
|
+
outputs:
|
|
315
|
+
- { name: before_hooks, from: "$.hooks" }
|
|
316
|
+
- id: dispatch_pre_hooks
|
|
317
|
+
action: dispatch_hooks
|
|
318
|
+
hooks_from_variable: before_hooks
|
|
319
|
+
|
|
320
|
+
# ══════════════════ BOOTSTRAP (GREENFIELD) — author the master ══════════════
|
|
321
|
+
# Author the whole HLD in one pass: all four layers as sections, tech-agnostic,
|
|
322
|
+
# traced to the spec. build_context REPLACES the per-request envelope; sticky
|
|
323
|
+
# standards stay prepended. The ENGINE writes plan_doc; plan-stamp then baselines it.
|
|
324
|
+
- id: ctx_plan
|
|
325
|
+
condition: "{{plan_mode}} == bootstrap_gf"
|
|
326
|
+
action: build_context
|
|
327
|
+
context:
|
|
328
|
+
- { path: "{{plan_doc}}" } # the copied template = section structure
|
|
329
|
+
- { path: "{{spec_file}}" } # the source of truth (FR/BR/CN/DC/IC/SC)
|
|
330
|
+
- { path: .appweaver/DESIGN-FORMAT.md } # the HLD contract
|
|
331
|
+
- { path: "{{feature_dir}}/interfaces.md", optional: true } # the published surface — cite these IF- ids in §3; absent on a first run
|
|
332
|
+
- { path: "{{okf_concept}}", optional: true } # the feature's OKF concept: use-case log + source map
|
|
333
|
+
- { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain (domain enters via the spec)
|
|
334
|
+
- id: gen_plan
|
|
335
|
+
condition: "{{plan_mode}} == bootstrap_gf"
|
|
336
|
+
action: agent_author
|
|
337
|
+
tools: Read # the current code arrives as {{source_csv}} paths, not inlined
|
|
338
|
+
output_target: { type: file, path: "{{plan_doc}}" }
|
|
339
|
+
task: |
|
|
340
|
+
Author the MASTER service plan (the target backend-plan.md in context) for this
|
|
341
|
+
aggregate by filling out the template, based on the feature spec + the loaded
|
|
342
|
+
technical/architecture standards. This is the MEDIATOR between the spec and the
|
|
343
|
+
generated code. Guidance: {{USER_INPUT}}
|
|
344
|
+
Rules:
|
|
345
|
+
- THE SPEC IS THE ONLY DOMAIN INPUT. You have the spec + technical standards, and
|
|
346
|
+
NO domain rule files. The domain arrives as spec ids only — realise them, and
|
|
347
|
+
invent no domain rule the spec omits (an unavoidable new concept → §7 DA-n).
|
|
348
|
+
- THE CODE IS THE WHAT-EXISTS AUTHORITY, and it is a DIFFERENT authority from the
|
|
349
|
+
spec. {{source_count}} source file(s) realise this feature today; read the
|
|
350
|
+
ones that bear on the structure you are deciding, and ADJUST this plan to the
|
|
351
|
+
shape that is actually there rather than to an idealised one. An empty list is
|
|
352
|
+
greenfield — design from a clean slate and say so. The code NEVER overrides the
|
|
353
|
+
spec on behaviour and may not introduce a domain rule the spec omits: code that
|
|
354
|
+
contradicts the spec is reported (§7 DA-n) and designed to the SPEC, never
|
|
355
|
+
quietly blessed. A deliberate divergence from the as-built shape is a §8 WA-n
|
|
356
|
+
with its revisit trigger.
|
|
357
|
+
{{source_csv}}
|
|
358
|
+
- THE PUBLISHED SURFACE IS CITED, NOT RESTATED. Every endpoint, published event
|
|
359
|
+
and externally consumed contract belongs to `interfaces.md`, the feature's
|
|
360
|
+
append-only interface log — the anchor /appweaver plan-ui and the FE story
|
|
361
|
+
designs read. §3 here is the INDEX: cite the IF- id of an interface the log
|
|
362
|
+
already carries (it is in context on an update run), and for one being
|
|
363
|
+
published for the FIRST time write a one-line description with `(this run)` in
|
|
364
|
+
the id cell. NEVER invent an IF- number — the script is the only id authority,
|
|
365
|
+
and a later step publishes what you describe. Changing a published interface
|
|
366
|
+
means a SUPERSEDING entry, never an edit to an existing one.
|
|
367
|
+
- Cover the four layers as sections: Domain (§1), Application (§2), API (§3),
|
|
368
|
+
Infrastructure (§4). Keep the template's section order and headings.
|
|
369
|
+
- The DOMAIN layer (§1) MUST follow spec.md EXACTLY: entities & value objects
|
|
370
|
+
are the spec's Key Entities (SAME names), invariants are the spec's Business
|
|
371
|
+
Rules (BR-nnn — cite each), the aggregate boundary is the spec's Aggregate
|
|
372
|
+
Boundary, and states are the lifecycle the spec describes. Elaborate with
|
|
373
|
+
domain events / state machines / behaviour signatures ONLY to realise spec
|
|
374
|
+
behaviour; do NOT invent or contradict. Any unavoidable new concept goes in
|
|
375
|
+
§7 as a DA-n assumption.
|
|
376
|
+
- REALISE THE LIFTED CONSTRAINTS (each cites its spec id):
|
|
377
|
+
• Constraints & NFRs (CN-) → structural ones (immutability, append-only audit,
|
|
378
|
+
no-hard-delete) as §1 invariants/events; retention/performance/audit as §5
|
|
379
|
+
Cross-Cutting rows or §4 persistence.
|
|
380
|
+
• Data Classification (DC-) → §5 data-protection + the §2/§3 projection surface
|
|
381
|
+
(what MUST NOT be exposed in API/logs/traces).
|
|
382
|
+
• Integration Contracts (IC-) → §4.2 External integrations (one port/adapter row
|
|
383
|
+
per IC-), realising the obligation the spec named.
|
|
384
|
+
- CONSTITUTION-MANDATED CROSS-CUTTING (from the loaded technical constitution, NOT
|
|
385
|
+
the spec): a NON-NEGOTIABLE technical principle you must apply that has NO
|
|
386
|
+
per-feature spec id. APPLY it (it is non-negotiable), and because it is not
|
|
387
|
+
spec-derived, LOG it as a §7 DA-n whose basis cites the mandating principle
|
|
388
|
+
("per Const. §<n>"); set the corresponding §5 row's "Serves" to that DA-n. Do
|
|
389
|
+
NOT fabricate a spec CN- for it, and do NOT leave it uncited — an uncited
|
|
390
|
+
non-spec element reads as spec over-reach at the analyze gate.
|
|
391
|
+
- TECHNOLOGY-AGNOSTIC: express behaviour as signatures + pre/post/invariant/
|
|
392
|
+
raises pseudocode and contracts as field shapes — NEVER language code. Use
|
|
393
|
+
conceptual types. The stack is bound only at `implement` time. Do NOT include
|
|
394
|
+
code-generation mechanics or the layering/dependency rule — those are
|
|
395
|
+
solution-wide rules applied in `implement` / the constitution, not per feature.
|
|
396
|
+
- Every plan element traces UP to a spec id (FR-/BR-/CN-/DC-/IC-/SC-); the whole
|
|
397
|
+
spec traces DOWN — fill §6 Traceability completely, with a row for EVERY
|
|
398
|
+
CN-/DC-/IC- so no lifted constraint is silently dropped. NO STORY IDS ANYWHERE:
|
|
399
|
+
this plan traces to requirements, which is what keeps it stable while the
|
|
400
|
+
backlog churns.
|
|
401
|
+
- Fill the two ledgers: §7 Assumptions (DA-n, to clarify later) and §8
|
|
402
|
+
Workarounds (WA-n, to clear later). Empty → "None (as of v1.0)".
|
|
403
|
+
- Physical persistence schema is OUT OF SCOPE (defer to the technical-data-model).
|
|
404
|
+
- LEAVE the template's initial Change History row as it stands and write NO
|
|
405
|
+
version anywhere — plan-stamp owns the **Version** header and every row.
|
|
406
|
+
- Omit a LAYER section only if the aggregate genuinely has nothing there
|
|
407
|
+
(state why); never write "N/A". No complete code anywhere.
|
|
408
|
+
|
|
409
|
+
# ═══════ BOOTSTRAP (BROWNFIELD) — code-derived template, NEW feature, no adopt ═══
|
|
410
|
+
# Same contract as the greenfield bootstrap (tech-agnostic HLD, spec is the only
|
|
411
|
+
# domain input, full §Traceability + the two ledgers), with ONE difference: the plan
|
|
412
|
+
# STRUCTURE follows the code-derived brownfield template + the project's AS-BUILT
|
|
413
|
+
# architecture, not the fixed four layers — so a new feature added to an existing
|
|
414
|
+
# repo is consistent with the code already there. Runs only when onboard generated
|
|
415
|
+
# the code-derived design-template override (plan_mode == bootstrap_bf).
|
|
416
|
+
- id: ctx_plan_brownfield
|
|
417
|
+
condition: "{{plan_mode}} == bootstrap_bf"
|
|
418
|
+
action: build_context
|
|
419
|
+
context:
|
|
420
|
+
- { path: "{{plan_doc}}" } # the scaffolded BROWNFIELD template = code-derived section structure
|
|
421
|
+
- { path: "{{spec_file}}" } # the source of truth (FR/BR/CN/DC/IC/SC)
|
|
422
|
+
- { path: .appweaver/DESIGN-FORMAT.md } # the HLD contract (see its Brownfield variant note)
|
|
423
|
+
- { path: "{{feature_dir}}/interfaces.md", optional: true } # the published surface — cite these IF- ids in §3; absent on a first run
|
|
424
|
+
- { path: "{{okf_concept}}", optional: true } # the feature's OKF concept: use-case log + source map
|
|
425
|
+
- { path: knowledge/architecture/overview.md, optional: true } # the project's AS-BUILT architecture (onboard) — align to it
|
|
426
|
+
- { path: knowledge/architecture/platform.md, optional: true } # the as-built technology base / cross-cutting reality
|
|
427
|
+
- { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain (domain enters via the spec)
|
|
428
|
+
- id: gen_plan_brownfield
|
|
429
|
+
condition: "{{plan_mode}} == bootstrap_bf"
|
|
430
|
+
action: agent_author
|
|
431
|
+
tools: Read # the current code arrives as {{source_csv}} paths, not inlined
|
|
432
|
+
output_target: { type: file, path: "{{plan_doc}}" }
|
|
433
|
+
task: |
|
|
434
|
+
Author the MASTER service plan (the target backend-plan.md in context — it is the
|
|
435
|
+
code-derived BROWNFIELD template) for this NEW feature being added to an EXISTING
|
|
436
|
+
codebase, by filling the template from the spec + the loaded technical/architecture
|
|
437
|
+
standards. It is the MEDIATOR between the spec and the code `implement` will
|
|
438
|
+
generate. Guidance: {{USER_INPUT}}
|
|
439
|
+
Rules:
|
|
440
|
+
- THE SPEC IS THE ONLY DOMAIN INPUT. You have the spec + technical standards + the
|
|
441
|
+
project's as-built architecture references, and NO domain rule files. The domain
|
|
442
|
+
arrives as spec ids only — realise them, invent no domain rule the spec omits (an
|
|
443
|
+
unavoidable new concept → a §5 DA-n assumption).
|
|
444
|
+
- THE CODE IS THE WHAT-EXISTS AUTHORITY, and it is a DIFFERENT authority from the
|
|
445
|
+
spec. {{source_count}} source file(s) realise this feature today; read the
|
|
446
|
+
ones that bear on the structure you are deciding, and ADJUST this plan to the
|
|
447
|
+
shape that is actually there rather than to an idealised one. An empty list is
|
|
448
|
+
greenfield — design from a clean slate and say so. The code NEVER overrides the
|
|
449
|
+
spec on behaviour and may not introduce a domain rule the spec omits: code that
|
|
450
|
+
contradicts the spec is reported (§5 DA-n) and designed to the SPEC, never
|
|
451
|
+
quietly blessed. A deliberate divergence from the as-built shape is a §6 WA-n
|
|
452
|
+
with its revisit trigger.
|
|
453
|
+
{{source_csv}}
|
|
454
|
+
- THE PUBLISHED SURFACE IS CITED, NOT RESTATED. Every endpoint, published event
|
|
455
|
+
and externally consumed contract belongs to `interfaces.md`, the feature's
|
|
456
|
+
append-only interface log — the anchor /appweaver plan-ui and the FE story
|
|
457
|
+
designs read. the EXTERNAL-facing §2 subsection here is the INDEX: cite the IF- id of an interface the log
|
|
458
|
+
already carries (it is in context on an update run), and for one being
|
|
459
|
+
published for the FIRST time write a one-line description with `(this run)` in
|
|
460
|
+
the id cell. NEVER invent an IF- number — the script is the only id authority,
|
|
461
|
+
and a later step publishes what you describe. Changing a published interface
|
|
462
|
+
means a SUPERSEDING entry, never an edit to an existing one.
|
|
463
|
+
- STRUCTURE FOLLOWS THE TEMPLATE + THE PROJECT'S AS-BUILT ARCHITECTURE, NOT the four
|
|
464
|
+
greenfield layers. Keep the template's own section order and headings (§0 Overview
|
|
465
|
+
+ ubiquitous language; §1 Architecture Context & Domain Model; §2 Component &
|
|
466
|
+
Behaviour Design; §3 Cross-Cutting; §4 Traceability; §5 Assumptions; §6
|
|
467
|
+
Deviations). Fill §1.1 (As-Built Architecture) by ALIGNING this feature to the
|
|
468
|
+
project's existing style/patterns/folders — read knowledge/architecture/
|
|
469
|
+
overview.md + platform.md (in context) and MATCH them, so the new feature slots
|
|
470
|
+
into the codebase as-built. Create ONE §2 subsection per structural axis THIS
|
|
471
|
+
feature actually needs (its use-cases, endpoints/UI, persistence, external
|
|
472
|
+
adapters, jobs) — titled after the project's OWN module/folder conventions, not a
|
|
473
|
+
fixed Domain/Application/API/Infrastructure layering. A component that fits no
|
|
474
|
+
textbook layer gets its own section; a layer the feature does not need does NOT
|
|
475
|
+
appear.
|
|
476
|
+
- KEEP THE NEUTRAL ANCHORS (the template pins them): §1.2 is the domain/data MODEL
|
|
477
|
+
and MUST follow spec.md EXACTLY — entities & value objects are the spec's Key
|
|
478
|
+
Entities (SAME names), invariants are the spec's Business Rules (BR-nnn, cite
|
|
479
|
+
each), the boundary is the spec's Aggregate Boundary, states are its lifecycle;
|
|
480
|
+
cross-slice references-by-id live here. One §2 subsection is the EXTERNAL
|
|
481
|
+
INTEGRATIONS table (its heading contains "Integration") — one port/adapter row
|
|
482
|
+
per IC-, realising the obligation the spec named.
|
|
483
|
+
- REALISE THE LIFTED CONSTRAINTS (each cites its spec id): CN- → structural ones
|
|
484
|
+
(immutability, append-only audit, no-hard-delete) as §1.2 invariants/events;
|
|
485
|
+
retention/performance/audit as §2 persistence or §3 Cross-Cutting rows. DC- → §3
|
|
486
|
+
data-protection + the projection surface (what MUST NOT be exposed in
|
|
487
|
+
API/logs/traces).
|
|
488
|
+
- CONSTITUTION-MANDATED CROSS-CUTTING (from the loaded technical constitution, NOT
|
|
489
|
+
the spec): a NON-NEGOTIABLE technical principle with no per-feature spec id —
|
|
490
|
+
APPLY it, and LOG it as a §5 DA-n whose basis cites the mandating principle
|
|
491
|
+
("per Const. §<n>"); do NOT fabricate a spec CN- for it, and do NOT leave it
|
|
492
|
+
uncited.
|
|
493
|
+
- TECHNOLOGY-AGNOSTIC: signatures + pre/post/invariant/raises pseudocode and
|
|
494
|
+
field-shape contracts — NEVER language code. Conceptual types only; the stack is
|
|
495
|
+
bound at `implement` time. Do NOT include code-generation mechanics or the
|
|
496
|
+
layering/dependency rule (those are solution-wide / constitution rules).
|
|
497
|
+
- Every plan element traces UP to a spec id; fill §4 Traceability completely (a
|
|
498
|
+
row for EVERY FR/BR/CN/DC/IC/SC so no lifted constraint is dropped). NO STORY
|
|
499
|
+
IDS ANYWHERE — this plan traces to requirements. Fill the
|
|
500
|
+
§5 Assumptions (DA-n) and §6 Deviations (WA-n) ledgers — "None (as of v1.0)" if
|
|
501
|
+
empty. §6 records only genuine deviations, not "organised unlike a reference
|
|
502
|
+
architecture" (that is the as-built shape, §1.1).
|
|
503
|
+
- LEAVE the template's initial Change History row as it stands and write NO
|
|
504
|
+
version anywhere — plan-stamp owns the **Version** header and every row.
|
|
505
|
+
Omit a section only if the feature genuinely has nothing there (state why); no
|
|
506
|
+
complete code anywhere.
|
|
507
|
+
|
|
508
|
+
# ══════════════════ UPDATE — conform the existing plan to the moved spec ═════
|
|
509
|
+
# The work list is COMPUTED, never guessed: the ledger rows above this plan's
|
|
510
|
+
# derivation stamp name exactly the ids to conform. Everything else stays
|
|
511
|
+
# byte-identical — the analyze gate reads an unrelated "improvement" as drift.
|
|
512
|
+
- id: ctx_plan_update
|
|
513
|
+
condition: "{{plan_mode}} == update"
|
|
514
|
+
action: build_context
|
|
515
|
+
context:
|
|
516
|
+
- { path: "{{plan_doc}}" } # the CURRENT plan — the document being conformed
|
|
517
|
+
- { path: "{{spec_file}}" } # the source of truth at {{current_version}}
|
|
518
|
+
- { path: .appweaver/DESIGN-FORMAT.md } # the HLD contract
|
|
519
|
+
- { path: "{{feature_dir}}/interfaces.md", optional: true } # the published surface — cite these IF- ids in §3; absent on a first run
|
|
520
|
+
- { path: "{{okf_concept}}", optional: true } # the feature's OKF concept: use-case log + source map
|
|
521
|
+
- { path: knowledge/architecture/overview.md, optional: true } # brownfield repos: the as-built architecture
|
|
522
|
+
- { path: knowledge/architecture/platform.md, optional: true }
|
|
523
|
+
- { path: .appweaver/memory/constitution.technical.md, optional: true }
|
|
524
|
+
- id: gen_plan_update
|
|
525
|
+
condition: "{{plan_mode}} == update"
|
|
526
|
+
action: agent_author
|
|
527
|
+
tools: Read # the current code arrives as {{source_csv}} paths, not inlined
|
|
528
|
+
output_target: { type: file, path: "{{plan_doc}}" }
|
|
529
|
+
task: |
|
|
530
|
+
UPDATE the master service plan (the target backend-plan.md in context) so it
|
|
531
|
+
conforms to the CURRENT spec. Tracking state: {{track_state}}. The spec moved
|
|
532
|
+
from {{derived_version}} (this plan's derivation) to {{current_version}}; the
|
|
533
|
+
ledger rows above the derivation name what moved:
|
|
534
|
+
ids: {{changed_csv}}
|
|
535
|
+
changes: {{changed_summary}}
|
|
536
|
+
Guidance: {{USER_INPUT}}
|
|
537
|
+
Rules:
|
|
538
|
+
- SCOPE IS THE COMPUTED WORK LIST. Rework ONLY the sections and rows that realise
|
|
539
|
+
the ids above — their §1 invariants/entities, the §2/§3 elements serving them,
|
|
540
|
+
the §4.2 integration rows, the §5 cross-cutting rows and the §6 traceability
|
|
541
|
+
rows. Every other line stays BYTE-IDENTICAL; an unrelated "improvement" is
|
|
542
|
+
drift the analyze gate will fail. EXCEPTION — tracking state `untracked`: this
|
|
543
|
+
document has no baseline (a migrated legacy design.md), so reconcile the WHOLE
|
|
544
|
+
plan to the spec once; the stamp then baselines it.
|
|
545
|
+
- accept_manual: {{ACCEPT_MANUAL}}. When true, the document carries hand edits
|
|
546
|
+
that were deliberately accepted: KEEP their intent, reconcile them with the
|
|
547
|
+
spec, and record each kept deviation honestly — a new concept as a §7 DA-n, a
|
|
548
|
+
deliberate shortcut as a §8 WA-n. Never silently revert a hand edit.
|
|
549
|
+
- AN ID THE SPEC NO LONGER DEFINES LOSES ITS REALISATION: remove the elements
|
|
550
|
+
that existed only to serve it (and their §6 rows). An id whose wording moved
|
|
551
|
+
is re-realised from the spec's CURRENT text — the spec is the wording.
|
|
552
|
+
- THE SPEC IS THE ONLY DOMAIN INPUT, same as at bootstrap: realise ids, invent
|
|
553
|
+
nothing; an unavoidable new concept is a §7 DA-n.
|
|
554
|
+
- THE CODE IS THE WHAT-EXISTS AUTHORITY, and it is a DIFFERENT authority from the
|
|
555
|
+
spec. {{source_count}} source file(s) realise this feature today; re-read the
|
|
556
|
+
ones the moved ids touch (code state is read FRESH every run, never assumed), and ADJUST this plan to the
|
|
557
|
+
shape that is actually there rather than to an idealised one. An empty list is
|
|
558
|
+
greenfield — design from a clean slate and say so. The code NEVER overrides the
|
|
559
|
+
spec on behaviour and may not introduce a domain rule the spec omits: code that
|
|
560
|
+
contradicts the spec is reported (§7 DA-n) and designed to the SPEC, never
|
|
561
|
+
quietly blessed. A deliberate divergence from the as-built shape is a §8 WA-n
|
|
562
|
+
with its revisit trigger.
|
|
563
|
+
{{source_csv}}
|
|
564
|
+
- THE PUBLISHED SURFACE IS CITED, NOT RESTATED. `interfaces.md` is in context:
|
|
565
|
+
§3 cites the IF- id of every interface the log already carries. A moved id that
|
|
566
|
+
CHANGES a published interface is described in §3 with `(this run)` in the id
|
|
567
|
+
cell — a later step publishes it as a SUPERSEDING entry, and the next update
|
|
568
|
+
cites the new id. Never invent an IF- number and never edit the log by hand.
|
|
569
|
+
- PRESERVE THE LEDGERS: §7 Assumptions, §8 Workarounds and §9 Change History
|
|
570
|
+
keep every existing entry and their numbering — append, never rewrite. Write
|
|
571
|
+
NO version numbers anywhere and ADD NO Change History row: the stamp step
|
|
572
|
+
appends the row and moves the **Version** header.
|
|
573
|
+
- §6 TRACEABILITY STAYS COMPLETE both ways — every CN-/DC-/IC- still has a row
|
|
574
|
+
after the update; no element without a spec reason.
|
|
575
|
+
- TECHNOLOGY-AGNOSTIC, no complete code, same contract as at bootstrap.
|
|
576
|
+
|
|
577
|
+
# 7. STAMP — the only writer of the plan's version identity, and the ledger-survival
|
|
578
|
+
# check: it REFUSES (no baseline written) if the update dropped the Change History
|
|
579
|
+
# section, so a lost ledger can never be silently baselined over.
|
|
580
|
+
- id: stamp_bootstrap
|
|
581
|
+
condition: "{{plan_mode}} != update"
|
|
582
|
+
action: execute_script
|
|
583
|
+
script: { ref: actions }
|
|
584
|
+
subcommand: plan-stamp
|
|
585
|
+
args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{plan_doc}}", "--no-row"]
|
|
586
|
+
outputs:
|
|
587
|
+
- { name: stamp_ok, from: "$.ok" }
|
|
588
|
+
- { name: stamp_error, from: "$.error" }
|
|
589
|
+
- { name: current_version, from: "$.version" }
|
|
590
|
+
- id: stamp_update
|
|
591
|
+
condition: "{{plan_mode}} == update"
|
|
592
|
+
action: execute_script
|
|
593
|
+
script: { ref: actions }
|
|
594
|
+
subcommand: plan-stamp
|
|
595
|
+
args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{plan_doc}}", "--ids", "{{changed_csv}}", "--note", "Conformed to spec {{current_version}}"]
|
|
596
|
+
outputs:
|
|
597
|
+
- { name: stamp_ok, from: "$.ok" }
|
|
598
|
+
- { name: stamp_error, from: "$.error" }
|
|
599
|
+
- id: stamp_fence
|
|
600
|
+
action: verify_context
|
|
601
|
+
require_true: stamp_ok
|
|
602
|
+
on_false: stop
|
|
603
|
+
on_false_message: "plan-stamp refused: {{stamp_error}} — backend-plan.md has been written but NOT baselined. Restore the missing ledger section, then re-run plan (or plan-stamp) to record the baseline."
|
|
604
|
+
|
|
605
|
+
# ═══════════ PUBLISH THE SURFACE — the interface log (payload-only) ════════
|
|
606
|
+
# §3 of the plan describes this feature's published surface; this pair turns what it
|
|
607
|
+
# newly describes into APPEND-ONLY log entries. Separate steps so the script stays the
|
|
608
|
+
# only id authority: entries arrive as IF-NEW blocks and log-append assigns IF-nnn,
|
|
609
|
+
# the date and status: proposed. It runs AFTER the stamp — the log lives in its own
|
|
610
|
+
# file, so appending to it cannot invalidate the baseline just recorded.
|
|
611
|
+
#
|
|
612
|
+
# This is the BE→FE contract anchor: /appweaver plan-ui and the FE story designs read
|
|
613
|
+
# the latest entry per id. A changed interface is published as a SUPERSEDING entry,
|
|
614
|
+
# never an edit — which is why the log is append-only and the plan cites ids.
|
|
615
|
+
- id: ctx_if
|
|
616
|
+
action: build_context
|
|
617
|
+
vars:
|
|
618
|
+
derived_version: "{{current_version}}"
|
|
619
|
+
context:
|
|
620
|
+
- { path: "{{plan_doc}}" } # the plan as just written — §3 is the source
|
|
621
|
+
- { path: "{{feature_dir}}/interfaces.md", optional: true } # existing entries — what a change supersedes
|
|
622
|
+
- id: gen_if
|
|
623
|
+
action: agent_author
|
|
624
|
+
output_target: { type: file, path: "{{feature_dir}}/.aw-if-new.tmp.md" }
|
|
625
|
+
task: |
|
|
626
|
+
From the plan document in context, extract this feature's PUBLISHED SURFACE into
|
|
627
|
+
formal interface-log entries: the endpoints, published events and externally
|
|
628
|
+
consumed contracts its API/published-surface section describes.
|
|
629
|
+
|
|
630
|
+
Output ONLY entries that are NOT ALREADY in the interface log in context, or that
|
|
631
|
+
SUPERSEDE an entry there because the plan now describes them differently. An
|
|
632
|
+
interface the log already carries unchanged is NOT re-published. If there is
|
|
633
|
+
nothing to publish — no published surface, or every one already logged unchanged
|
|
634
|
+
— output exactly:
|
|
635
|
+
none
|
|
636
|
+
|
|
637
|
+
Otherwise output ONE block per interface, nothing else:
|
|
638
|
+
|
|
639
|
+
## IF-NEW — <METHOD /route, or the event name>
|
|
640
|
+
|
|
641
|
+
serves: <the FR-/BR- ids from the spec this interface exists for>
|
|
642
|
+
supersedes: <the IF-nnn id it replaces from the log in context, or none>
|
|
643
|
+
|
|
644
|
+
<the contract: request/response or payload shapes, auth, status codes and the
|
|
645
|
+
error contract — shapes, not implementations>
|
|
646
|
+
|
|
647
|
+
Every block starts with "## IF-NEW — " exactly: the script assigns the real ids,
|
|
648
|
+
the date and status: proposed. Describe only what the plan actually contains —
|
|
649
|
+
invent no interface the plan does not describe.
|
|
650
|
+
- id: append_if
|
|
651
|
+
action: execute_script
|
|
652
|
+
on_error: continue # a malformed extraction is reported LOUDLY but must not undo a
|
|
653
|
+
# plan that already landed and baselined: the temp file survives
|
|
654
|
+
# for a hand-run of log-append; if_error carries the refusal
|
|
655
|
+
script: { ref: actions }
|
|
656
|
+
subcommand: log-append
|
|
657
|
+
args: ["--feature-dir", "{{feature_dir}}", "--log", "interfaces", "--entries-file", "{{feature_dir}}/.aw-if-new.tmp.md"]
|
|
658
|
+
outputs:
|
|
659
|
+
- { name: if_ok, from: "$.ok" }
|
|
660
|
+
- { name: if_error, from: "$.error" }
|
|
661
|
+
- { name: if_appended, from: "$.appended" }
|
|
662
|
+
- { name: if_ids_csv, from: "$.ids_csv" }
|
|
663
|
+
- { name: if_unknown_csv, from: "$.unknown_ids_csv" }
|
|
664
|
+
- { name: if_badsup_csv, from: "$.bad_supersedes_csv" }
|
|
665
|
+
|
|
666
|
+
# 8. Post-execution hooks.
|
|
667
|
+
- id: resolve_post_hooks
|
|
668
|
+
action: execute_script
|
|
669
|
+
script: { ref: actions }
|
|
670
|
+
subcommand: resolve-hooks
|
|
671
|
+
args: ["--phase", "after_design"]
|
|
672
|
+
outputs:
|
|
673
|
+
- { name: after_hooks, from: "$.hooks" }
|
|
674
|
+
- id: dispatch_post_hooks
|
|
675
|
+
action: dispatch_hooks
|
|
676
|
+
hooks_from_variable: after_hooks
|
|
677
|
+
|
|
678
|
+
outputs:
|
|
679
|
+
feature_dir: "{{feature_dir}}"
|
|
680
|
+
plan_doc: "{{plan_doc}}"
|
|
681
|
+
plan_mode: "{{plan_mode}}"
|
|
682
|
+
migrated_legacy_design: "{{plan_migrated}}"
|
|
683
|
+
derived_from_spec: "{{current_version}}"
|
|
684
|
+
conformed_ids: "{{changed_csv}}"
|
|
685
|
+
interfaces_published: "{{if_ids_csv}}"
|
|
686
|
+
interfaces_error: "{{if_error}}"
|
|
687
|
+
source_files_read: "{{source_count}}"
|
|
688
|
+
next: "Gate this write: /appweaver analyze-design (spec ↔ plan conformance) — after EVERY bootstrap and EVERY update. Then the backlog and the stories: /appweaver sync-stories, then /appweaver story-plan <id>, which READS this plan and refuses to design against one the spec has moved past. {{fe_next}} READ THE FIELDS ABOVE FIRST: interfaces_published are the IF- ids this run appended to interfaces.md as status:proposed — cite them in the plan's API section on the next update, and never edit the log by hand; interfaces_error non-empty means the extraction was refused and .aw-if-new.tmp.md is still beside the plan for a hand-run of log-append, so THIS RUN PUBLISHED NOTHING; source_files_read 0 means no code was found for this feature, so the plan was written greenfield. NOTE while the pipeline is mid-rename: migrated_legacy_design=true means design.md is now backend-plan.md, and stages not yet rebuilt for the rename (implement, the delta chain) still look for design.md — rebuild or point them at backend-plan.md before relying on them."
|