aw-spec-cli 0.1.15 → 0.1.17

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 (69) hide show
  1. package/assets/.appweaver/DESIGN-FORMAT.md +18 -18
  2. package/assets/.appweaver/ORCH-DIALECT.md +1 -3
  3. package/assets/.appweaver/RULE-PARTITION.md +29 -13
  4. package/assets/.appweaver/STACK-PROFILES.md +10 -9
  5. package/assets/.appweaver/orchestrator/.state/smoke.state.json +32 -0
  6. package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +4 -1
  7. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +251 -0
  8. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +104 -35
  9. package/assets/.appweaver/orchestrator/orchestrator.py +5 -4
  10. package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +94 -36
  11. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +1 -1
  12. package/assets/.appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md +115 -0
  13. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +2 -9
  14. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +16 -40
  15. package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +38 -17
  16. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +8 -8
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +10 -11
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +673 -0
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +535 -0
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +643 -0
  21. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +78 -626
  22. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
  23. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +7 -7
  24. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +16 -14
  25. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +2 -2
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +243 -144
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +882 -464
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -4
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +32 -91
  30. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
  31. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +832 -477
  32. package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +2 -2
  33. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +132 -0
  34. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +38 -5
  35. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +3 -3
  36. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +271 -892
  37. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1096 -2911
  38. package/assets/.appweaver/scripts/bash/aw-xref.sh +1 -2
  39. package/assets/.appweaver/scripts/bash/common.sh +47 -0
  40. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +936 -2766
  41. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
  42. package/assets/.appweaver/scripts/python/aw-payload-parity.py +174 -0
  43. package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
  44. package/assets/.appweaver/scripts/python/aw-xref.py +185 -292
  45. package/assets/.appweaver/templates/constitution-ui-template.md +1 -1
  46. package/assets/.appweaver/templates/delta-design-template.md +120 -30
  47. package/assets/.appweaver/templates/delta-domain-model-template.md +81 -0
  48. package/assets/.appweaver/templates/delta-spec-template.md +43 -17
  49. package/assets/.appweaver/templates/design-template.md +68 -98
  50. package/assets/.appweaver/templates/domain-model-template.md +130 -0
  51. package/assets/.appweaver/templates/okf-concept-template.md +2 -2
  52. package/assets/.appweaver/templates/source-map-template.md +3 -3
  53. package/assets/.appweaver/templates/spec-template.md +100 -122
  54. package/assets/.appweaver/templates/ui-delta-design-template.md +159 -0
  55. package/assets/.appweaver/templates/ui-design-template.md +46 -75
  56. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +7 -18
  57. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +34 -39
  58. package/assets/guardrails/requirement-spec.txt +2 -2
  59. package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
  60. package/package.json +1 -1
  61. package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +0 -105
  62. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
  63. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -749
  64. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
  65. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -540
  66. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -597
  67. package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -945
  68. package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1040
  69. package/assets/.appweaver/templates/story-template.md +0 -147
@@ -1,88 +1,99 @@
1
1
  # =============================================================================
2
- # aw-spec-plan (orchestrator) — the spec-anchored Plan phase (service lane)
2
+ # aw-spec-plan (orchestrator) — the spec-anchored Plan phase, BOTH LANES
3
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.)
4
+ # Authors the feature's LANE PLANS — the tech-agnostic HLDs that MEDIATE between the
5
+ # spec and the code:
6
+ # • specs/<Context>/<Feature>/backend-plan.md when a BACKEND lane is declared
7
+ # • specs/<Context>/<Feature>/frontend-plan.md when a FRONTEND lane is declared
8
+ # `implement` GENERATES each lane's code from ITS PLAN ALONE (model-driven, not a
9
+ # deterministic extract) — it never reads the spec, so whatever a plan leaves out is
10
+ # missing from the code. See .appweaver/DESIGN-FORMAT.md + the design-template /
11
+ # ui-design-template.
14
12
  #
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.
13
+ # THE LANES COME FROM stacks.json, NOT FROM A FLAG. select-stack is the sole writer of
14
+ # the lane declaration, and this phase simply plans whichever lanes are there: backend
15
+ # only, frontend only, or both. A lane declared `(none)` produces no plan and no failure.
16
+ # With NO lane declared at all the phase stops and names select-stack — a plan written
17
+ # against no declared stack invents its own.
21
18
  #
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.
19
+ # ONE COMMAND, TWO LANES, TWO RULE SETS. The lanes take their rules from different
20
+ # headings of tech-rule-bindings (`## /aw-spec-plan` for the service, `## /aw-spec-plan-ui`
21
+ # for the frontend), and the frontend set is loaded ONLY when that lane is declared — so
22
+ # a backend-only project sees exactly the rules it did before. In a two-lane project both
23
+ # sets are sticky for the whole run; the lane boundary is held by the prompts instead,
24
+ # and §5 Contract Binding is where it is stated.
27
25
  #
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.
26
+ # RENAMED FROM `design`. design.md → backend-plan.md, design.ui.md → frontend-plan.md,
27
+ # and the former separate `design-ui` command is FOLDED IN here. The scaffold MIGRATES a
28
+ # legacy document by renaming it, so an existing feature moves to the plan names on its
29
+ # next run instead of needing a flag day.
33
30
  #
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.
31
+ # TEMPLATE-DRIVEN AUTHORING (greenfield-safe). Each lane's authoring step follows the
32
+ # structure of whichever template is ACTIVE for it, chosen deterministically:
33
+ # • GREENFIELD (default) — the kit's templates: the four-layer service template
34
+ # (Domain·Application·API·Infra) and the experience template (§0–§9).
35
+ # • BROWNFIELD — when /appweaver onboard has GENERATED a code-derived override
36
+ # (design-template / ui-design-template; override_active), a NEW feature added to an
37
+ # existing repo is authored with the as-built structure instead, so it slots into the
38
+ # codebase already there.
39
+ # The variants are separate steps selected by one `*_mode` var per lane, so greenfield
40
+ # authoring never sees the brownfield guidance and vice-versa.
44
41
  #
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.
42
+ # IT READS THE DOMAIN MODEL AND NEVER WRITES IT. The aggregate root, its entities, its
43
+ # events and its invariants (BR-n) live in specs/<Context>/domain-model.md, which
44
+ # `specify` authors. This phase records where each invariant is ENFORCED, not what it
45
+ # says — the model is spec-tier, the plan is realisation-tier.
56
46
  #
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.
47
+ # AUTHORS only — the gates are SEPARATE stages. IT ASKS WHAT IT IS PLANNING, exactly as
48
+ # `implement` asks what it is building: list what is open, pick one, activate it. The
49
+ # difference is that this phase can also be pointed at the feature's OWN spec, so the menu
50
+ # carries two synthetic rows beside the open changes:
51
+ # • main → author the MASTER plan for each declared lane, from spec.md + the context's
52
+ # domain model. On a feature that has never been planned this is a BOOTSTRAP and
53
+ # nothing is asked. On one that HAS masters it is a RE-DERIVATION, and it is
54
+ # CONFIRMED first — the run stops on a decline, leaving them untouched.
55
+ # Next: /appweaver analyze-plan (spec↔plan gate) → implement.
56
+ # • CR-… → author THAT change's delta plans ONLY (delta-plan module). The masters are
57
+ # left UNTOUCHED, and finalize-delta does not merge them either: it merges the
58
+ # change's spec and model halves, and a master plan is RE-DERIVED from those — which
59
+ # is what the main row above does. The change stays OPEN.
60
+ # Next: analyze-plan-delta → implement (which asks which change) → build →
61
+ # analyze-slice-delta → finalize-delta → analyze-slice → close-delta.
62
+ # • new → open a plan-only change first, then plan it: a refactor or architectural
63
+ # decision where the realisation of EXISTING spec ids moves with no spec delta. Its
64
+ # delta-spec stays an unfilled stub, and that stub is the signal downstream.
65
+ # The argument doubles as the answer — `plan main`, `plan CR-…` and `plan new` all skip
66
+ # the prompt — and only the WHOLE argument counts, so guidance prose never selects.
63
67
  #
64
- # AUTHORS only — the gates are SEPARATE stages: /appweaver analyze-design proves
65
- # spec ↔ plan conformance after every bootstrap AND every update.
68
+ # THE MENU APPEARS ONLY ONCE THERE IS A MASTER. With none yet there is nothing to choose:
69
+ # a delta needs a master to be a delta OF, and so does a plan-only change. A first run
70
+ # therefore authors the masters with no prompt at all. A change already open on such a
71
+ # feature waits for the next run, when the menu will offer it.
66
72
  #
67
- # Launch: /appweaver plan [guidance] (accept_manual:true folds hand edits in)
73
+ # The forward paths are explicit:
74
+ # NEW : specify → plan → analyze-plan → implement
75
+ # CHANGE : specify (delta-spec + model delta) → plan (delta plans, per change) →
76
+ # analyze-plan-delta → implement (the delta plans) → build →
77
+ # finalize-delta → analyze-slice → close-delta
78
+ #
79
+ # Launch: /appweaver plan [guidance]
80
+ # /appweaver plan main (re-plan the masters, no prompt)
81
+ # /appweaver plan CR-20260910-… (plan THAT open change, no prompt)
82
+ # /appweaver plan new (open a plan-only change and plan it)
68
83
  # Prereq: /appweaver specify
69
84
  # =============================================================================
70
85
 
71
86
  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
87
+ description: "Author the feature's lane plans from the spec and the bounded context's domain model — backend-plan.md when a backend lane is declared, frontend-plan.md when a frontend lane is, both when both are. Lanes come from .appweaver/stacks.json, so one command covers backend-only, frontend-only and full-stack projects; the former separate design-ui command is folded in. It ASKS what it is planning, the way implement asks what it is building: the master (`main`), one of the OPEN CHANGES, or a new plan-only change for a refactor with no spec delta — the argument doubles as the answer, and the menu appears only once a master exists, so a first run bootstraps with no prompt. `main` on a feature that already has masters RE-DERIVES them from the merged spec.md + domain-model.md — the operation finalize-delta's contract assumes when it merges a change's spec and model halves and no lane plan — and is confirmed before it rewrites anything. A change instead gets delta-backend-plan.md / delta-frontend-plan.md inside changes/<CR-id>/ while the masters stay untouched, written against the effective model (the master model, the change's own model delta, and the pending model deltas of LOWER waves, which are its predecessors) and against the feature's real source files from its OKF concept. Master authoring is template-driven: the kit's templates by default, or the code-derived as-built structure when onboard installed an override. Authoring only — a master gates at analyze-plan; a change ships through the CR chain."
88
+ version: 5.0.0
74
89
  agent_agnostic: true
75
90
  dialect: aw-spec.orchestrator/v1
76
91
 
77
92
  inputs:
78
93
  user_input:
79
- description: Optional plan guidance (architectural constraints, trade-off preferences).
94
+ description: "Optional plan guidance (architectural constraints, trade-off preferences), OR the answer to the one question this phase asks: `main` to re-plan the masters, a bare CR id to plan that open change, `new` to open a plan-only change. Only the whole argument counts as an answer, so guidance prose is never mistaken for one."
80
95
  required: false
81
96
  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
97
 
87
98
  scripts:
88
99
  actions:
@@ -94,10 +105,13 @@ scripts:
94
105
 
95
106
  # Sticky standards seed = the UNIVERSAL always-on rule only (ai-rules). The TECHNICAL/
96
107
  # 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).
108
+ # guardrails/projects/<project>/tech-rule-bindings.md by the tech-select module (see the
109
+ # tech_select steps below), exactly as the DOMAIN rule files are loaded by domain-select.
110
+ # Per the rule-partition this phase loads NO domain rule file — the domain reaches the
111
+ # plan as spec ids (BR-/CN-/DC-/IC-) and as the context's domain model. The technology
112
+ # base itself arrives via constitution.technical.md / constitution.ui.md (referred to
113
+ # directly, as-is — NOT mapped). DDD stays in specify; coding-standards in implement.
114
+ # (missing files skipped at assembly time)
101
115
  standards:
102
116
  - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
103
117
 
@@ -105,71 +119,98 @@ vars:
105
119
  lanes_declared: "false"
106
120
  be_declared: "false"
107
121
  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: ""
122
+ be_lane_id: ""
123
+ fe_lane_id: ""
124
+ active_change: ""
125
+ has_change: "false"
126
+ version: ""
127
+ # Per-lane authoring state. Each lane resolves its own mode; a lane that is not
128
+ # declared never resolves one and its steps never run.
129
+ be_mode: "skip" # skip | bootstrap_gf | bootstrap_bf | update
130
+ fe_mode: "skip" # skip | bootstrap_gf | bootstrap_bf | update
131
+ be_brownfield: "false" # plan-scaffold.override_active — onboard installed the code-derived template
132
+ fe_brownfield: "false"
133
+ be_created: "false" # plan-scaffold.created — true ⇒ the document was ABSENT ⇒ bootstrap
134
+ fe_created: "false"
135
+ be_migrated: "false" # a legacy design.md was renamed into backend-plan.md this run
136
+ fe_migrated: "false"
137
+ backend_plan: ""
138
+ frontend_plan: ""
139
+ # PER-LANE BRANCH FLAGS. A lane is either NEW (declared, and its master was just
140
+ # scaffolded ⇒ bootstrap it) or EXISTING (declared, and its master was already there ⇒
141
+ # its change goes through the delta module). The engine has no compound conditions, so
142
+ # each AND is computed as one ordered assignment.
143
+ #
144
+ # BOTH CAN BE TRUE IN ONE RUN, and that case is real: declaring a frontend lane on a
145
+ # feature that already has a backend plan bootstraps the frontend master (from the whole
146
+ # spec — nothing is missing from it) while the backend half of the change is a delta.
147
+ be_new: "false"
148
+ be_existing: "false"
149
+ fe_new: "false"
150
+ fe_existing: "false"
151
+ be_update: "false" # this lane has a master AND the re-derivation was confirmed
152
+ fe_update: "false"
153
+ # WHAT THIS RUN IS POINTED AT, and how it was answered. plan_target is the menu's
154
+ # answer; master_exists is what makes the menu appear at all.
155
+ master_exists: "false" # a DECLARED lane's master plan was already on disk
156
+ plan_target: "main" # main | delta — the answer to the one question this phase asks
157
+ plan_choice: "" # the raw selection (a label): the MAIN row, a change, or the NEW row
158
+ main_label: "" # cr-list --include-main's exact label, to test the selection against
159
+ new_label: "" # cr-list --include-new's
160
+ open_new: "false" # the NEW row was chosen ⇒ open a plan-only change before planning it
161
+ has_changes: "false"
162
+ rederive_needed: "false" # main was chosen on a feature that already has masters
163
+ rederive: "false" # …and the rewrite was confirmed
164
+ plan_mode: "bootstrap" # bootstrap | rederive | delta — reported, not branched on
165
+ domain_model: ""
166
+ domain_model_exists: "false"
167
+ source_files: [] # the feature's real source files, from its OKF concept
168
+ source_count: "0"
132
169
 
133
170
  workflow:
134
171
 
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.
172
+ # 0. WHICH LANES EXIST. lane-resolve reads the declaration only — it does NOT check the
173
+ # source path on disk, unlike the lane-gate module, because a plan is authored BEFORE
174
+ # the code exists. That is the point of planning.
151
175
  - id: lane_resolve
152
176
  action: execute_script
153
177
  on_error: continue # never crash raw on an undeclared lane — the gate below explains it
154
178
  script: { ref: actions }
155
179
  subcommand: lane-resolve
156
180
  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
181
+ - { name: lanes_declared, from: "$.declared" }
182
+ - { name: be_declared, from: "$.backend_declared" }
183
+ - { name: be_lane_id, from: "$.backend_lane_id" }
184
+ - { name: be_language, from: "$.backend_language" }
185
+ - { name: be_root, from: "$.backend_source_path" }
186
+ - { name: fe_declared, from: "$.frontend_declared" }
187
+ - { name: fe_lane_id, from: "$.frontend_lane_id" }
188
+ - { name: fe_language, from: "$.frontend_language" }
189
+ - { name: fe_root, from: "$.frontend_source_path" }
190
+
191
+ - id: require_lane
165
192
  action: verify_context
166
- require_true: be_declared
193
+ require_true: lanes_declared
167
194
  on_false: stop
168
- on_false_message: "{{be_gate_msg}}"
169
- - id: fe_handoff
195
+ on_false_message: "No lane is declared in .appweaver/stacks.json, so there is nothing to plan. Run /appweaver select-stack and declare the backend stack, the frontend stack, or both — plus each one's source path — then re-run plan. The declaration is what tells this phase which documents to write; a plan written against no declared stack invents its own."
196
+
197
+ # 1. TECHNICAL rule files → sticky standards (configuration, not hardcoded).
198
+ # The service lane's set is loaded under `## /aw-spec-plan`; the frontend lane's under
199
+ # `## /aw-spec-plan-ui`, and ONLY when that lane is declared — so a backend-only
200
+ # project's standards are exactly what they were before the two commands merged.
201
+ # No blocking gate: an unconfigured project soft-continues (the constitutions still
202
+ # carry each lane's ratified base).
203
+ - id: tech_select
204
+ condition: "{{be_declared}} == true"
205
+ action: include
206
+ module: .appweaver/orchestrator/modules/tech-select.orch.yaml
207
+ with: { command: aw-spec-plan }
208
+
209
+ - id: tech_select_ui
170
210
  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." }
211
+ action: include
212
+ module: .appweaver/orchestrator/modules/tech-select.orch.yaml
213
+ with: { command: aw-spec-plan-ui }
173
214
 
174
215
  # 2. Resolve the current feature and require its spec.
175
216
  - id: feature_paths
@@ -178,10 +219,25 @@ workflow:
178
219
  script: { ref: actions }
179
220
  subcommand: feature-paths
180
221
  outputs:
222
+ - { name: feature, from: "$.feature" }
181
223
  - { name: feature_dir, from: "$.feature_dir" }
182
224
  - { name: spec_file, from: "$.spec_file" }
183
225
  - { name: spec_exists, from: "$.spec_exists" }
184
226
 
227
+ # The BOUNDED CONTEXT's domain model — where the aggregate root, its entities and its
228
+ # invariants (BR-n) live. The spec cites them by id; this phase records where each is
229
+ # ENFORCED, so the model has to be in context or the instruction to "follow it exactly"
230
+ # has nothing to follow.
231
+ - id: context_paths
232
+ action: execute_script
233
+ on_error: continue
234
+ script: { ref: actions }
235
+ subcommand: context-paths
236
+ outputs:
237
+ - { name: context, from: "$.context" }
238
+ - { name: domain_model, from: "$.domain_model" }
239
+ - { name: domain_model_exists, from: "$.domain_model_exists" }
240
+
185
241
  - id: require_feature
186
242
  action: verify_context
187
243
  require_non_empty: feature_dir
@@ -194,118 +250,246 @@ workflow:
194
250
  on_false: stop
195
251
  on_false_message: "No spec.md for the current feature. Run /appweaver specify first."
196
252
 
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
253
+ # 3. Read the open change state (set by specify). The delta path uses it to decide
254
+ # whether there is anything to plan at all.
255
+ - id: read_change
202
256
  action: execute_script
203
257
  script: { ref: actions }
204
- subcommand: spec-check
258
+ subcommand: version-read
205
259
  args: ["--feature-dir", "{{feature_dir}}"]
206
260
  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."
261
+ - { name: active_change, from: "$.active_change" }
262
+ - { name: has_change, from: "$.has_change" }
263
+ - { name: version, from: "$.version" }
214
264
 
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
265
+ # 4. THE FEATURE'S REAL SOURCE FILES, from its OKF concept. A plan over an existing
266
+ # codebase has to conform to the shape that is actually there; the paths arrive as a
267
+ # LIST and the authoring steps declare `tools: Read`, because a legacy tree does not
268
+ # fit in a payload. Read-only, exits 0 with paths=[] when there is no concept yet —
269
+ # source_count 0 IS the greenfield case, and the phase never blocks on it.
270
+ - id: concept_paths
271
+ action: execute_script
272
+ on_error: continue
273
+ script: { ref: xref }
274
+ subcommand: concept-paths
275
+ args: ["--feature-dir", "{{feature_dir}}"]
276
+ outputs:
277
+ - { name: source_files, from: "$.paths" }
278
+ - { name: source_count, from: "$.count" }
279
+
280
+ # 5. Scaffold each DECLARED lane's plan (idempotent — returns the existing path when it
281
+ # already exists, and RENAMES a legacy design.md / design.ui.md into the plan name).
282
+ - id: be_scaffold
283
+ condition: "{{be_declared}} == true"
219
284
  action: execute_script
220
285
  script: { ref: actions }
221
- subcommand: design-scaffold
286
+ subcommand: plan-scaffold
222
287
  args: ["--feature-dir", "{{feature_dir}}", "--lane", "backend"]
223
288
  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"
289
+ - { name: backend_plan, from: "$.plan_doc" }
290
+ - { name: be_brownfield, from: "$.override_active" } # true ⇒ onboard installed the code-derived template
291
+ - { name: be_created, from: "$.created" } # true ⇒ the document was ABSENT ⇒ bootstrap
292
+ - { name: be_migrated, from: "$.migrated" } # true ⇒ a legacy design.md was renamed into place
293
+
294
+ - id: fe_scaffold
295
+ condition: "{{fe_declared}} == true"
296
+ action: execute_script
297
+ script: { ref: actions }
298
+ subcommand: plan-scaffold
299
+ args: ["--feature-dir", "{{feature_dir}}", "--lane", "frontend"]
300
+ outputs:
301
+ - { name: frontend_plan, from: "$.plan_doc" }
302
+ - { name: fe_brownfield, from: "$.override_active" }
303
+ - { name: fe_created, from: "$.created" }
304
+ - { name: fe_migrated, from: "$.migrated" }
305
+
306
+ # 5a. Self-detect NEW-vs-EXISTING per lane, from each plan document's OWN existence
307
+ # (symmetric with specify's spec.md trigger). A scaffold reports created=true only
308
+ # when it had to CREATE the document, so created=false on a DECLARED lane means the
309
+ # plan pre-existed and that lane's change is a delta.
310
+ # A MIGRATED document counts as pre-existing: it is the same plan under its new
311
+ # name, and its content was authored before this run.
312
+ # Taking the lane's own `declared` value under the created/not-created condition is
313
+ # how the AND is expressed — an undeclared lane's flags stay false either way.
314
+ - id: be_flag_new
315
+ condition: "{{be_created}} == true"
234
316
  action: set
235
- vars: { plan_mode: "bootstrap_bf" }
236
- - id: mode_update
237
- condition: "{{plan_created}} == false"
317
+ vars: { be_new: "{{be_declared}}" }
318
+ - id: be_flag_existing
319
+ condition: "{{be_created}} == false"
238
320
  action: set
239
- vars: { plan_mode: "update" }
321
+ vars: { be_existing: "{{be_declared}}" }
322
+ - id: fe_flag_new
323
+ condition: "{{fe_created}} == true"
324
+ action: set
325
+ vars: { fe_new: "{{fe_declared}}" }
326
+ - id: fe_flag_existing
327
+ condition: "{{fe_created}} == false"
328
+ action: set
329
+ vars: { fe_existing: "{{fe_declared}}" }
240
330
 
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
331
+ # Either declared lane having a pre-existing master means this feature HAS BEEN PLANNED
332
+ # ONCE. That is the precondition for both of the things the menu below offers — a delta
333
+ # has to have a master to be a delta OF, and a re-derivation has to have a document to
334
+ # re-derive. The two raises are independent so neither can cancel the other.
335
+ - id: master_exists_backend
336
+ condition: "{{be_existing}} == true"
337
+ action: set
338
+ vars: { master_exists: "true" }
339
+ - id: master_exists_frontend
340
+ condition: "{{fe_existing}} == true"
341
+ action: set
342
+ vars: { master_exists: "true" }
343
+
344
+ # ═════════════ 5b. WHAT IS BEING PLANNED — ONE QUESTION, ASKED ONCE ═════════
345
+ # The same opening `implement` has: list what is open, ask which one, activate it. The
346
+ # difference is that this phase can also be pointed at the feature's OWN spec, so the
347
+ # menu carries two synthetic options alongside the open changes (cr-list --include-main
348
+ # / --include-new):
349
+ # main · re-plan the master from spec.md + domain-model.md
350
+ # CR-… · author THAT change's delta plans (one row per open change, by wave)
351
+ # new · open a plan-only change — a refactor or architectural decision with no spec
352
+ # delta. This is the path that used to be taken AUTOMATICALLY whenever a master
353
+ # existed and no change was open; it is now something you ask for, because
354
+ # "re-plan the master" and "open a refactor change" were never the same intent.
355
+ #
356
+ # ONLY WHEN THERE IS A MASTER. With no master yet there is nothing to choose: a delta
357
+ # needs one to be a delta of, and so does a plan-only change. So a first run authors the
358
+ # masters with no prompt at all, exactly as before — and if a change is already open on
359
+ # such a feature, it waits for the next run, when the menu will offer it.
360
+ - id: cr_list
361
+ condition: "{{master_exists}} == true"
245
362
  action: execute_script
246
363
  script: { ref: actions }
247
- subcommand: plan-check
248
- args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{plan_doc}}"]
364
+ subcommand: cr-list
365
+ args: ["--feature-dir", "{{feature_dir}}", "--preselect", "{{USER_INPUT}}", "--include-main", "--include-new"]
249
366
  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"
367
+ - { name: change_options, from: "$.select_options" }
368
+ - { name: matched_label, from: "$.matched_label" }
369
+ - { name: main_label, from: "$.main_label" }
370
+ - { name: new_label, from: "$.new_label" }
371
+ - { name: has_changes, from: "$.has_changes" }
372
+ - { name: change_count, from: "$.count" }
373
+
374
+ # The argument doubles as the answer: `plan main` and `plan CR-…` both resolve to a
375
+ # label in matched_label and skip the prompt. Only the WHOLE argument counts as one of
376
+ # the synthetic words, so ordinary guidance prose ("new caching layer") never selects.
377
+ - id: pick_target
378
+ condition: "{{master_exists}} == true"
379
+ action: user_select
380
+ prompt: "What is being planned? `main` re-plans the feature's master from its spec and domain model. A CR row authors that change's delta plans only, leaving the masters untouched — deltas sharing a wave are independent, and a higher wave is planned on top of the lower ones. `new` opens a plan-only change, for a refactor or architectural decision with no spec delta."
381
+ options_from_variable: change_options
382
+ preselect_from: matched_label
383
+ single_option_confirm: true
384
+ outputs:
385
+ - { name: plan_choice, from: selection }
386
+
387
+ # 5c. The answer, RAISED then KNOCKED DOWN. Anything that is not the MAIN row is a
388
+ # change — either an existing one or the NEW row, which is the same path plus a
389
+ # cr-setup.
390
+ #
391
+ # THE KNOCKDOWN IS NOT DECORATIVE. When the menu never ran, plan_choice and both
392
+ # labels are empty, and the engine's conditions compare RENDERED STRINGS: "" == ""
393
+ # is TRUE, so the NEW test fires on a feature that has no menu, no changes and no
394
+ # master. Without the knockdown a first `plan` run opens a stray plan-only change
395
+ # before authoring its first master. master_exists is exactly "the menu ran", so it
396
+ # is what restores the first-run defaults.
397
+ - id: target_delta
398
+ condition: "{{plan_choice}} != {{main_label}}"
262
399
  action: set
263
- vars: { manual_ok: "false" }
264
- - id: manual_flag_edited_behind
265
- condition: "{{track_state}} == edited_behind"
400
+ vars: { plan_target: "delta", plan_mode: "delta" }
401
+ - id: target_new
402
+ condition: "{{plan_choice}} == {{new_label}}"
266
403
  action: set
267
- vars: { manual_ok: "false" }
268
- - id: manual_accept
269
- condition: "{{ACCEPT_MANUAL}} == true"
404
+ vars: { open_new: "true" }
405
+ - id: target_no_menu
406
+ condition: "{{master_exists}} == false"
270
407
  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."
408
+ vars: { plan_target: "main", plan_mode: "bootstrap", open_new: "false" }
277
409
 
278
- # 5b. Idempotence: an update with nothing to update stops here, friendly.
279
- - id: clean_flag
280
- condition: "{{track_state}} == clean"
410
+ # ═════════ 5d. RE-DERIVING A MASTER IS CONFIRMED, NOT ASSUMED ══════════════
411
+ # `main` on a feature that has never been planned is a bootstrap: there is no document
412
+ # to lose, and nothing is asked. `main` on a feature that HAS one is a REWRITE of a
413
+ # long-lived, hand-edited document, so it is confirmed first and the run stops on a
414
+ # decline. The options are ordered no-first deliberately, against this kit's usual
415
+ # yes-first convention: an unattended run takes the first option, and the safe default
416
+ # for "rewrite the plan this team has been maintaining" is to decline and say so.
417
+ - id: rederive_needed_set
418
+ condition: "{{plan_target}} == main"
281
419
  action: set
282
- vars: { plan_stale: "false" }
283
- - id: clean_stop
420
+ vars: { rederive_needed: "{{master_exists}}" }
421
+
422
+ - id: confirm_rederive
423
+ condition: "{{rederive_needed}} == true"
424
+ action: ask
425
+ prompt: "This feature already has master plan(s). Re-derive them from the current spec.md and domain-model.md? Recorded architectural decisions (DA-n) and workarounds (WA-n) are carried forward, but any section the spec and model no longer support IS REWRITTEN — including hand edits. Answer no to stop without touching them."
426
+ options: ["no", "yes"]
427
+ answer_var: rederive_answer
428
+
429
+ - id: rederive_go
430
+ condition: "{{rederive_answer}} == yes"
431
+ action: set
432
+ vars: { rederive: "true", plan_mode: "rederive" }
433
+
434
+ - id: require_rederive_confirm
435
+ condition: "{{rederive_needed}} == true"
284
436
  action: verify_context
285
- require_true: plan_stale
437
+ require_true: rederive
286
438
  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" }
439
+ on_false_message: "Stopped — the master plans were left untouched. Re-run and answer yes to re-derive them, or pick a change instead (/appweaver plan CR-…) to author that change's delta plans without touching a master."
306
440
 
307
- # 6. Pre-execution hooks. (Phase keys keep their historical names — they are the
308
- # hooks CONFIG contract, orthogonal to the command's name.)
441
+ # 5e. Resolve each lane's authoring MODE. Four ordered assignments per lane: the
442
+ # greenfield default for a new lane, the code-derived override replacing it, a
443
+ # `skip` for any lane that is not new — which silences both bootstrap branches on
444
+ # the delta path without needing a compound condition — and finally `update` for a
445
+ # lane whose master exists on a CONFIRMED re-derivation. The raise-then-test pair is
446
+ # how "this lane has a master AND the re-derivation was confirmed" is expressed.
447
+ #
448
+ # A re-derivation has no greenfield/brownfield split: both bootstrap branches differ
449
+ # only in WHICH TEMPLATE supplies the section structure, and in update mode the
450
+ # structure is the existing document's own.
451
+ - id: be_mode_gf
452
+ condition: "{{be_new}} == true"
453
+ action: set
454
+ vars: { be_mode: "bootstrap_gf" }
455
+ - id: be_mode_bf
456
+ condition: "{{be_brownfield}} == true"
457
+ action: set
458
+ vars: { be_mode: "bootstrap_bf" }
459
+ - id: be_mode_skip
460
+ condition: "{{be_new}} == false"
461
+ action: set
462
+ vars: { be_mode: "skip" }
463
+ - id: be_update_raise
464
+ condition: "{{be_existing}} == true"
465
+ action: set
466
+ vars: { be_update: "{{rederive}}" }
467
+ - id: be_mode_update
468
+ condition: "{{be_update}} == true"
469
+ action: set
470
+ vars: { be_mode: "update" }
471
+ - id: fe_mode_gf
472
+ condition: "{{fe_new}} == true"
473
+ action: set
474
+ vars: { fe_mode: "bootstrap_gf" }
475
+ - id: fe_mode_bf
476
+ condition: "{{fe_brownfield}} == true"
477
+ action: set
478
+ vars: { fe_mode: "bootstrap_bf" }
479
+ - id: fe_mode_skip
480
+ condition: "{{fe_new}} == false"
481
+ action: set
482
+ vars: { fe_mode: "skip" }
483
+ - id: fe_update_raise
484
+ condition: "{{fe_existing}} == true"
485
+ action: set
486
+ vars: { fe_update: "{{rederive}}" }
487
+ - id: fe_mode_update
488
+ condition: "{{fe_update}} == true"
489
+ action: set
490
+ vars: { fe_mode: "update" }
491
+
492
+ # 6. Pre-execution hooks.
309
493
  - id: resolve_pre_hooks
310
494
  action: execute_script
311
495
  script: { ref: actions }
@@ -317,70 +501,61 @@ workflow:
317
501
  action: dispatch_hooks
318
502
  hooks_from_variable: before_hooks
319
503
 
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"
504
+ # ═══════════ BACKEND LANE — bootstrap, greenfield four-layer template ═══════
505
+ # Author the whole HLD in one pass: all four layers as sections, tech-agnostic, traced
506
+ # to the spec. build_context REPLACES the per-request envelope; sticky standards stay
507
+ # prepended. The ENGINE writes backend_plan.
508
+ - id: ctx_backend
509
+ condition: "{{be_mode}} == bootstrap_gf"
326
510
  action: build_context
327
511
  context:
328
- - { path: "{{plan_doc}}" } # the copied template = section structure
329
- - { path: "{{spec_file}}" } # the source of truth (FR/BR/CN/DC/IC/SC)
512
+ - { path: "{{backend_plan}}" } # the copied template = section structure
513
+ - { path: "{{spec_file}}" } # BEHAVIOUR: FR/CN/DC/IC/US/SC + cited BR ids
514
+ - { path: "{{domain_model}}", optional: true } # the CONTEXT's canonical model — entities, invariants, boundary
330
515
  - { 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"
516
+ - { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain (domain enters via the spec + model)
517
+ - id: gen_backend
518
+ condition: "{{be_mode}} == bootstrap_gf"
336
519
  action: agent_author
337
- tools: Read # the current code arrives as {{source_csv}} paths, not inlined
338
- output_target: { type: file, path: "{{plan_doc}}" }
520
+ tools: Read # the feature's EXISTING source arrives as {{source_files}} paths, not inlined
521
+ output_target: { type: file, path: "{{backend_plan}}" }
339
522
  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}}
523
+ Author the MASTER service-lane plan (the target backend-plan.md in context) for this
524
+ aggregate by filling out the template, based on the feature spec + the context's
525
+ domain model + the loaded technical/architecture standards. This is the MEDIATOR
526
+ between the spec and the generated code. Guidance: {{USER_INPUT}}
344
527
  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.
528
+ - THE SPEC AND THE DOMAIN MODEL ARE THE ONLY DOMAIN INPUTS. You have them plus the
529
+ technical standards, and NO domain rule files. Realise what they state, and invent
530
+ no domain rule they omit (an unavoidable new concept → §7 DA-n).
531
+ - THE DOMAIN LAYER (§1) MUST FOLLOW THE CONTEXT'S DOMAIN MODEL EXACTLY: entities &
532
+ value objects are its `## Aggregate:` section's entities (SAME names), invariants
533
+ are its BR-nnn (cite each, never restate), the aggregate boundary is its Boundary
534
+ block, and states are the lifecycle it describes. The model — not the spec — is
535
+ where those facts live; the spec cites them by id. This document records WHERE
536
+ each invariant is ENFORCED, never what it says. Elaborate with domain events /
537
+ state machines / behaviour signatures ONLY to realise spec behaviour; do NOT
538
+ invent or contradict. Any unavoidable new concept goes in §7 as a DA-n.
539
+ - THE CODE THAT EXISTS IS THE WHAT-IS AUTHORITY. {{source_count}} source file(s)
540
+ already realise this feature: {{source_files}}. READ them before planning and
541
+ CONFORM to the shape that is there — its module boundaries, its naming, its
542
+ seams. The spec stays the only authority on BEHAVIOUR; the code is the authority
543
+ on what the structure currently IS. With no source files this is a greenfield
544
+ feature and there is nothing to conform to.
367
545
  - Cover the four layers as sections: Domain (§1), Application (§2), API (§3),
368
546
  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
547
  - REALISE THE LIFTED CONSTRAINTS (each cites its spec id):
377
548
  • 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.
549
+ no-hard-delete) as §1 invariant enforcement/events; retention/performance/audit
550
+ as §5 Cross-Cutting rows or §4 persistence.
380
551
  • Data Classification (DC-) → §5 data-protection + the §2/§3 projection surface
381
552
  (what MUST NOT be exposed in API/logs/traces).
382
553
  • Integration Contracts (IC-) → §4.2 External integrations (one port/adapter row
383
554
  per IC-), realising the obligation the spec named.
555
+ - PUBLISH THE SURFACE THE FRONTEND LANE BINDS TO. §3 states each endpoint's contract
556
+ as field shapes. That section is the ONLY part of this document the frontend plan
557
+ may read; keep it complete and self-contained, because a contract the UI needs and
558
+ this document never states becomes a client-side workaround.
384
559
  - CONSTITUTION-MANDATED CROSS-CUTTING (from the loaded technical constitution, NOT
385
560
  the spec): a NON-NEGOTIABLE technical principle you must apply that has NO
386
561
  per-feature spec id. APPLY it (it is non-negotiable), and because it is not
@@ -393,73 +568,52 @@ workflow:
393
568
  conceptual types. The stack is bound only at `implement` time. Do NOT include
394
569
  code-generation mechanics or the layering/dependency rule — those are
395
570
  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.
571
+ - Every plan element traces UP to a spec id (FR-/BR-/CN-/DC-/IC-/US-/SC-); the
572
+ whole spec traces DOWN — fill §6 Traceability completely, with a row for EVERY
573
+ CN-/DC-/IC- so no lifted constraint is silently dropped.
401
574
  - Fill the two ledgers: §7 Assumptions (DA-n, to clarify later) and §8
402
575
  Workarounds (WA-n, to clear later). Empty → "None (as of v1.0)".
403
576
  - 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.
577
+ - This is v1.0: set the initial Change History row + keep US-1's mapping.
406
578
  - Omit a LAYER section only if the aggregate genuinely has nothing there
407
579
  (state why); never write "N/A". No complete code anywhere.
408
580
 
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"
581
+ # ═══ BACKEND LANE — bootstrap, brownfield code-derived template, NEW feature ═══
582
+ # Same contract as the greenfield bootstrap above (tech-agnostic HLD, spec + model are
583
+ # the only domain inputs, full §Traceability + the two ledgers), with ONE difference:
584
+ # the STRUCTURE follows the code-derived template + the project's AS-BUILT architecture,
585
+ # not the fixed four layers — so a new feature added to an existing repo is consistent
586
+ # with the code already there. Runs only when onboard generated the override.
587
+ - id: ctx_backend_brownfield
588
+ condition: "{{be_mode}} == bootstrap_bf"
418
589
  action: build_context
419
590
  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)
591
+ - { path: "{{backend_plan}}" } # the scaffolded BROWNFIELD template = code-derived section structure
592
+ - { path: "{{spec_file}}" } # BEHAVIOUR: FR/CN/DC/IC/US/SC + cited BR ids
593
+ - { path: "{{domain_model}}", optional: true } # the CONTEXT's canonical model — entities, invariants, boundary
422
594
  - { 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
595
  - { path: knowledge/architecture/overview.md, optional: true } # the project's AS-BUILT architecture (onboard) — align to it
426
596
  - { 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"
597
+ - { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain
598
+ - id: gen_backend_brownfield
599
+ condition: "{{be_mode}} == bootstrap_bf"
430
600
  action: agent_author
431
- tools: Read # the current code arrives as {{source_csv}} paths, not inlined
432
- output_target: { type: file, path: "{{plan_doc}}" }
601
+ tools: Read # the feature's EXISTING source arrives as {{source_files}} paths, not inlined
602
+ output_target: { type: file, path: "{{backend_plan}}" }
433
603
  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}}
604
+ Author the MASTER service-lane plan (the target backend-plan.md in context — it is
605
+ the code-derived BROWNFIELD template) for this NEW feature being added to an EXISTING
606
+ codebase, by filling the template from the spec + the context's domain model + the
607
+ loaded technical/architecture standards. It is the MEDIATOR between the spec and the
608
+ code `implement` will generate. Guidance: {{USER_INPUT}}
439
609
  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
610
+ - THE SPEC AND THE DOMAIN MODEL ARE THE ONLY DOMAIN INPUTS. You have them plus the
611
+ technical standards and the project's as-built architecture references, and NO
612
+ domain rule files. Realise what they state, invent no domain rule they omit (an
443
613
  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.
614
+ - THE CODE THAT EXISTS IS THE WHAT-IS AUTHORITY. {{source_count}} source file(s)
615
+ already realise this feature: {{source_files}}. READ them and CONFORM to the shape
616
+ that is there.
463
617
  - STRUCTURE FOLLOWS THE TEMPLATE + THE PROJECT'S AS-BUILT ARCHITECTURE, NOT the four
464
618
  greenfield layers. Keep the template's own section order and headings (§0 Overview
465
619
  + ubiquitous language; §1 Architecture Context & Domain Model; §2 Component &
@@ -474,17 +628,20 @@ workflow:
474
628
  textbook layer gets its own section; a layer the feature does not need does NOT
475
629
  appear.
476
630
  - 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.
631
+ and MUST follow the CONTEXT'S DOMAIN MODEL EXACTLY — entities & value objects are
632
+ its aggregate section's entities (SAME names), invariants are its BR-nnn (cite
633
+ each and record WHERE each is enforced), the boundary is its Boundary block,
634
+ states are its lifecycle; cross-slice references-by-id live here. One §2
635
+ subsection is the EXTERNAL INTEGRATIONS table (its heading contains "Integration")
636
+ — one port/adapter row per IC-, realising the obligation the spec named.
483
637
  - 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
638
+ (immutability, append-only audit, no-hard-delete) as §1.2 invariant enforcement /
639
+ events; retention/performance/audit as §2 persistence or §3 Cross-Cutting rows.
640
+ DC- → §3 data-protection + the projection surface (what MUST NOT be exposed in
487
641
  API/logs/traces).
642
+ - PUBLISH THE SURFACE THE FRONTEND LANE BINDS TO: the section carrying this feature's
643
+ endpoints states each contract as field shapes, completely, because it is the only
644
+ part of this document the frontend plan may read.
488
645
  - CONSTITUTION-MANDATED CROSS-CUTTING (from the loaded technical constitution, NOT
489
646
  the spec): a NON-NEGOTIABLE technical principle with no per-feature spec id —
490
647
  APPLY it, and LOG it as a §5 DA-n whose basis cites the mandating principle
@@ -495,175 +652,369 @@ workflow:
495
652
  bound at `implement` time. Do NOT include code-generation mechanics or the
496
653
  layering/dependency rule (those are solution-wide / constitution rules).
497
654
  - 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
655
+ row for EVERY FR/BR/CN/DC/IC/US/SC so no lifted constraint is dropped). Fill the
500
656
  §5 Assumptions (DA-n) and §6 Deviations (WA-n) ledgers — "None (as of v1.0)" if
501
657
  empty. §6 records only genuine deviations, not "organised unlike a reference
502
658
  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"
659
+ - This is v1.0: set the initial Change History row + keep US-1's mapping. Omit a
660
+ section only if the feature genuinely has nothing there (state why); no complete
661
+ code anywhere.
662
+
663
+ # ═══════════ BACKEND LANE — RE-DERIVE an existing master ════════════════════
664
+ # `main` on a feature that already has a master. This is the operation finalize-delta's
665
+ # contract has always assumed exists: it merges a change's SPEC and MODEL halves and
666
+ # merges no lane plan, on the stated grounds that "a master plan is DERIVED from those
667
+ # two and is re-derived from them". This is where that re-derivation happens.
668
+ #
669
+ # IT REVISES, IT DOES NOT REFILL. The document in context is the CURRENT master, not a
670
+ # blank template, and it is the section structure — which is why this branch has no
671
+ # greenfield/brownfield split. Content that the spec and model still support survives
672
+ # verbatim; content they no longer support is rewritten; content they now require and
673
+ # the document lacks is added. Nothing is preserved on the grounds that it is already
674
+ # written: an element with no spec id behind it is exactly what the analyze-plan gate
675
+ # calls over-reach.
676
+ - id: ctx_backend_update
677
+ condition: "{{be_mode}} == update"
514
678
  action: build_context
515
679
  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"
680
+ - { path: "{{backend_plan}}" } # the CURRENT master = what is being revised, and its section structure
681
+ - { path: "{{spec_file}}" } # BEHAVIOUR, carrying every change finalize-delta has merged
682
+ - { path: "{{domain_model}}", optional: true } # the CONTEXT's canonical model — entities, invariants, boundary
683
+ - { path: .appweaver/DESIGN-FORMAT.md } # the HLD contract
684
+ - { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY
685
+ - id: gen_backend_update
686
+ condition: "{{be_mode}} == update"
526
687
  action: agent_author
527
- tools: Read # the current code arrives as {{source_csv}} paths, not inlined
528
- output_target: { type: file, path: "{{plan_doc}}" }
688
+ tools: Read # the feature's EXISTING source arrives as {{source_files}} paths, not inlined
689
+ output_target: { type: file, path: "{{backend_plan}}" }
529
690
  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}}
691
+ RE-DERIVE the MASTER service-lane plan in context from the current spec + the
692
+ context's domain model. The document you are given is the plan as it stands today;
693
+ produce the plan this feature's spec and model call for NOW. Guidance: {{USER_INPUT}}
537
694
  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
695
+ - THIS IS A REVISION, NOT A RE-AUTHORING. Keep the existing document's section
696
+ structure, its wording, and every element the spec and model still support —
697
+ byte-for-byte where nothing requires a change. A re-derivation that rewrites
698
+ untouched sections destroys the review history of a document a team maintains.
699
+ - WHAT THE SPEC AND MODEL SAY NOW WINS. Every §4 Traceability row must resolve to a
700
+ spec id that STILL EXISTS; every §1 invariant to a BR-nnn the model STILL states.
701
+ A row whose id is gone is deleted along with what it justified. A spec id or model
702
+ invariant with NO plan element is a GAP — realise it, in the shape the rest of the
703
+ document already uses.
704
+ - THE DOMAIN LAYER (§1) MUST FOLLOW THE CONTEXT'S DOMAIN MODEL EXACTLY: entities &
705
+ value objects are its `## Aggregate:` section's entities (SAME names), invariants
706
+ are its BR-nnn (cite each, never restate), the aggregate boundary is its Boundary
707
+ block, and states are the lifecycle it describes. Where the model has MOVED since
708
+ this plan was written, the model is right and the plan is stale.
709
+ - CARRY THE LEDGERS FORWARD. §7 DA-n architectural decisions and the WA-n
710
+ workarounds are DECISIONS THIS TEAM MADE, not derivations from the spec — keep
711
+ every one, with its id and its basis, unless the thing it decided about is gone.
712
+ Never renumber a surviving entry: its id is cited elsewhere.
713
+ - THE CODE THAT EXISTS IS THE WHAT-IS AUTHORITY. {{source_count}} source file(s)
714
+ realise this feature: {{source_files}}. READ them, and where the plan and the code
715
+ disagree about STRUCTURE, the code is what is actually there — conform to it and
716
+ record the divergence rather than describing a shape nobody built. The spec stays
717
+ the only authority on BEHAVIOUR.
718
+ - TECHNOLOGY-AGNOSTIC, exactly as the master already is: signatures + pre/post/
719
+ invariant/raises pseudocode, contracts as field shapes, conceptual types. Never
720
+ language code; the stack binds at `implement` time.
721
+ - ADD ONE Change History row for this re-derivation, describing what MOVED and why
722
+ (the spec ids and BR-nnn that drove it). Do NOT invent a version number — the
723
+ version spine belongs to close-delta.
724
+
725
+ # ═══════════ FRONTEND LANE — bootstrap, greenfield experience template ═══════
726
+ # The experience twin of the service plan, and the MEDIATOR between spec.md and the
727
+ # frontend code. Its sections are the experience, not the four clean-architecture
728
+ # layers. UX- lives HERE and only here — the service plan excludes the spec's Experience
729
+ # Standards by contract, so a UX- that reaches neither plan is a dropped requirement.
730
+ - id: ctx_frontend
731
+ condition: "{{fe_mode}} == bootstrap_gf"
616
732
  action: build_context
617
- vars:
618
- derived_version: "{{current_version}}"
619
733
  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
734
+ - { path: "{{frontend_plan}}" } # the copied template = section structure
735
+ - { path: "{{spec_file}}" } # the source of truth (UX/US/FR/CN/DC/IC/SC)
736
+ - { path: "{{domain_model}}", optional: true } # the ubiquitous language this lane's labels and messages use
737
+ - { path: .appweaver/DESIGN-FORMAT.md } # the design-doc contract + its frontend lane variant
738
+ - { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's ratified UX-* base — optional: a lane may be declared before it is ratified
739
+ - { path: "{{backend_plan}}", optional: true } # §5 ONLY — the service's published surface, never its internals
740
+ - id: gen_frontend
741
+ condition: "{{fe_mode}} == bootstrap_gf"
623
742
  action: agent_author
624
- output_target: { type: file, path: "{{feature_dir}}/.aw-if-new.tmp.md" }
743
+ output_target: { type: file, path: "{{frontend_plan}}" }
625
744
  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.
745
+ Author the MASTER frontend-lane plan (the target frontend-plan.md in context) for
746
+ this feature's EXPERIENCE by filling out the template from the feature spec + the
747
+ loaded frontend standards. This is the frontend lane's MEDIATOR between the spec
748
+ and the components `implement` will generate. Guidance: {{USER_INPUT}}
749
+ Rules:
750
+ - THE SPEC IS THE ONLY DOMAIN INPUT. You have the spec + frontend standards, and
751
+ NO domain rule files. The domain arrives as spec ids only — realise them, and
752
+ invent no domain rule the spec omits (an unavoidable new concept → §8 DA-n).
753
+ Where the context's domain model is in context, use its UBIQUITOUS LANGUAGE
754
+ exactly — the same terms in labels, states and messages, no synonyms — and cite
755
+ its BR-nnn rather than restating a rule.
756
+ - Keep the template's section order and headings: §0 Design Overview, §1
757
+ Navigation & Screen Structure, §2 Client State, §3 Component Structure, §4
758
+ Interaction & Flow, §5 Contract Binding, §6 Cross-Cutting, §7 Traceability,
759
+ §8 Assumptions, §9 Workarounds, Change History.
760
+ - REALISE THE SPEC'S EXPERIENCE CONTENT:
761
+ • Experience Standards (UX-) are THIS document's to realise — the service plan
762
+ excludes them by contract, so a UX- with no home here is a DROPPED
763
+ requirement, not a tidy omission. Every UX- gets a §7 row.
764
+ • Experience Scope (actors · channels · device/browser/AT floors) bounds §1
765
+ and §6 — design for the floors the spec names and no more.
766
+ • Each User Story (US-) gets a screen home in §1 and a flow in §4. A US- with
767
+ no screen is a coverage failure.
768
+ • Interaction-state requirements are ordinary FR- ids (the EARS
769
+ unwanted-behaviour form). Each one gets a §4 row.
770
+ - REALISE THE LIFTED CONSTRAINTS, each citing its spec id:
771
+ • CN- → §6 Authorization surface and the §1 access conditions (what is hidden
772
+ vs disabled vs enforced server-side — the client never becomes the authority).
773
+ • DC- → §2 "what must NOT be held" and the §6 data-exposure row: what may not
774
+ reach the client, its logs, its storage or its URLs.
775
+ • IC-/SC- → §5 binding rows and the §6 performance row respectively.
776
+ - §4 INTERACTION & FLOW AND §6 CROSS-CUTTING ARE NEVER OMITTED, however thin the
777
+ feature. Loading, empty, error, permission-denied and in-flight states, plus
778
+ validation timing, focus placement and keyboard reachability, are where most UI
779
+ defects live. A state the UI can reach that the spec does not describe is a SPEC
780
+ gap: log it as a §8 DA-n and say it routes to `specify`; never invent the
781
+ requirement here.
782
+ - §5 IS THE LANE BOUNDARY. Bind against the service's PUBLISHED surface only —
783
+ the spec's IC- ids, and where the backend plan is in context, the API contracts
784
+ it states. CITE those contracts; do NOT restate, re-derive or contradict them,
785
+ and introduce NO server-side behaviour, persistence assumption or handler detail.
786
+ Anything the experience needs that the surface does not offer is a CONTRACT
787
+ DELTA raised back to the service lane under "Contract deltas needed" — never a
788
+ workaround in the client. Where no service plan is in context, bind from the
789
+ spec's IC- surface and record that as a §8 DA-n.
790
+ - CONSTITUTION-MANDATED CROSS-CUTTING (from the loaded UI constitution, NOT the
791
+ spec): a NON-NEGOTIABLE UX principle you must apply that has no per-feature spec
792
+ id. APPLY it, and because it is not spec-derived, LOG it as a §8 DA-n whose
793
+ basis cites the mandating principle ("per constitution.ui.md UX-<n>"); set the
794
+ corresponding §6 row's "Serves" to that DA-n. Do NOT fabricate a spec UX- for
795
+ it, and do NOT leave it uncited.
796
+ - TECHNOLOGY-AGNOSTIC: name no framework, router, state library, component library
797
+ or styling approach. Describe a component by its RESPONSIBILITY, its INPUTS as
798
+ field shapes, the EVENTS it raises and the STATES it can be in; describe routing
799
+ as a conceptual shape. The framework binds at implement time. Do NOT include
800
+ file placement, naming conventions or test mechanics — those are the UI
801
+ constitution's and `implement`'s, identical for every feature.
802
+ - The header's Lane field is `{{fe_lane_id}}`. That identifier is the ONLY thing you
803
+ are told about the lane: its framework and language are deliberately withheld,
804
+ because a plan that knows them starts naming them.
805
+ - STAY IN THE LANE'S SCOPE. This feature's screens only. The app shell, global
806
+ layout and the design system belong to no single feature: cite them under
807
+ "Screens this feature does NOT own" / "Reused from the design system", and do
808
+ not claim or redesign them here.
809
+ - §7 TRACEABILITY IS THE SPINE — fill it completely, both ways: a row for EVERY
810
+ UX-, US-, and for every CN-/DC-/IC-/SC- with an experience obligation, so no
811
+ lifted constraint is silently dropped; and no UI element without a spec reason.
812
+ - Fill the two ledgers: §8 Assumptions (DA-n, to clarify later) and §9 Workarounds
813
+ (WA-n, to clear later). Empty → "None (as of v1.0)".
814
+ - This is v1.0: set the initial Change History row. No component code anywhere.
629
815
 
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
816
+ # ═══ FRONTEND LANE — bootstrap, brownfield code-derived template, NEW feature ═══
817
+ # Same contract as above (technology-agnostic, spec is the only domain input, full §7
818
+ # and both ledgers), with ONE difference: the STRUCTURE follows the code-derived
819
+ # template's own composition axes and the lane's as-built conventions, so a new feature
820
+ # slots into the app already there instead of introducing a second convention.
821
+ - id: ctx_frontend_brownfield
822
+ condition: "{{fe_mode}} == bootstrap_bf"
823
+ action: build_context
824
+ context:
825
+ - { path: "{{frontend_plan}}" } # the scaffolded CODE-DERIVED template = as-built section structure
826
+ - { path: "{{spec_file}}" } # the source of truth (UX/US/FR/CN/DC/IC/SC)
827
+ - { path: "{{domain_model}}", optional: true } # the ubiquitous language this lane's labels and messages use
828
+ - { path: .appweaver/DESIGN-FORMAT.md } # the design-doc contract + its frontend lane variant
829
+ - { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's UX-* base, reversed by onboard from this same UI
830
+ - { path: knowledge/architecture/frontend.md, optional: true } # the as-built UI architecture + the SHARED surface no slice owns
831
+ - { path: "{{backend_plan}}", optional: true } # §5 ONLY — the service's published surface, never its internals
832
+ - id: gen_frontend_brownfield
833
+ condition: "{{fe_mode}} == bootstrap_bf"
834
+ action: agent_author
835
+ tools: Read # the lane's EXISTING source arrives as {{source_files}} paths, not inlined
836
+ output_target: { type: file, path: "{{frontend_plan}}" }
837
+ task: |
838
+ Author the MASTER frontend-lane plan (the target frontend-plan.md in context — it is
839
+ this project's CODE-DERIVED UI template) for this NEW feature being added to an
840
+ EXISTING frontend, by filling the template from the feature spec + the loaded
841
+ frontend standards. It is the MEDIATOR between the spec and the components
842
+ `implement` will generate. Guidance: {{USER_INPUT}}
843
+ Rules:
844
+ - THE SPEC IS THE ONLY DOMAIN INPUT. You have the spec + frontend standards + the
845
+ lane's as-built architecture reference, and NO domain rule files. The domain
846
+ arrives as spec ids only; an unavoidable new concept is an Assumption (DA-n).
847
+ Where the context's domain model is in context, use its ubiquitous language
848
+ exactly and cite its BR-nnn rather than restating a rule.
849
+ - THE CODE THAT EXISTS IS THE WHAT-IS AUTHORITY. {{source_count}} source file(s)
850
+ already realise this feature: {{source_files}}. READ them and MATCH the app's
851
+ screen organisation, its state and data-fetching placement and its styling
852
+ approach, so the feature slots in rather than introducing a second convention.
853
+ - STRUCTURE FOLLOWS THE TEMPLATE AND THE LANE AS-BUILT, not a fixed screen
854
+ taxonomy. Keep the template's own section order and headings, and create one
855
+ component subsection per composition axis THIS feature actually needs, titled
856
+ with the lane's OWN name for it.
857
+ - KEEP THE NEUTRAL ANCHORS the template pins: the navigation/screen inventory, the
858
+ client-state table, the interaction-and-flow section, the cross-cutting table
859
+ and the traceability spine exist in every UI plan, whatever the axes are
860
+ called. INTERACTION & FLOW and CROSS-CUTTING are NEVER omitted, however thin the
861
+ feature — loading, empty, error, permission-denied and in-flight states, focus
862
+ placement and keyboard reachability are where most UI defects live.
863
+ - REALISE THE SPEC'S EXPERIENCE CONTENT: every Experience Standard (UX-) gets a
864
+ traceability row (the service plan excludes them by contract, so this is their
865
+ only home), every User Story a screen and a flow, every interaction-state FR- a
866
+ flow row. Realise the lifted constraints citing their ids — CN- as the
867
+ authorization surface and access conditions, DC- as what may not reach the client
868
+ or its logs, storage and URLs, IC- as binding rows, SC- as the performance
869
+ decision.
870
+ - THE LANE BOUNDARY: bind against the service's PUBLISHED surface only — the
871
+ spec's IC- ids and, where the backend plan is in context, the API contracts it
872
+ states. Cite them; do not restate, re-derive or contradict them, and introduce no
873
+ server-side behaviour. What the experience needs and the surface lacks is a
874
+ contract delta raised back to the service lane, never a client workaround.
875
+ - CONSTITUTION-MANDATED CROSS-CUTTING with no per-feature spec id: apply it, and
876
+ log it as a DA-n citing the mandating principle ("per constitution.ui.md
877
+ UX-<n>"). Do not fabricate a spec id for it and do not leave it uncited.
878
+ - TECHNOLOGY-AGNOSTIC: name no framework, router, state library, component library
879
+ or styling approach — responsibilities, field shapes, events and states only.
880
+ Naming the lane's as-built CONVENTION is not naming a framework; naming the
881
+ library that implements it is. The framework binds at implement time.
882
+ - The header's Lane field is `{{fe_lane_id}}`. That identifier is the ONLY thing you
883
+ are told about the lane: its framework and language are deliberately withheld,
884
+ because a plan that knows them starts naming them.
885
+ - THE SHARED SURFACE (app shell, global layout, design system) belongs to no
886
+ feature. Cite it; do not copy it in and do not claim it here.
887
+ - Fill the traceability spine completely, both ways, and both ledgers —
888
+ Assumptions (DA-n) and Workarounds (WA-n), "None (as of v1.0)" when empty.
889
+ - This is v1.0: set the initial Change History row. No component code anywhere.
636
890
 
637
- Otherwise output ONE block per interface, nothing else:
891
+ # ═══════════ FRONTEND LANE — RE-DERIVE an existing master ═══════════════════
892
+ # The experience twin of the service re-derivation, and the same contract: revise the
893
+ # document that is there against the spec as it stands now, keep what still holds, and
894
+ # never refill from a template.
895
+ - id: ctx_frontend_update
896
+ condition: "{{fe_mode}} == update"
897
+ action: build_context
898
+ context:
899
+ - { path: "{{frontend_plan}}" } # the CURRENT master = what is being revised
900
+ - { path: "{{spec_file}}" } # UX/US/FR/CN/DC/IC/SC as they stand now
901
+ - { path: "{{domain_model}}", optional: true } # the ubiquitous language this lane's labels and messages use
902
+ - { path: .appweaver/DESIGN-FORMAT.md } # the design-doc contract + its frontend lane variant
903
+ - { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's ratified UX-* base
904
+ - { path: "{{backend_plan}}", optional: true } # §5 ONLY — the service's published surface, never its internals
905
+ - id: gen_frontend_update
906
+ condition: "{{fe_mode}} == update"
907
+ action: agent_author
908
+ tools: Read # the feature's EXISTING source arrives as {{source_files}} paths, not inlined
909
+ output_target: { type: file, path: "{{frontend_plan}}" }
910
+ task: |
911
+ RE-DERIVE the MASTER frontend-lane plan in context from the current spec. The
912
+ document you are given is the plan as it stands today; produce the plan this
913
+ feature's spec calls for NOW. Guidance: {{USER_INPUT}}
914
+ Rules:
915
+ - THIS IS A REVISION, NOT A RE-AUTHORING. Keep the existing document's section
916
+ structure and every element the spec still supports — byte-for-byte where nothing
917
+ requires a change.
918
+ - WHAT THE SPEC SAYS NOW WINS. Every §7 Traceability row must resolve to a spec id
919
+ that STILL EXISTS; a row whose id is gone is deleted along with what it justified.
920
+ Every UX- and every US- in the spec needs a home here — UX- lives in THIS document
921
+ and only here, so a UX- with no §7 row is a DROPPED requirement, and a US- with no
922
+ screen in §1 and flow in §4 is a coverage failure.
923
+ - CARRY THE LEDGERS FORWARD. §8 DA-n and §9 WA-n are decisions this team made, not
924
+ derivations from the spec — keep every one with its id and basis unless the thing
925
+ it decided about is gone, and never renumber a survivor.
926
+ - THE CODE THAT EXISTS IS THE WHAT-IS AUTHORITY for STRUCTURE. {{source_count}}
927
+ source file(s) realise this feature: {{source_files}}. Read them and conform to
928
+ the component boundaries and naming actually there.
929
+ - THE LANE BOUNDARY IS UNCHANGED: bind against the service's PUBLISHED surface only
930
+ — the spec's IC- ids and, where the backend plan is in context, the API contracts
931
+ it states. What the experience needs and the surface lacks is a contract delta
932
+ raised back to the service lane, never a client workaround.
933
+ - TECHNOLOGY-AGNOSTIC: no framework, router, state library, component library or
934
+ styling approach — responsibilities, field shapes, events and states only.
935
+ - ADD ONE Change History row for this re-derivation, describing what MOVED and why.
936
+ Do NOT invent a version number — the version spine belongs to close-delta.
638
937
 
639
- ## IF-NEW — <METHOD /route, or the event name>
938
+ # ══════════════ DELTA — resolve the chosen change, then delegate ════════════
939
+ # A change was chosen at 5b. Everything that RESOLVES it lives here, beside the question
940
+ # that asked it, so this command's opening reads the same as `implement`'s: list → ask →
941
+ # activate → paths. The delta-plan module below is then pure AUTHORING.
640
942
 
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>
943
+ # The NEW row: there is no change yet, so open one. A PLAN-ONLY change — the spec is
944
+ # unchanged and the realisation of EXISTING spec ids moves (a refactor, an architectural
945
+ # decision). cr-setup scaffolds the delta-spec, which stays an unfilled stub on this
946
+ # path — that stub IS the signal to every downstream gate that no requirement moved.
947
+ - id: open_change
948
+ condition: "{{open_new}} == true"
949
+ action: execute_script
950
+ script: { ref: actions }
951
+ subcommand: cr-setup
952
+ args: ["--feature-dir", "{{feature_dir}}", "--title", "{{USER_INPUT}}"]
953
+ outputs:
954
+ - { name: opened_cr, from: "$.cr_id" }
955
+
956
+ # Coalesce the two ways in. An existing change arrived as a LABEL ("CR-… · wave 2 ·
957
+ # title") and the scripts take its first token; a just-opened one arrives as a bare id.
958
+ - id: cr_choice_picked
959
+ condition: "{{plan_target}} == delta"
960
+ action: set
961
+ vars: { cr_choice: "{{plan_choice}}" }
962
+ - id: cr_choice_opened
963
+ condition: "{{open_new}} == true"
964
+ action: set
965
+ vars: { cr_choice: "{{opened_cr}}" }
643
966
 
644
- <the contract: request/response or payload shapes, auth, status codes and the
645
- error contract — shapes, not implementations>
967
+ # Make the chosen change the ACTIVE one. Every stage after this still resolves the
968
+ # change through active_version.json.active_change, so without this a plan authored for
969
+ # CR-B would be gated, implemented and merged as CR-C.
970
+ - id: activate
971
+ condition: "{{plan_target}} == delta"
972
+ action: execute_script
973
+ script: { ref: actions }
974
+ subcommand: cr-activate
975
+ args: ["--cr-id", "{{cr_choice}}", "--feature-dir", "{{feature_dir}}"]
976
+ outputs:
977
+ - { name: active_change, from: "$.active_change" }
646
978
 
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
979
+ # That change's documents, its wave, and the model deltas that come BEFORE it — the open
980
+ # ones in a LOWER wave, which are the deltas committed to landing first and so the only
981
+ # ones a plan may build on. Same-wave siblings are counted, never assumed.
982
+ - id: cr_paths
983
+ condition: "{{plan_target}} == delta"
651
984
  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
985
  script: { ref: actions }
656
- subcommand: log-append
657
- args: ["--feature-dir", "{{feature_dir}}", "--log", "interfaces", "--entries-file", "{{feature_dir}}/.aw-if-new.tmp.md"]
986
+ subcommand: cr-paths
987
+ args: ["--cr-id", "{{cr_choice}}", "--feature-dir", "{{feature_dir}}"]
658
988
  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.
989
+ - { name: cr_id, from: "$.cr_id" } # normalised: the label's first token
990
+ - { name: cr_dir, from: "$.cr_dir" }
991
+ - { name: delta_spec, from: "$.delta_spec" }
992
+ - { name: delta_model, from: "$.delta_model" }
993
+ - { name: wave, from: "$.wave" }
994
+ - { name: predecessor_files, from: "$.predecessor_files" }
995
+ - { name: predecessor_count, from: "$.predecessor_count" }
996
+ - { name: sibling_count, from: "$.sibling_count" }
997
+
998
+ # Author that change's delta plans — one per declared lane whose MASTER exists, inside
999
+ # changes/<CR-id>/ beside its delta-spec. The masters are left UNTOUCHED; the change
1000
+ # stays OPEN and finalize-delta merges it later. Runs in this context (shares vars +
1001
+ # the lane rule sets + source_files).
1002
+ - id: delta_plan_module
1003
+ condition: "{{plan_target}} == delta"
1004
+ action: include
1005
+ module: .appweaver/orchestrator/modules/delta-plan.orch.yaml
1006
+
1007
+ # ═══════════ HANDOFF — authoring only; the gates are SEPARATE stages ═══════
1008
+ # • MAIN (bootstrap or re-derivation) → /appweaver analyze-plan (spec↔plan
1009
+ # conformance gate) → implement. No change is opened either way.
1010
+ # • DELTA → the chosen change stays OPEN with filled delta plans. Ship it via the CR
1011
+ # chain: /appweaver analyze-plan-delta → implement → build → analyze-slice-delta
1012
+ # → finalize-delta (merges the delta into spec.md + domain-model.md — not the lane
1013
+ # plans, which are derived from those two; the change stays OPEN) → analyze-slice
1014
+ # → close-delta (archives the change, bumps the minor version). Re-deriving the
1015
+ # masters from the merged documents is `plan main` on a later run.
1016
+
1017
+ # 7. Post-execution hooks.
667
1018
  - id: resolve_post_hooks
668
1019
  action: execute_script
669
1020
  script: { ref: actions }
@@ -677,12 +1028,16 @@ workflow:
677
1028
 
678
1029
  outputs:
679
1030
  feature_dir: "{{feature_dir}}"
680
- plan_doc: "{{plan_doc}}"
1031
+ bounded_context: "{{context}}"
1032
+ backend_plan: "{{backend_plan}}" # empty when no backend lane is declared
1033
+ frontend_plan: "{{frontend_plan}}" # empty when no frontend lane is declared
1034
+ domain_model: "{{domain_model}}"
681
1035
  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."
1036
+ plan_target: "{{plan_target}}"
1037
+ be_mode: "{{be_mode}}"
1038
+ fe_mode: "{{fe_mode}}"
1039
+ version: "{{version}}"
1040
+ change_id: "{{active_change}}"
1041
+ source_count: "{{source_count}}"
1042
+ note: "This run was pointed at `{{plan_target}}` (plan_mode {{plan_mode}}); backend lane {{be_mode}}, frontend lane {{fe_mode}}. Lanes come from .appweaver/stacks.json: backend declared = {{be_declared}}, frontend declared = {{fe_declared}}. A declared lane gets a plan; an undeclared one gets nothing and that is not a failure. A lane whose mode is `update` had its MASTER re-derived from the current spec and model, with its DA-n and WA-n ledgers carried forward — that rewrite was confirmed before it ran. The domain model at {{domain_model}} was READ, never written — it is spec-tier, and this phase records where each invariant is ENFORCED rather than what it says."
1043
+ next: "MAIN (masters authored or re-derived): /appweaver analyze-plan (spec↔plan gate) → implement. DELTA (change {{active_change}} open, delta plans authored): /appweaver analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta — implement is the same command on both paths and ASKS which open change it is building, so pass the id (/appweaver implement {{active_change}}) to skip the prompt (finalize-delta merges the delta into spec.md and domain-model.md — not the lane plans, which are derived from those two — but leaves the change open; analyze-slice verifies the slice against the merged masters; close-delta archives the change and bumps the minor version). Once a change has merged, /appweaver plan main re-derives the masters from the documents it merged into."