aw-spec-cli 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/.appweaver/DESIGN-FORMAT.md +18 -18
- package/assets/.appweaver/ORCH-DIALECT.md +1 -3
- package/assets/.appweaver/RULE-PARTITION.md +29 -13
- package/assets/.appweaver/STACK-PROFILES.md +10 -9
- package/assets/.appweaver/orchestrator/.state/smoke.state.json +32 -0
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +4 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +251 -0
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +104 -35
- package/assets/.appweaver/orchestrator/orchestrator.py +5 -4
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +94 -36
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/domain-model-template.skeleton.md +115 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +2 -9
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +16 -40
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +38 -17
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +8 -8
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +10 -11
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +673 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +535 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +643 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +84 -701
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +7 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +16 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +243 -144
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +877 -578
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +32 -91
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +836 -514
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +132 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +38 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +271 -979
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1085 -3385
- package/assets/.appweaver/scripts/bash/aw-xref.sh +1 -2
- package/assets/.appweaver/scripts/bash/common.sh +47 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +914 -3220
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +19 -3
- package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
- package/assets/.appweaver/scripts/python/aw-xref.py +86 -350
- package/assets/.appweaver/templates/constitution-ui-template.md +1 -1
- package/assets/.appweaver/templates/delta-design-template.md +120 -30
- package/assets/.appweaver/templates/delta-domain-model-template.md +81 -0
- package/assets/.appweaver/templates/delta-spec-template.md +43 -17
- package/assets/.appweaver/templates/design-template.md +68 -98
- package/assets/.appweaver/templates/domain-model-template.md +130 -0
- package/assets/.appweaver/templates/okf-concept-template.md +2 -2
- package/assets/.appweaver/templates/source-map-template.md +3 -3
- package/assets/.appweaver/templates/spec-template.md +100 -122
- package/assets/.appweaver/templates/ui-delta-design-template.md +159 -0
- package/assets/.appweaver/templates/ui-design-template.md +46 -75
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +7 -18
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +34 -39
- package/assets/guardrails/requirement-spec.txt +2 -2
- package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +0 -105
- package/assets/.appweaver/orchestrator/modules/spec-state-gate.orch.yaml +0 -87
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -813
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +0 -253
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +0 -663
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -600
- package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -1147
- package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1121
- package/assets/.appweaver/templates/story-template.md +0 -178
|
@@ -1,89 +1,99 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
|
-
# aw-spec-plan (orchestrator) — the spec-anchored Plan phase
|
|
2
|
+
# aw-spec-plan (orchestrator) — the spec-anchored Plan phase, BOTH LANES
|
|
3
3
|
#
|
|
4
|
-
# Authors
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
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
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# FEATURE-LEVEL ARCHITECTURAL CONFIRMATION: what the feature's structure IS to be,
|
|
21
|
-
# tech-agnostic, with the concrete file-level work left to the per-story passes.
|
|
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.
|
|
22
18
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
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.
|
|
28
25
|
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# 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.
|
|
34
30
|
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
# •
|
|
38
|
-
#
|
|
39
|
-
# •
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
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.
|
|
45
41
|
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
# • .aw-plan-track.backend-plan.json (beside the spec) records {spec_version,
|
|
51
|
-
# sha256} — one track file per plan document, so the two lanes never contend.
|
|
52
|
-
# plan-check reads that record back: BEHIND (ledger rows above the derived version →
|
|
53
|
-
# the exact ids to conform), EDITED (hash mismatch — the doc was hand-edited since
|
|
54
|
-
# the stamp; refused unless accept_manual folds the edits in), UNTRACKED (a migrated
|
|
55
|
-
# legacy design.md with no baseline — the update reconciles the WHOLE plan once and
|
|
56
|
-
# baselines it). The model writes NO version anywhere; the script stamps.
|
|
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.
|
|
57
46
|
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
# •
|
|
63
|
-
#
|
|
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.
|
|
64
67
|
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
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.
|
|
67
72
|
#
|
|
68
|
-
#
|
|
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)
|
|
69
83
|
# Prereq: /appweaver specify
|
|
70
84
|
# =============================================================================
|
|
71
85
|
|
|
72
86
|
name: aw-spec-plan
|
|
73
|
-
description: "Author
|
|
74
|
-
version:
|
|
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
|
|
75
89
|
agent_agnostic: true
|
|
76
90
|
dialect: aw-spec.orchestrator/v1
|
|
77
91
|
|
|
78
92
|
inputs:
|
|
79
93
|
user_input:
|
|
80
|
-
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."
|
|
81
95
|
required: false
|
|
82
96
|
bind: USER_INPUT
|
|
83
|
-
accept_manual:
|
|
84
|
-
description: "true ⇒ a hand-edited plan (tracking state edited/edited_behind) is folded in instead of refused: the update keeps the manual edits' intent, reconciles them with the spec, and the stamp re-baselines."
|
|
85
|
-
required: false
|
|
86
|
-
bind: ACCEPT_MANUAL
|
|
87
97
|
|
|
88
98
|
scripts:
|
|
89
99
|
actions:
|
|
@@ -95,10 +105,13 @@ scripts:
|
|
|
95
105
|
|
|
96
106
|
# Sticky standards seed = the UNIVERSAL always-on rule only (ai-rules). The TECHNICAL/
|
|
97
107
|
# architecture rule files are CONFIGURATION, loaded per-command from
|
|
98
|
-
# guardrails/projects/<project>/tech-rule-bindings.md
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
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)
|
|
102
115
|
standards:
|
|
103
116
|
- { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
|
|
104
117
|
|
|
@@ -106,71 +119,98 @@ vars:
|
|
|
106
119
|
lanes_declared: "false"
|
|
107
120
|
be_declared: "false"
|
|
108
121
|
fe_declared: "false"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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"
|
|
133
169
|
|
|
134
170
|
workflow:
|
|
135
171
|
|
|
136
|
-
# 0.
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
# carries the tech base).
|
|
140
|
-
- id: tech_select
|
|
141
|
-
action: include
|
|
142
|
-
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
143
|
-
with: { command: aw-spec-plan }
|
|
144
|
-
|
|
145
|
-
# 1. The BACKEND LANE MUST EXIST. This phase writes a service design document, so
|
|
146
|
-
# it asks the declaration whether there is a service lane at all — not the
|
|
147
|
-
# lane-gate module, whose on-disk check would refuse a greenfield project that
|
|
148
|
-
# has declared `src/` but not scaffolded it yet. A plan is authored before the
|
|
149
|
-
# code exists; that is the point of planning. Two distinct refusals (the
|
|
150
|
-
# evaluator takes single comparisons only, so the message is picked by an
|
|
151
|
-
# ordered set): nothing declared at all, vs a frontend-only project.
|
|
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.
|
|
152
175
|
- id: lane_resolve
|
|
153
176
|
action: execute_script
|
|
154
177
|
on_error: continue # never crash raw on an undeclared lane — the gate below explains it
|
|
155
178
|
script: { ref: actions }
|
|
156
179
|
subcommand: lane-resolve
|
|
157
180
|
outputs:
|
|
158
|
-
- { name: lanes_declared,
|
|
159
|
-
- { name: be_declared,
|
|
160
|
-
- { name:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
166
192
|
action: verify_context
|
|
167
|
-
require_true:
|
|
193
|
+
require_true: lanes_declared
|
|
168
194
|
on_false: stop
|
|
169
|
-
on_false_message: "
|
|
170
|
-
|
|
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
|
|
171
210
|
condition: "{{fe_declared}} == true"
|
|
172
|
-
action:
|
|
173
|
-
|
|
211
|
+
action: include
|
|
212
|
+
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
213
|
+
with: { command: aw-spec-plan-ui }
|
|
174
214
|
|
|
175
215
|
# 2. Resolve the current feature and require its spec.
|
|
176
216
|
- id: feature_paths
|
|
@@ -179,10 +219,25 @@ workflow:
|
|
|
179
219
|
script: { ref: actions }
|
|
180
220
|
subcommand: feature-paths
|
|
181
221
|
outputs:
|
|
222
|
+
- { name: feature, from: "$.feature" }
|
|
182
223
|
- { name: feature_dir, from: "$.feature_dir" }
|
|
183
224
|
- { name: spec_file, from: "$.spec_file" }
|
|
184
225
|
- { name: spec_exists, from: "$.spec_exists" }
|
|
185
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
|
+
|
|
186
241
|
- id: require_feature
|
|
187
242
|
action: verify_context
|
|
188
243
|
require_non_empty: feature_dir
|
|
@@ -195,109 +250,246 @@ workflow:
|
|
|
195
250
|
on_false: stop
|
|
196
251
|
on_false_message: "No spec.md for the current feature. Run /appweaver specify first."
|
|
197
252
|
|
|
198
|
-
# 3.
|
|
199
|
-
#
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
256
|
+
action: execute_script
|
|
257
|
+
script: { ref: actions }
|
|
258
|
+
subcommand: version-read
|
|
259
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
260
|
+
outputs:
|
|
261
|
+
- { name: active_change, from: "$.active_change" }
|
|
262
|
+
- { name: has_change, from: "$.has_change" }
|
|
263
|
+
- { name: version, from: "$.version" }
|
|
206
264
|
|
|
207
|
-
# 4.
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
-
|
|
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"
|
|
211
284
|
action: execute_script
|
|
212
285
|
script: { ref: actions }
|
|
213
|
-
subcommand:
|
|
286
|
+
subcommand: plan-scaffold
|
|
214
287
|
args: ["--feature-dir", "{{feature_dir}}", "--lane", "backend"]
|
|
215
288
|
outputs:
|
|
216
|
-
- { name:
|
|
217
|
-
- { name:
|
|
218
|
-
- { name:
|
|
219
|
-
- { name:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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"
|
|
316
|
+
action: set
|
|
317
|
+
vars: { be_new: "{{be_declared}}" }
|
|
318
|
+
- id: be_flag_existing
|
|
319
|
+
condition: "{{be_created}} == false"
|
|
226
320
|
action: set
|
|
227
|
-
vars: {
|
|
228
|
-
- id:
|
|
229
|
-
condition: "{{
|
|
321
|
+
vars: { be_existing: "{{be_declared}}" }
|
|
322
|
+
- id: fe_flag_new
|
|
323
|
+
condition: "{{fe_created}} == true"
|
|
230
324
|
action: set
|
|
231
|
-
vars: {
|
|
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}}" }
|
|
330
|
+
|
|
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" }
|
|
232
343
|
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
-
|
|
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"
|
|
237
362
|
action: execute_script
|
|
238
363
|
script: { ref: actions }
|
|
239
|
-
subcommand:
|
|
240
|
-
args: ["--feature-dir", "{{feature_dir}}", "--
|
|
364
|
+
subcommand: cr-list
|
|
365
|
+
args: ["--feature-dir", "{{feature_dir}}", "--preselect", "{{USER_INPUT}}", "--include-main", "--include-new"]
|
|
366
|
+
outputs:
|
|
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
|
|
241
384
|
outputs:
|
|
242
|
-
- { name:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
# the
|
|
251
|
-
#
|
|
252
|
-
|
|
253
|
-
|
|
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}}"
|
|
254
399
|
action: set
|
|
255
|
-
vars: {
|
|
256
|
-
- id:
|
|
257
|
-
condition: "{{
|
|
400
|
+
vars: { plan_target: "delta", plan_mode: "delta" }
|
|
401
|
+
- id: target_new
|
|
402
|
+
condition: "{{plan_choice}} == {{new_label}}"
|
|
258
403
|
action: set
|
|
259
|
-
vars: {
|
|
260
|
-
- id:
|
|
261
|
-
condition: "{{
|
|
404
|
+
vars: { open_new: "true" }
|
|
405
|
+
- id: target_no_menu
|
|
406
|
+
condition: "{{master_exists}} == false"
|
|
262
407
|
action: set
|
|
263
|
-
vars: {
|
|
264
|
-
- id: manual_fence
|
|
265
|
-
action: verify_context
|
|
266
|
-
require_true: manual_ok
|
|
267
|
-
on_false: stop
|
|
268
|
-
on_false_message: "backend-plan.md was hand-edited since its last stamp (tracking state: {{track_state}}). Re-run plan with accept_manual:true to fold the edits in (kept, reconciled with the spec, recorded in the ledgers, re-baselined) — or restore the document to its stamped state."
|
|
408
|
+
vars: { plan_target: "main", plan_mode: "bootstrap", open_new: "false" }
|
|
269
409
|
|
|
270
|
-
#
|
|
271
|
-
|
|
272
|
-
|
|
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"
|
|
273
419
|
action: set
|
|
274
|
-
vars: {
|
|
275
|
-
|
|
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"
|
|
276
436
|
action: verify_context
|
|
277
|
-
require_true:
|
|
437
|
+
require_true: rederive
|
|
278
438
|
on_false: stop
|
|
279
|
-
on_false_message: "
|
|
280
|
-
|
|
281
|
-
# 5b. THE CODE. The plan is an architectural confirmation, so it must know what the
|
|
282
|
-
# feature already looks like on disk: concept-paths resolves the feature's OKF
|
|
283
|
-
# concept to the source files realising it, and the authoring steps declare
|
|
284
|
-
# `tools: Read` so they can open the ones that matter. A PATH LIST, not inlined
|
|
285
|
-
# content — a real codebase does not fit a payload (E60). on_error: continue,
|
|
286
|
-
# because a greenfield feature has no concept doc and must not be blocked by it:
|
|
287
|
-
# source_count 0 is the greenfield signal the tasks read.
|
|
288
|
-
- id: concept_paths
|
|
289
|
-
action: execute_script
|
|
290
|
-
on_error: continue
|
|
291
|
-
script: { ref: xref }
|
|
292
|
-
subcommand: concept-paths
|
|
293
|
-
args: ["--feature-dir", "{{feature_dir}}"]
|
|
294
|
-
outputs:
|
|
295
|
-
- { name: source_csv, from: "$.csv" }
|
|
296
|
-
- { name: source_count, from: "$.count" }
|
|
297
|
-
- { name: okf_concept, from: "$.concept" }
|
|
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."
|
|
298
440
|
|
|
299
|
-
#
|
|
300
|
-
#
|
|
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.
|
|
301
493
|
- id: resolve_pre_hooks
|
|
302
494
|
action: execute_script
|
|
303
495
|
script: { ref: actions }
|
|
@@ -309,70 +501,61 @@ workflow:
|
|
|
309
501
|
action: dispatch_hooks
|
|
310
502
|
hooks_from_variable: before_hooks
|
|
311
503
|
|
|
312
|
-
#
|
|
313
|
-
# Author the whole HLD in one pass: all four layers as sections, tech-agnostic,
|
|
314
|
-
#
|
|
315
|
-
#
|
|
316
|
-
- id:
|
|
317
|
-
condition: "{{
|
|
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"
|
|
318
510
|
action: build_context
|
|
319
511
|
context:
|
|
320
|
-
- { path: "{{
|
|
321
|
-
- { path: "{{spec_file}}" } #
|
|
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
|
|
322
515
|
- { path: .appweaver/DESIGN-FORMAT.md } # the HLD contract
|
|
323
|
-
- { path:
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
- id: gen_plan
|
|
327
|
-
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"
|
|
328
519
|
action: agent_author
|
|
329
|
-
tools: Read # the
|
|
330
|
-
output_target: { type: file, path: "{{
|
|
520
|
+
tools: Read # the feature's EXISTING source arrives as {{source_files}} paths, not inlined
|
|
521
|
+
output_target: { type: file, path: "{{backend_plan}}" }
|
|
331
522
|
task: |
|
|
332
|
-
Author the MASTER service plan (the target backend-plan.md in context) for this
|
|
333
|
-
aggregate by filling out the template, based on the feature spec + the
|
|
334
|
-
technical/architecture standards. This is the MEDIATOR
|
|
335
|
-
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}}
|
|
336
527
|
Rules:
|
|
337
|
-
- THE SPEC
|
|
338
|
-
NO domain rule files.
|
|
339
|
-
|
|
340
|
-
- THE
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
already carries (it is in context on an update run), and for one being
|
|
355
|
-
published for the FIRST time write a one-line description with `(this run)` in
|
|
356
|
-
the id cell. NEVER invent an IF- number — the script is the only id authority,
|
|
357
|
-
and a later step publishes what you describe. Changing a published interface
|
|
358
|
-
means a SUPERSEDING entry, never an edit to an existing one.
|
|
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.
|
|
359
545
|
- Cover the four layers as sections: Domain (§1), Application (§2), API (§3),
|
|
360
546
|
Infrastructure (§4). Keep the template's section order and headings.
|
|
361
|
-
- The DOMAIN layer (§1) MUST follow spec.md EXACTLY: entities & value objects
|
|
362
|
-
are the spec's Key Entities (SAME names), invariants are the spec's Business
|
|
363
|
-
Rules (BR-nnn — cite each), the aggregate boundary is the spec's Aggregate
|
|
364
|
-
Boundary, and states are the lifecycle the spec describes. Elaborate with
|
|
365
|
-
domain events / state machines / behaviour signatures ONLY to realise spec
|
|
366
|
-
behaviour; do NOT invent or contradict. Any unavoidable new concept goes in
|
|
367
|
-
§7 as a DA-n assumption.
|
|
368
547
|
- REALISE THE LIFTED CONSTRAINTS (each cites its spec id):
|
|
369
548
|
• Constraints & NFRs (CN-) → structural ones (immutability, append-only audit,
|
|
370
|
-
no-hard-delete) as §1
|
|
371
|
-
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.
|
|
372
551
|
• Data Classification (DC-) → §5 data-protection + the §2/§3 projection surface
|
|
373
552
|
(what MUST NOT be exposed in API/logs/traces).
|
|
374
553
|
• Integration Contracts (IC-) → §4.2 External integrations (one port/adapter row
|
|
375
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.
|
|
376
559
|
- CONSTITUTION-MANDATED CROSS-CUTTING (from the loaded technical constitution, NOT
|
|
377
560
|
the spec): a NON-NEGOTIABLE technical principle you must apply that has NO
|
|
378
561
|
per-feature spec id. APPLY it (it is non-negotiable), and because it is not
|
|
@@ -385,73 +568,52 @@ workflow:
|
|
|
385
568
|
conceptual types. The stack is bound only at `implement` time. Do NOT include
|
|
386
569
|
code-generation mechanics or the layering/dependency rule — those are
|
|
387
570
|
solution-wide rules applied in `implement` / the constitution, not per feature.
|
|
388
|
-
- Every plan element traces UP to a spec id (FR-/BR-/CN-/DC-/IC-/SC-); the
|
|
389
|
-
spec traces DOWN — fill §6 Traceability completely, with a row for EVERY
|
|
390
|
-
CN-/DC-/IC- so no lifted constraint is silently dropped.
|
|
391
|
-
this plan traces to requirements, which is what keeps it stable while the
|
|
392
|
-
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.
|
|
393
574
|
- Fill the two ledgers: §7 Assumptions (DA-n, to clarify later) and §8
|
|
394
575
|
Workarounds (WA-n, to clear later). Empty → "None (as of v1.0)".
|
|
395
576
|
- Physical persistence schema is OUT OF SCOPE (defer to the technical-data-model).
|
|
396
|
-
-
|
|
397
|
-
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.
|
|
398
578
|
- Omit a LAYER section only if the aggregate genuinely has nothing there
|
|
399
579
|
(state why); never write "N/A". No complete code anywhere.
|
|
400
580
|
|
|
401
|
-
#
|
|
402
|
-
# Same contract as the greenfield bootstrap (tech-agnostic HLD, spec
|
|
403
|
-
# domain
|
|
404
|
-
# STRUCTURE follows the code-derived
|
|
405
|
-
#
|
|
406
|
-
#
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
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"
|
|
410
589
|
action: build_context
|
|
411
590
|
context:
|
|
412
|
-
- { path: "{{
|
|
413
|
-
- { path: "{{spec_file}}" } #
|
|
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
|
|
414
594
|
- { path: .appweaver/DESIGN-FORMAT.md } # the HLD contract (see its Brownfield variant note)
|
|
415
|
-
- { path: "{{feature_dir}}/interfaces.md", optional: true } # the published surface — cite these IF- ids in §3; absent on a first run
|
|
416
|
-
- { path: "{{okf_concept}}", optional: true } # the feature's OKF concept: use-case log + source map
|
|
417
595
|
- { path: knowledge/architecture/overview.md, optional: true } # the project's AS-BUILT architecture (onboard) — align to it
|
|
418
596
|
- { path: knowledge/architecture/platform.md, optional: true } # the as-built technology base / cross-cutting reality
|
|
419
|
-
- { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain
|
|
420
|
-
- id:
|
|
421
|
-
condition: "{{
|
|
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"
|
|
422
600
|
action: agent_author
|
|
423
|
-
tools: Read # the
|
|
424
|
-
output_target: { type: file, path: "{{
|
|
601
|
+
tools: Read # the feature's EXISTING source arrives as {{source_files}} paths, not inlined
|
|
602
|
+
output_target: { type: file, path: "{{backend_plan}}" }
|
|
425
603
|
task: |
|
|
426
|
-
Author the MASTER service plan (the target backend-plan.md in context — it is
|
|
427
|
-
code-derived BROWNFIELD template) for this NEW feature being added to an EXISTING
|
|
428
|
-
codebase, by filling the template from the spec + the
|
|
429
|
-
standards. It is the MEDIATOR between the spec and the
|
|
430
|
-
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}}
|
|
431
609
|
Rules:
|
|
432
|
-
- THE SPEC
|
|
433
|
-
project's as-built architecture references, and NO
|
|
434
|
-
|
|
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
|
|
435
613
|
unavoidable new concept → a §5 DA-n assumption).
|
|
436
|
-
- THE CODE IS THE WHAT-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
shape that is actually there rather than to an idealised one. An empty list is
|
|
440
|
-
greenfield — design from a clean slate and say so. The code NEVER overrides the
|
|
441
|
-
spec on behaviour and may not introduce a domain rule the spec omits: code that
|
|
442
|
-
contradicts the spec is reported (§5 DA-n) and designed to the SPEC, never
|
|
443
|
-
quietly blessed. A deliberate divergence from the as-built shape is a §6 WA-n
|
|
444
|
-
with its revisit trigger.
|
|
445
|
-
{{source_csv}}
|
|
446
|
-
- THE PUBLISHED SURFACE IS CITED, NOT RESTATED. Every endpoint, published event
|
|
447
|
-
and externally consumed contract belongs to `interfaces.md`, the feature's
|
|
448
|
-
append-only interface log — the anchor /appweaver plan-ui and the FE story
|
|
449
|
-
designs read. the EXTERNAL-facing §2 subsection here is the INDEX: cite the IF- id of an interface the log
|
|
450
|
-
already carries (it is in context on an update run), and for one being
|
|
451
|
-
published for the FIRST time write a one-line description with `(this run)` in
|
|
452
|
-
the id cell. NEVER invent an IF- number — the script is the only id authority,
|
|
453
|
-
and a later step publishes what you describe. Changing a published interface
|
|
454
|
-
means a SUPERSEDING entry, never an edit to an existing one.
|
|
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.
|
|
455
617
|
- STRUCTURE FOLLOWS THE TEMPLATE + THE PROJECT'S AS-BUILT ARCHITECTURE, NOT the four
|
|
456
618
|
greenfield layers. Keep the template's own section order and headings (§0 Overview
|
|
457
619
|
+ ubiquitous language; §1 Architecture Context & Domain Model; §2 Component &
|
|
@@ -466,17 +628,20 @@ workflow:
|
|
|
466
628
|
textbook layer gets its own section; a layer the feature does not need does NOT
|
|
467
629
|
appear.
|
|
468
630
|
- KEEP THE NEUTRAL ANCHORS (the template pins them): §1.2 is the domain/data MODEL
|
|
469
|
-
and MUST follow
|
|
470
|
-
|
|
471
|
-
each), the boundary is
|
|
472
|
-
cross-slice references-by-id live here. One §2
|
|
473
|
-
INTEGRATIONS table (its heading contains "Integration")
|
|
474
|
-
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.
|
|
475
637
|
- REALISE THE LIFTED CONSTRAINTS (each cites its spec id): CN- → structural ones
|
|
476
|
-
(immutability, append-only audit, no-hard-delete) as §1.2
|
|
477
|
-
retention/performance/audit as §2 persistence or §3 Cross-Cutting rows.
|
|
478
|
-
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
|
|
479
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.
|
|
480
645
|
- CONSTITUTION-MANDATED CROSS-CUTTING (from the loaded technical constitution, NOT
|
|
481
646
|
the spec): a NON-NEGOTIABLE technical principle with no per-feature spec id —
|
|
482
647
|
APPLY it, and LOG it as a §5 DA-n whose basis cites the mandating principle
|
|
@@ -487,216 +652,369 @@ workflow:
|
|
|
487
652
|
bound at `implement` time. Do NOT include code-generation mechanics or the
|
|
488
653
|
layering/dependency rule (those are solution-wide / constitution rules).
|
|
489
654
|
- Every plan element traces UP to a spec id; fill §4 Traceability completely (a
|
|
490
|
-
row for EVERY FR/BR/CN/DC/IC/SC so no lifted constraint is dropped).
|
|
491
|
-
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
|
|
492
656
|
§5 Assumptions (DA-n) and §6 Deviations (WA-n) ledgers — "None (as of v1.0)" if
|
|
493
657
|
empty. §6 records only genuine deviations, not "organised unlike a reference
|
|
494
658
|
architecture" (that is the as-built shape, §1.1).
|
|
495
|
-
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
#
|
|
501
|
-
#
|
|
502
|
-
#
|
|
503
|
-
#
|
|
504
|
-
|
|
505
|
-
|
|
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"
|
|
506
678
|
action: build_context
|
|
507
679
|
context:
|
|
508
|
-
- { path: "{{
|
|
509
|
-
- { path: "{{spec_file}}" }
|
|
510
|
-
- { path:
|
|
511
|
-
- { path:
|
|
512
|
-
- { path:
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
- { path: .appweaver/memory/constitution.technical.md, optional: true }
|
|
516
|
-
- id: gen_plan_update
|
|
517
|
-
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"
|
|
518
687
|
action: agent_author
|
|
519
|
-
tools: Read # the
|
|
520
|
-
output_target: { type: file, path: "{{
|
|
688
|
+
tools: Read # the feature's EXISTING source arrives as {{source_files}} paths, not inlined
|
|
689
|
+
output_target: { type: file, path: "{{backend_plan}}" }
|
|
521
690
|
task: |
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
ledger rows above the derivation name what moved:
|
|
526
|
-
ids: {{changed_csv}}
|
|
527
|
-
changes: {{changed_summary}}
|
|
528
|
-
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}}
|
|
529
694
|
Rules:
|
|
530
|
-
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
cites the new id. Never invent an IF- number and never edit the log by hand.
|
|
568
|
-
- PRESERVE THE LEDGERS: §7 Assumptions, §8 Workarounds and §9 Change History
|
|
569
|
-
keep every existing entry and their numbering — append, never rewrite. Write
|
|
570
|
-
NO version numbers anywhere and ADD NO Change History row: the stamp step
|
|
571
|
-
appends the row and moves the **Version** header.
|
|
572
|
-
- §6 TRACEABILITY STAYS COMPLETE both ways — every CN-/DC-/IC- still has a row
|
|
573
|
-
after the update; no element without a spec reason.
|
|
574
|
-
- TECHNOLOGY-AGNOSTIC, no complete code, same contract as at bootstrap.
|
|
575
|
-
|
|
576
|
-
# 7. STAMP — the only writer of the plan's version identity, and the ledger-survival
|
|
577
|
-
# check: it REFUSES (no baseline written) if the update dropped the Change History
|
|
578
|
-
# section, so a lost ledger can never be silently baselined over.
|
|
579
|
-
- id: stamp_bootstrap
|
|
580
|
-
condition: "{{plan_mode}} != update"
|
|
581
|
-
action: execute_script
|
|
582
|
-
script: { ref: actions }
|
|
583
|
-
subcommand: plan-stamp
|
|
584
|
-
args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{plan_doc}}", "--no-row"]
|
|
585
|
-
outputs:
|
|
586
|
-
- { name: stamp_ok, from: "$.ok" }
|
|
587
|
-
- { name: stamp_error, from: "$.error" }
|
|
588
|
-
- { name: current_version, from: "$.version" }
|
|
589
|
-
- id: stamp_update
|
|
590
|
-
condition: "{{plan_mode}} == update"
|
|
591
|
-
action: execute_script
|
|
592
|
-
script: { ref: actions }
|
|
593
|
-
subcommand: plan-stamp
|
|
594
|
-
args: ["--feature-dir", "{{feature_dir}}", "--doc", "{{plan_doc}}", "--ids", "{{changed_csv}}", "--note", "Conformed to spec {{current_version}}"]
|
|
595
|
-
outputs:
|
|
596
|
-
- { name: stamp_ok, from: "$.ok" }
|
|
597
|
-
- { name: stamp_error, from: "$.error" }
|
|
598
|
-
- id: stamp_fence
|
|
599
|
-
action: verify_context
|
|
600
|
-
require_true: stamp_ok
|
|
601
|
-
on_false: stop
|
|
602
|
-
on_false_message: "plan-stamp refused: {{stamp_error}} — backend-plan.md has been written but NOT baselined. Restore the missing ledger section, then re-run plan (or plan-stamp) to record the baseline."
|
|
603
|
-
|
|
604
|
-
# ═══════════ PUBLISH THE SURFACE — the interface log (payload-only) ════════
|
|
605
|
-
# §3 of the plan describes this feature's published surface; this pair turns what it
|
|
606
|
-
# newly describes into APPEND-ONLY log entries. Separate steps so the script stays the
|
|
607
|
-
# only id authority: entries arrive as IF-NEW blocks and log-append assigns IF-nnn,
|
|
608
|
-
# the date and status: proposed. It runs AFTER the stamp — the log lives in its own
|
|
609
|
-
# file, so appending to it cannot invalidate the baseline just recorded.
|
|
610
|
-
#
|
|
611
|
-
# This is the BE→FE contract anchor: /appweaver plan-ui and the FE story designs read
|
|
612
|
-
# the latest entry per id. A changed interface is published as a SUPERSEDING entry,
|
|
613
|
-
# never an edit — which is why the log is append-only and the plan cites ids.
|
|
614
|
-
- id: ctx_if
|
|
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"
|
|
615
732
|
action: build_context
|
|
616
|
-
vars:
|
|
617
|
-
derived_version: "{{current_version}}"
|
|
618
733
|
context:
|
|
619
|
-
- { path: "{{
|
|
620
|
-
- { path: "{{
|
|
621
|
-
|
|
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"
|
|
622
742
|
action: agent_author
|
|
623
|
-
output_target: { type: file, path: "{{
|
|
743
|
+
output_target: { type: file, path: "{{frontend_plan}}" }
|
|
624
744
|
task: |
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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.
|
|
628
815
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
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.
|
|
890
|
+
|
|
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.
|
|
635
937
|
|
|
636
|
-
|
|
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.
|
|
637
942
|
|
|
638
|
-
|
|
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" }
|
|
639
955
|
|
|
640
|
-
|
|
641
|
-
|
|
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}}" }
|
|
642
966
|
|
|
643
|
-
|
|
644
|
-
|
|
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" }
|
|
645
978
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
- id:
|
|
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"
|
|
650
984
|
action: execute_script
|
|
651
|
-
on_error: continue # a malformed extraction is reported LOUDLY but must not undo a
|
|
652
|
-
# plan that already landed and baselined: the temp file survives
|
|
653
|
-
# for a hand-run of log-append; if_error carries the refusal
|
|
654
985
|
script: { ref: actions }
|
|
655
|
-
subcommand:
|
|
656
|
-
args: ["--
|
|
986
|
+
subcommand: cr-paths
|
|
987
|
+
args: ["--cr-id", "{{cr_choice}}", "--feature-dir", "{{feature_dir}}"]
|
|
657
988
|
outputs:
|
|
658
|
-
- { name:
|
|
659
|
-
- { name:
|
|
660
|
-
- { name:
|
|
661
|
-
- { name:
|
|
662
|
-
- { name:
|
|
663
|
-
- { name:
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
#
|
|
668
|
-
#
|
|
669
|
-
#
|
|
670
|
-
#
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
- id: if_badsup_notice
|
|
688
|
-
condition: "{{if_badsup_csv}} != ''"
|
|
689
|
-
action: verify_context
|
|
690
|
-
require_value: if_badsup_csv
|
|
691
|
-
equals: ""
|
|
692
|
-
on_mismatch: continue
|
|
693
|
-
on_mismatch_message: >
|
|
694
|
-
An entry appended to interfaces.md supersedes an id the log does not carry:
|
|
695
|
-
{{if_badsup_csv}}. Nothing was replaced, so the interface it meant to retire is still
|
|
696
|
-
the latest entry for its surface and both now read as current. Append a corrected
|
|
697
|
-
superseding entry naming the real id.
|
|
698
|
-
|
|
699
|
-
# 8. Post-execution hooks.
|
|
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.
|
|
700
1018
|
- id: resolve_post_hooks
|
|
701
1019
|
action: execute_script
|
|
702
1020
|
script: { ref: actions }
|
|
@@ -710,12 +1028,16 @@ workflow:
|
|
|
710
1028
|
|
|
711
1029
|
outputs:
|
|
712
1030
|
feature_dir: "{{feature_dir}}"
|
|
713
|
-
|
|
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}}"
|
|
714
1035
|
plan_mode: "{{plan_mode}}"
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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."
|