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.
Files changed (30) hide show
  1. package/assets/.appweaver/STACK-PROFILES.md +1 -1
  2. package/assets/.appweaver/orchestrator/orchestrator.py +5 -0
  3. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +10 -3
  4. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +38 -11
  5. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +2 -1
  6. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +607 -123
  7. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +625 -66
  8. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +407 -134
  9. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +452 -143
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +78 -14
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +597 -0
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +688 -0
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +3 -3
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +23 -23
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +945 -0
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +1040 -0
  17. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +2069 -28
  18. package/assets/.appweaver/scripts/bash/aw-xref.sh +2 -1
  19. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +1973 -13
  20. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +2 -1
  21. package/assets/.appweaver/scripts/python/aw-xref.py +246 -20
  22. package/assets/.appweaver/templates/design-template.md +88 -54
  23. package/assets/.appweaver/templates/spec-template.md +12 -10
  24. package/assets/.appweaver/templates/story-template.md +48 -8
  25. package/assets/.appweaver/templates/ui-design-template.md +71 -39
  26. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +19 -7
  27. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +35 -26
  28. package/package.json +1 -1
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-design-ui.orch.yaml +0 -386
  30. 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."