aw-spec-cli 0.1.14 → 0.1.16

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