aw-spec-cli 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +78 -626
- 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 +882 -464
- 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 +832 -477
- 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 -892
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +1096 -2911
- 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 +936 -2766
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +1 -2
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +174 -0
- package/assets/.appweaver/scripts/python/aw-stack.py +6 -2
- package/assets/.appweaver/scripts/python/aw-xref.py +185 -292
- 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/workflows/aw-spec-analyze-delta-design.orch.yaml +0 -251
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +0 -749
- 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 -540
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan-ui.orch.yaml +0 -597
- package/assets/.appweaver/orchestrator/workflows/aw-spec-story-plan.orch.yaml +0 -945
- package/assets/.appweaver/orchestrator/workflows/aw-spec-sync-stories.orch.yaml +0 -1040
- package/assets/.appweaver/templates/story-template.md +0 -147
|
@@ -1,57 +1,104 @@
|
|
|
1
1
|
# =============================================================================
|
|
2
|
-
# aw-spec-implement (orchestrator) — code generation from
|
|
2
|
+
# aw-spec-implement (orchestrator) — code generation from the LANE PLANS, BOTH LANES
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
4
|
+
# GENERATE the source files + paired tests for the aggregate, STRICTLY from its lane
|
|
5
|
+
# plans — the mediators between the 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
|
|
7
8
|
#
|
|
8
|
-
#
|
|
9
|
-
# the
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# says is new — and its OKF write is a MERGE, never a replace.
|
|
9
|
+
# THE LANES COME FROM stacks.json, NOT FROM A FLAG — select-stack is the sole writer of
|
|
10
|
+
# the declaration, and this phase implements whichever lanes are there: backend only,
|
|
11
|
+
# frontend only, or both. It absorbs the former `implement-ui` and `implement-delta`
|
|
12
|
+
# commands, exactly as `plan` absorbed `design-ui` and the delta-design module.
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
14
|
+
# SELF-DETECTING, off the CHANGES ON DISK (symmetric with how `plan` keys off the plan
|
|
15
|
+
# documents' own existence):
|
|
16
|
+
# • BOOTSTRAP (no change open) → generate from the MASTER plans.
|
|
17
|
+
# Next: /appweaver build → analyze-slice.
|
|
18
|
+
# • DELTA (one or more changes/<CR-id>/ open) → ASK WHICH ONE, activate it, and
|
|
19
|
+
# generate ONLY what that change's delta plans mark ADDED / MODIFIED, into the
|
|
20
|
+
# tree that is already there. The masters are the BASE, read and never written.
|
|
21
|
+
# A bare CR id in the argument selects without a prompt; a feature with exactly one
|
|
22
|
+
# open change auto-confirms. Next: build → analyze-slice-delta (which verifies that
|
|
23
|
+
# what was planned was actually built, before the merge) → finalize-delta →
|
|
24
|
+
# analyze-slice → close-delta.
|
|
25
|
+
# "The most recently opened" is a guess that is wrong the moment two people work in
|
|
26
|
+
# parallel, so the change is CHOSEN and then ACTIVATED (cr-activate) — every stage after
|
|
27
|
+
# this one still resolves "the" change through active_version.json.active_change.
|
|
18
28
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
29
|
+
# EACH LANE IS GATED ON ITS OWN, AND A LANE'S GAP IS NOT THE RUN'S. A declared lane needs
|
|
30
|
+
# two things before it can generate: the plan for THIS run (its master plan, or this
|
|
31
|
+
# change's delta plan) and a scaffolded project under the source path it declares. A lane
|
|
32
|
+
# that is missing either is REPORTED with the fix named and SKIPPED — the other lane still
|
|
33
|
+
# runs. Failing the whole command for the frontend's sake is how a backend that was ready
|
|
34
|
+
# goes unbuilt; failing silently is how a frontend that was never generated reports green.
|
|
35
|
+
# Only when NO lane can run does the phase stop, naming every reason it collected.
|
|
22
36
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# current code (OKF) what EXISTS, per lane, so this run edits rather than recreates
|
|
27
|
-
# interfaces.md the PUBLISHED surface the generated code must match
|
|
28
|
-
# adr.md what was DECIDED — binding unless a pass supersedes it
|
|
29
|
-
# constitution + rules HOW to build it (stack, layout, standards)
|
|
30
|
-
# backend-plan.md is NOT a generation input. The pass was designed inside it and is the
|
|
31
|
-
# concrete form of it; re-reading the plan here would let code drift from the pass back
|
|
32
|
-
# toward the architecture, with no gate between the two. It is read for two DERIVED
|
|
33
|
-
# facts only — the aggregate's cross-slice edges and its Overview prose — both of which
|
|
34
|
-
# are feature-level properties no single story can state.
|
|
37
|
+
# IT ASKS BEFORE IT WRITES. Generation is the expensive, side-effecting step of the
|
|
38
|
+
# pipeline, so each runnable lane confirms first, naming its stack, its plan and the
|
|
39
|
+
# subtree it will write into. Answering no to one lane leaves the other free to run.
|
|
35
40
|
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
+
# THE PLAN SAYS WHAT TO BUILD, THE MODEL SAYS WHAT THE DOMAIN IS. This phase does NOT read
|
|
42
|
+
# spec.md — analyze-plan already conformed the plans to it, so the plan is the complete,
|
|
43
|
+
# authoritative source for WHICH elements exist, where each lives and what surface they
|
|
44
|
+
# publish, and anything missing from it is fixed in `plan`, never compensated for here.
|
|
45
|
+
# But since the model split a plan CITES an invariant by id and never restates it, so the
|
|
46
|
+
# bounded context's domain-model.md is in context too, as the authority on the entity
|
|
47
|
+
# names, the aggregate boundary, the lifecycle states and the TEXT of every BR-nnn the plan
|
|
48
|
+
# cites. The plan names the PLACE a rule is enforced; the model states the RULE.
|
|
49
|
+
# ON THE DELTA PATH THE MODEL ARRIVES IN TWO HALVES — the master, plus that change's
|
|
50
|
+
# domain-model.<CR-id>.delta.md — and the effective model is the two read together, the
|
|
51
|
+
# delta winning where they differ. It has to be, because finalize-delta merges the model
|
|
52
|
+
# AFTER this phase: at generation time the master does not yet carry the change, and a
|
|
53
|
+
# generator reading only the master would build the change's code against the pre-change
|
|
54
|
+
# rules. No OTHER change's pending delta is loaded: a predecessor was already accounted
|
|
55
|
+
# for when the delta plan was authored, and reaching for one here would implement a
|
|
56
|
+
# change that has not landed.
|
|
57
|
+
# THE MODEL IS READ, NEVER MINED FOR WORK. It describes the whole bounded context while a
|
|
58
|
+
# run builds one feature, so an entity or invariant in it that no plan element realises is
|
|
59
|
+
# a PLANNING gap to surface — not work to invent here.
|
|
41
60
|
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
61
|
+
# THE STACK BINDS HERE. The plans are technology-agnostic — a component is a
|
|
62
|
+
# responsibility, a contract is a set of field shapes. The language, framework, router,
|
|
63
|
+
# state library, component library and styling approach come from the lane declaration +
|
|
64
|
+
# the constitution halves + this phase's rule sets. Each lane takes its rules from its own
|
|
65
|
+
# heading of tech-rule-bindings, and the frontend set is loaded only when that lane runs,
|
|
66
|
+
# so a backend-only project sees exactly the rules it did before:
|
|
67
|
+
# • BACKEND — `## /aw-spec-implement` on the bootstrap path, `## /aw-spec-implement-delta`
|
|
68
|
+
# on the delta path. Keyed by MODE, which is what the two commands did separately, so
|
|
69
|
+
# no project's rule set changes by being merged into one command.
|
|
70
|
+
# • FRONTEND — `## /aw-spec-implement-ui`, both paths. There was never a delta heading
|
|
71
|
+
# for this lane because the delta half of it was never built; it is built here.
|
|
72
|
+
#
|
|
73
|
+
# ONE SUBTREE PER LANE. Each lane owns the source path stacks.json declares for it and
|
|
74
|
+
# writes nothing outside it. A file written elsewhere belongs to the other lane or to no
|
|
75
|
+
# lane at all, and the project gate is deliberately stricter for the frontend: a manifest
|
|
76
|
+
# found only elsewhere in the repository is the service lane's, and accepting it is how a
|
|
77
|
+
# frontend that was never scaffolded is generated into anyway and reports green.
|
|
78
|
+
#
|
|
79
|
+
# WHAT IT DOES NOT DO. It never writes spec.md, a master plan or the domain model, and it
|
|
80
|
+
# never closes a change — finalize-delta merges the change's spec and model halves,
|
|
81
|
+
# analyze-slice verifies the slice, and close-delta archives it and bumps the version.
|
|
82
|
+
#
|
|
83
|
+
# Prereqs: /appweaver plan (+ analyze-plan, or analyze-plan-delta for a change), and a
|
|
84
|
+
# project scaffolded under each lane's source path — this phase GENERATES into that tree,
|
|
85
|
+
# so with no project there is nowhere to place the files. /appweaver foundation unpacks one
|
|
86
|
+
# where a bundle ships for the stack; a stack with no bundle scaffolds with its own tooling
|
|
87
|
+
# instead, and the gate accepts either, since it tests for a project rather than a bundle.
|
|
88
|
+
#
|
|
89
|
+
# Launch: /appweaver implement [guidance]
|
|
90
|
+
# /appweaver implement CR-20260910-… (implement THAT open change, no prompt)
|
|
44
91
|
# =============================================================================
|
|
45
92
|
|
|
46
93
|
name: aw-spec-implement
|
|
47
|
-
description: "Generate the
|
|
94
|
+
description: "Generate the aggregate's source files and paired tests from its lane plans — the backend lane from backend-plan.md when a backend stack is declared, the frontend lane from frontend-plan.md when a frontend stack is, both when both are. One command covers backend-only, frontend-only and full-stack projects and absorbs the former implement-ui and implement-delta. Self-detecting off the changes on disk — with none open it generates from the master plans; with one or more open it ASKS which change and generates only what that change's delta plans mark ADDED or MODIFIED, leaving the masters untouched. Every lane is gated on its own plan and its own scaffolded project, and a lane missing either is reported and skipped rather than failing the other lane; each runnable lane confirms before it writes. Model-driven, and it does not read the spec — analyze-plan already conformed the plans to it. The plan says WHAT to build; the bounded context's domain-model.md, in context alongside it, says what the domain IS — the entity names, the boundary and the text of every invariant the plan cites by id — joined on the delta path by that change's own model delta, since the master is not merged until finalize-delta. The stack binds here from the lane declaration and the constitution halves; the plans stay technology-agnostic."
|
|
48
95
|
version: 3.0.0
|
|
49
96
|
agent_agnostic: true
|
|
50
97
|
dialect: aw-spec.orchestrator/v1
|
|
51
98
|
|
|
52
99
|
inputs:
|
|
53
100
|
user_input:
|
|
54
|
-
description: Optional
|
|
101
|
+
description: "Optional implementation guidance (focus area, stack hints). On the delta path a bare CR id selects which open change to implement."
|
|
55
102
|
required: false
|
|
56
103
|
bind: USER_INPUT
|
|
57
104
|
|
|
@@ -63,400 +110,810 @@ scripts:
|
|
|
63
110
|
sh: .appweaver/scripts/bash/aw-xref.sh
|
|
64
111
|
ps1: .appweaver/scripts/powershell/aw-xref.ps1
|
|
65
112
|
|
|
66
|
-
# Sticky standards seed = the UNIVERSAL always-on rule only (ai-rules).
|
|
67
|
-
# code-generation rule files are CONFIGURATION, loaded per-
|
|
68
|
-
# guardrails/projects/<project>/tech-rule-bindings.md
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
# a component's PORT, and installing the component is
|
|
113
|
+
# Sticky standards seed = the UNIVERSAL always-on rule only (ai-rules). Each lane's
|
|
114
|
+
# FRAMEWORK / code-generation rule files are CONFIGURATION, loaded per-lane from
|
|
115
|
+
# guardrails/projects/<project>/tech-rule-bindings.md by the tech-select module (see the
|
|
116
|
+
# tech_select steps below). Per the rule-partition this phase loads NO domain rule file —
|
|
117
|
+
# everything domain reached the code through spec → plan. The architecture and UX bases
|
|
118
|
+
# arrive via constitution.technical.md / constitution.ui.md (referred to directly, as-is —
|
|
119
|
+
# NOT mapped); the structure via the lane plans. Component rule docs are NOT loaded here:
|
|
120
|
+
# this phase codes against a component's PORT, and installing the component itself is a
|
|
121
|
+
# separate step, /appweaver add-component. (missing files skipped at assembly time)
|
|
74
122
|
standards:
|
|
75
123
|
- { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
|
|
76
124
|
|
|
77
125
|
vars:
|
|
78
|
-
|
|
79
|
-
|
|
126
|
+
# WHICH RUN THIS IS. bootstrap = generate from the masters; delta = generate one open
|
|
127
|
+
# change's delta plans. Decided by the changes on disk, not by a flag.
|
|
128
|
+
mode: "bootstrap"
|
|
129
|
+
has_changes: "false"
|
|
130
|
+
cr_choice: ""
|
|
131
|
+
active_change: ""
|
|
132
|
+
cr_dir: ""
|
|
133
|
+
delta_spec: ""
|
|
134
|
+
delta_model: ""
|
|
135
|
+
delta_model_exists: "false"
|
|
136
|
+
delta_backend_plan: ""
|
|
137
|
+
delta_backend_plan_exists: "false"
|
|
138
|
+
delta_frontend_plan: ""
|
|
139
|
+
delta_frontend_plan_exists: "false"
|
|
140
|
+
wave: ""
|
|
141
|
+
# PER-LANE STATE. Each lane resolves its own plan, its own project and its own answer;
|
|
142
|
+
# a lane that is not declared never resolves any of them and its steps never run.
|
|
143
|
+
# The engine has no compound conditions, so each AND is an ordered assignment: a step
|
|
144
|
+
# conditioned on one fact assigning the value of the other.
|
|
80
145
|
be_plan: ""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
# the
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# generation + enrichment
|
|
116
|
-
files: ""
|
|
117
|
-
xref_wrote: ""
|
|
118
|
-
xref_edge_count: "0"
|
|
119
|
-
okf_overview: ""
|
|
120
|
-
accept_ok: "true"
|
|
121
|
-
accept_gaps: ""
|
|
122
|
-
accept_note: ""
|
|
146
|
+
be_plan_ok: "false"
|
|
147
|
+
be_project_ok: "false"
|
|
148
|
+
be_project_scope: ""
|
|
149
|
+
be_runnable: "false"
|
|
150
|
+
be_answer: ""
|
|
151
|
+
be_go: "false"
|
|
152
|
+
be_mode: "skip" # skip | bootstrap | delta
|
|
153
|
+
be_files: []
|
|
154
|
+
fe_plan: ""
|
|
155
|
+
fe_plan_ok: "false"
|
|
156
|
+
fe_project_ok: "false"
|
|
157
|
+
fe_project_scope: ""
|
|
158
|
+
fe_runnable: "false"
|
|
159
|
+
fe_answer: ""
|
|
160
|
+
fe_go: "false"
|
|
161
|
+
fe_mode: "skip" # skip | bootstrap | delta
|
|
162
|
+
fe_files: []
|
|
163
|
+
generate_any: "false"
|
|
164
|
+
record_edges: "false"
|
|
165
|
+
overview_ok: "false"
|
|
166
|
+
domain_model: ""
|
|
167
|
+
domain_model_exists: "false"
|
|
168
|
+
# The backend lane's rule-set heading, keyed by MODE — see the header. The frontend
|
|
169
|
+
# lane's is the same on both paths, so it is written inline at its include.
|
|
170
|
+
be_tech_command: "aw-spec-implement"
|
|
171
|
+
# HOOK PHASES. The merged command fires the phase of every branch it actually takes, so
|
|
172
|
+
# an extensions.yml wired to any of the three former commands keeps working:
|
|
173
|
+
# before/after_implement always, the _ui pair when the frontend lane generates, the _cr
|
|
174
|
+
# pair on the delta path. A branch not taken passes the base phase again, and
|
|
175
|
+
# resolve-hooks merges its phases and dedups by command, so the repeat adds nothing.
|
|
176
|
+
pre_ui_phase: "before_implement"
|
|
177
|
+
pre_cr_phase: "before_implement"
|
|
178
|
+
post_ui_phase: "after_implement"
|
|
179
|
+
post_cr_phase: "after_implement"
|
|
123
180
|
|
|
124
181
|
workflow:
|
|
125
182
|
|
|
126
|
-
# 0.
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
183
|
+
# 0. THE LANE MODEL. lane-verify with NO --require, because this phase requires neither
|
|
184
|
+
# kind: a backend-only and a frontend-only project are both correct states, and which
|
|
185
|
+
# lanes exist is exactly what it is here to find out. It still reports every DECLARED
|
|
186
|
+
# lane whose source path is empty, missing or overlapping another's — a stack named
|
|
187
|
+
# against a path that is not there is a mistake in a phase that writes code — and it
|
|
188
|
+
# always exits 0, so the reason reaches the gate below as text.
|
|
189
|
+
- id: lane_verify
|
|
190
|
+
action: execute_script
|
|
191
|
+
on_error: continue
|
|
192
|
+
script: { ref: actions }
|
|
193
|
+
subcommand: lane-verify
|
|
194
|
+
args: ["--command", "/appweaver implement"]
|
|
195
|
+
outputs:
|
|
196
|
+
- { name: lane_ok, from: "$.ok" }
|
|
197
|
+
- { name: lane_issues, from: "$.message" }
|
|
198
|
+
- { name: be_declared, from: "$.backend_declared" }
|
|
199
|
+
- { name: be_lane_id, from: "$.backend_lane_id" }
|
|
200
|
+
- { name: be_source_path, from: "$.backend_source_path" }
|
|
201
|
+
- { name: be_profile, from: "$.backend_profile" }
|
|
202
|
+
- { name: be_title, from: "$.backend_title" }
|
|
203
|
+
- { name: be_language, from: "$.backend_language" }
|
|
204
|
+
- { name: be_ecosystem, from: "$.backend_ecosystem" }
|
|
205
|
+
- { name: be_build, from: "$.backend_build" }
|
|
206
|
+
- { name: be_test, from: "$.backend_test" }
|
|
207
|
+
- { name: fe_declared, from: "$.frontend_declared" }
|
|
208
|
+
- { name: fe_lane_id, from: "$.frontend_lane_id" }
|
|
209
|
+
- { name: fe_source_path, from: "$.frontend_source_path" }
|
|
210
|
+
- { name: fe_profile, from: "$.frontend_profile" }
|
|
211
|
+
- { name: fe_title, from: "$.frontend_title" }
|
|
212
|
+
- { name: fe_language, from: "$.frontend_language" }
|
|
213
|
+
- { name: fe_ecosystem, from: "$.frontend_ecosystem" }
|
|
214
|
+
- { name: fe_build, from: "$.frontend_build" }
|
|
215
|
+
- { name: fe_test, from: "$.frontend_test" }
|
|
216
|
+
|
|
217
|
+
- id: require_lanes
|
|
218
|
+
action: verify_context
|
|
219
|
+
require_true: lane_ok
|
|
220
|
+
on_false: stop
|
|
221
|
+
on_false_message: >
|
|
222
|
+
/appweaver implement cannot run — {{lane_issues}}
|
|
223
|
+
The lane model in .appweaver/stacks.json is what tells this phase which subtree it
|
|
224
|
+
owns, what language to generate and what to compile. Declaring it is the first step
|
|
225
|
+
of both the greenfield and the brownfield sequence.
|
|
226
|
+
|
|
227
|
+
# 1. The current feature and its MASTER documents. The plans resolve under their legacy
|
|
228
|
+
# names too (design.md / design.ui.md), so a feature authored before the lane split
|
|
229
|
+
# is implemented from the document that is actually on disk.
|
|
136
230
|
- id: feature_paths
|
|
137
231
|
action: execute_script
|
|
138
232
|
on_error: continue # never crash raw on 'no feature set' — the gate below explains it
|
|
139
233
|
script: { ref: actions }
|
|
140
234
|
subcommand: feature-paths
|
|
141
235
|
outputs:
|
|
142
|
-
- { name:
|
|
143
|
-
- { name:
|
|
144
|
-
- { name:
|
|
145
|
-
- { name:
|
|
236
|
+
- { name: feature, from: "$.feature" }
|
|
237
|
+
- { name: feature_dir, from: "$.feature_dir" }
|
|
238
|
+
- { name: backend_plan, from: "$.backend_plan" }
|
|
239
|
+
- { name: backend_plan_exists, from: "$.backend_plan_exists" }
|
|
240
|
+
- { name: frontend_plan, from: "$.frontend_plan" }
|
|
241
|
+
- { name: frontend_plan_exists, from: "$.frontend_plan_exists" }
|
|
242
|
+
|
|
146
243
|
- id: require_feature
|
|
147
244
|
action: verify_context
|
|
148
245
|
require_non_empty: feature_dir
|
|
149
246
|
on_empty: stop
|
|
150
247
|
on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run implement."
|
|
151
248
|
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
-
|
|
249
|
+
# The bounded context's DOMAIN MODEL — in every generating lane's payload as the authority
|
|
250
|
+
# on what the plan's cited BR-nnn actually require and on the ubiquitous language, and the
|
|
251
|
+
# source of the OKF concept Overview at the end. On the delta path it is joined by that
|
|
252
|
+
# change's own model delta, which cr-paths resolves. See the header.
|
|
253
|
+
- id: context_paths
|
|
156
254
|
action: execute_script
|
|
255
|
+
on_error: continue
|
|
157
256
|
script: { ref: actions }
|
|
158
|
-
subcommand:
|
|
159
|
-
args: ["--feature-dir", "{{feature_dir}}", "--input", "{{USER_INPUT}}"]
|
|
257
|
+
subcommand: context-paths
|
|
160
258
|
outputs:
|
|
161
|
-
- { name:
|
|
162
|
-
- { name:
|
|
163
|
-
- { name:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
- { name: story_title, from: "$.story_title" }
|
|
172
|
-
- { name: exercises_csv, from: "$.exercises_csv" }
|
|
173
|
-
- { name: constraints_csv, from: "$.constraints_csv" }
|
|
174
|
-
- { name: current_version, from: "$.current_version" }
|
|
175
|
-
- { name: stale_csv, from: "$.stale_csv" }
|
|
176
|
-
- { name: stale_note, from: "$.stale_note" }
|
|
177
|
-
- { name: story_dir, from: "$.story_dir" }
|
|
178
|
-
- { name: adr_path, from: "$.adr_path" }
|
|
179
|
-
- { name: if_path, from: "$.if_path" }
|
|
180
|
-
- { name: design_doc, from: "$.be_doc_path" }
|
|
181
|
-
- { name: design_exists, from: "$.be_doc_exists" }
|
|
182
|
-
- { name: design_pass, from: "$.be_last_pass" }
|
|
183
|
-
- { name: design_spec_version, from: "$.be_doc_spec_version" }
|
|
184
|
-
- { name: design_stale, from: "$.be_doc_stale" }
|
|
185
|
-
- id: require_open_backlog
|
|
186
|
-
action: verify_context
|
|
187
|
-
require_true: any_open
|
|
188
|
-
on_false: stop
|
|
189
|
-
on_false_message: "No open stories for this feature — there is nothing to implement. Stories are minted by /appweaver specify (bootstrap) and /appweaver sync-stories (when the spec moves)."
|
|
190
|
-
- id: ask_story
|
|
191
|
-
condition: "{{needs_pick}} == true"
|
|
192
|
-
action: ask
|
|
193
|
-
prompt: |
|
|
194
|
-
Which story is this implementation for? ({{pick_reason}})
|
|
195
|
-
|
|
196
|
-
{{digest}}
|
|
197
|
-
options_from: "{{open_ids}}"
|
|
198
|
-
answer_key: picked_story
|
|
199
|
-
answer_var: picked_story
|
|
200
|
-
- id: story_pick_check
|
|
201
|
-
condition: "{{needs_pick}} == true"
|
|
259
|
+
- { name: context, from: "$.context" }
|
|
260
|
+
- { name: domain_model, from: "$.domain_model" }
|
|
261
|
+
- { name: domain_model_exists, from: "$.domain_model_exists" }
|
|
262
|
+
|
|
263
|
+
# 2. WHICH CHANGES ARE OPEN — the evidence is changes/<CR-id>/ on disk; a closed change
|
|
264
|
+
# has moved to changes/archive/. Any open change makes this run a DELTA run.
|
|
265
|
+
# `matched_label` resolves a CR id passed as the argument to the whole option string,
|
|
266
|
+
# so the pick below can auto-select it (the engine matches a preselection against the
|
|
267
|
+
# option text exactly).
|
|
268
|
+
- id: cr_list
|
|
202
269
|
action: execute_script
|
|
203
270
|
script: { ref: actions }
|
|
204
|
-
subcommand:
|
|
205
|
-
args: ["--feature-dir", "{{feature_dir}}", "--
|
|
271
|
+
subcommand: cr-list
|
|
272
|
+
args: ["--feature-dir", "{{feature_dir}}", "--preselect", "{{USER_INPUT}}"]
|
|
206
273
|
outputs:
|
|
207
|
-
- { name:
|
|
208
|
-
- { name:
|
|
209
|
-
- { name:
|
|
210
|
-
- { name:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
{
|
|
230
|
-
Re-run implement naming one of them.
|
|
231
|
-
- id: require_open_story
|
|
232
|
-
action: verify_context
|
|
233
|
-
require_true: story_open
|
|
234
|
-
on_false: stop
|
|
235
|
-
on_false_message: "{{status_msg}}"
|
|
274
|
+
- { name: change_options, from: "$.select_options" }
|
|
275
|
+
- { name: matched_label, from: "$.matched_label" }
|
|
276
|
+
- { name: has_changes, from: "$.has_changes" }
|
|
277
|
+
- { name: change_count, from: "$.count" }
|
|
278
|
+
|
|
279
|
+
- id: mode_delta
|
|
280
|
+
condition: "{{has_changes}} == true"
|
|
281
|
+
action: set
|
|
282
|
+
vars:
|
|
283
|
+
mode: "delta"
|
|
284
|
+
pre_cr_phase: "before_implement_cr"
|
|
285
|
+
post_cr_phase: "after_implement_cr"
|
|
286
|
+
be_tech_command: "aw-spec-implement-delta"
|
|
287
|
+
|
|
288
|
+
- id: pick_change
|
|
289
|
+
condition: "{{mode}} == delta"
|
|
290
|
+
action: user_select
|
|
291
|
+
prompt: "Which open change is being implemented? Deltas sharing a wave are independent; a higher wave is built on top of the lower ones, so implement those first."
|
|
292
|
+
options_from_variable: change_options
|
|
293
|
+
preselect_from: matched_label
|
|
294
|
+
single_option_confirm: true
|
|
295
|
+
outputs:
|
|
296
|
+
- { name: cr_choice, from: selection }
|
|
236
297
|
|
|
237
|
-
#
|
|
238
|
-
#
|
|
239
|
-
#
|
|
240
|
-
- id:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
298
|
+
# 2a. Make the chosen change the ACTIVE one. build, finalize-delta, analyze-slice and
|
|
299
|
+
# close-delta all still resolve the change through active_version.json.active_change,
|
|
300
|
+
# so without this the code generated for CR-B would be merged and closed as CR-C.
|
|
301
|
+
- id: activate
|
|
302
|
+
condition: "{{mode}} == delta"
|
|
303
|
+
action: execute_script
|
|
304
|
+
script: { ref: actions }
|
|
305
|
+
subcommand: cr-activate
|
|
306
|
+
args: ["--cr-id", "{{cr_choice}}", "--feature-dir", "{{feature_dir}}"]
|
|
307
|
+
outputs:
|
|
308
|
+
- { name: active_change, from: "$.active_change" }
|
|
245
309
|
|
|
246
|
-
#
|
|
247
|
-
#
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
#
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
- id:
|
|
310
|
+
# 2b. That change's documents. The picked value is a LABEL ("CR-… · wave 2 · title");
|
|
311
|
+
# the scripts take its first token, and cr-paths reports the clean id back.
|
|
312
|
+
- id: cr_paths
|
|
313
|
+
condition: "{{mode}} == delta"
|
|
314
|
+
action: execute_script
|
|
315
|
+
script: { ref: actions }
|
|
316
|
+
subcommand: cr-paths
|
|
317
|
+
args: ["--cr-id", "{{cr_choice}}", "--feature-dir", "{{feature_dir}}"]
|
|
318
|
+
outputs:
|
|
319
|
+
- { name: cr_id, from: "$.cr_id" }
|
|
320
|
+
- { name: cr_dir, from: "$.cr_dir" }
|
|
321
|
+
- { name: delta_spec, from: "$.delta_spec" }
|
|
322
|
+
# THIS CHANGE'S MODEL HALF — the structure the delta plans realise. It is context for
|
|
323
|
+
# every generating lane, exactly as the master model is: the delta plan cites the
|
|
324
|
+
# invariants this change ADDS or CHANGES by id and never restates them either.
|
|
325
|
+
- { name: delta_model, from: "$.delta_model" }
|
|
326
|
+
- { name: delta_model_exists, from: "$.delta_model_exists" }
|
|
327
|
+
- { name: delta_backend_plan, from: "$.delta_backend_plan" }
|
|
328
|
+
- { name: delta_backend_plan_exists, from: "$.delta_backend_plan_exists" }
|
|
329
|
+
- { name: delta_frontend_plan, from: "$.delta_frontend_plan" }
|
|
330
|
+
- { name: delta_frontend_plan_exists, from: "$.delta_frontend_plan_exists" }
|
|
331
|
+
- { name: wave, from: "$.wave" }
|
|
332
|
+
|
|
333
|
+
# 3. WHICH PLAN EACH LANE GENERATES FROM, resolved once per lane from the mode — the
|
|
334
|
+
# master on the bootstrap path, this change's delta plan on the delta path. Every gate
|
|
335
|
+
# and prompt below reads be_plan / fe_plan, so neither has to re-branch on the mode.
|
|
336
|
+
- id: be_plan_bootstrap
|
|
337
|
+
condition: "{{mode}} == bootstrap"
|
|
338
|
+
action: set
|
|
339
|
+
vars: { be_plan: "{{backend_plan}}", be_plan_ok: "{{backend_plan_exists}}" }
|
|
340
|
+
- id: be_plan_delta
|
|
341
|
+
condition: "{{mode}} == delta"
|
|
342
|
+
action: set
|
|
343
|
+
vars: { be_plan: "{{delta_backend_plan}}", be_plan_ok: "{{delta_backend_plan_exists}}" }
|
|
344
|
+
- id: fe_plan_bootstrap
|
|
345
|
+
condition: "{{mode}} == bootstrap"
|
|
346
|
+
action: set
|
|
347
|
+
vars: { fe_plan: "{{frontend_plan}}", fe_plan_ok: "{{frontend_plan_exists}}" }
|
|
348
|
+
- id: fe_plan_delta
|
|
349
|
+
condition: "{{mode}} == delta"
|
|
350
|
+
action: set
|
|
351
|
+
vars: { fe_plan: "{{delta_frontend_plan}}", fe_plan_ok: "{{delta_frontend_plan_exists}}" }
|
|
352
|
+
|
|
353
|
+
# 4. THE PROJECT each declared lane generates INTO. Deterministic structure gate:
|
|
354
|
+
# detect-project searches the LANE'S OWN subtree for an ecosystem manifest (.sln/
|
|
355
|
+
# .csproj, pom.xml, build.gradle, package.json, go.mod, Cargo.toml, pyproject.toml),
|
|
356
|
+
# falls back to the whole repo, and reports which scope answered. How the project got
|
|
357
|
+
# there is not asked — `foundation` unpacked it, or the team scaffolded it with the
|
|
358
|
+
# stack's own tooling.
|
|
359
|
+
- id: be_detect_project
|
|
360
|
+
condition: "{{be_declared}} == true"
|
|
361
|
+
action: execute_script
|
|
362
|
+
script: { ref: actions }
|
|
363
|
+
subcommand: detect-project
|
|
364
|
+
args: ["--root", "{{be_source_path}}"]
|
|
365
|
+
outputs:
|
|
366
|
+
- { name: be_project_ok, from: "$.scaffolded" }
|
|
367
|
+
- { name: be_project_scope, from: "$.scope" }
|
|
368
|
+
|
|
369
|
+
# THE SCOPE DISTINCTION IS THE GATE FOR THE FRONTEND, and only for it. A repo-scope hit
|
|
370
|
+
# in a two-lane project is the service lane's manifest, and generating this lane's code
|
|
371
|
+
# against it is exactly the failure the lane model exists to prevent — a frontend that
|
|
372
|
+
# was never scaffolded, generated into anyway, reporting green. The service lane accepts
|
|
373
|
+
# either scope, because the layout that keeps a manifest beside the lane root rather than
|
|
374
|
+
# inside it is a normal one for it.
|
|
375
|
+
- id: fe_detect_project
|
|
376
|
+
condition: "{{fe_declared}} == true"
|
|
281
377
|
action: execute_script
|
|
282
378
|
script: { ref: actions }
|
|
283
379
|
subcommand: detect-project
|
|
284
|
-
args: ["--root", "{{
|
|
380
|
+
args: ["--root", "{{fe_source_path}}"]
|
|
285
381
|
outputs:
|
|
286
|
-
- { name:
|
|
287
|
-
- { name:
|
|
288
|
-
|
|
289
|
-
|
|
382
|
+
- { name: fe_scaffolded, from: "$.scaffolded" }
|
|
383
|
+
- { name: fe_project_scope, from: "$.scope" }
|
|
384
|
+
- id: fe_project_flag
|
|
385
|
+
condition: "{{fe_project_scope}} == lane"
|
|
386
|
+
action: set
|
|
387
|
+
vars: { fe_project_ok: "true" }
|
|
388
|
+
|
|
389
|
+
# 5. REPORT EACH DECLARED LANE'S GAPS AND CARRY ON. `on_false: continue` is the whole
|
|
390
|
+
# point: the message is the deliverable, not the stop. A lane that cannot run is named
|
|
391
|
+
# here with the command that fixes it, and the run continues so the other lane still
|
|
392
|
+
# generates. If NOTHING can run, the gate at step 7 stops with that fact.
|
|
393
|
+
- id: warn_be_plan
|
|
394
|
+
condition: "{{be_declared}} == true"
|
|
290
395
|
action: verify_context
|
|
291
|
-
require_true:
|
|
396
|
+
require_true: be_plan_ok
|
|
397
|
+
on_false: continue
|
|
398
|
+
on_false_message: "BACKEND LANE SKIPPED — lane `{{be_lane_id}}` ({{be_title}}) is declared in .appweaver/stacks.json, but the plan this run generates from is not there: {{be_plan}}. Run /appweaver plan (then /appweaver analyze-plan for the masters, or /appweaver analyze-plan-delta for a change) and re-run implement. Nothing is generated for this lane; any other declared lane still runs."
|
|
399
|
+
- id: warn_be_project
|
|
400
|
+
condition: "{{be_declared}} == true"
|
|
401
|
+
action: verify_context
|
|
402
|
+
require_true: be_project_ok
|
|
403
|
+
on_false: continue
|
|
404
|
+
on_false_message: "BACKEND LANE SKIPPED — no project found under the {{be_lane_id}} lane's source path `{{be_source_path}}`, nor anywhere else in this repository. This phase GENERATES into an existing project tree, so one has to be scaffolded first: run /appweaver foundation <FoundationName> <AppName> where a foundation bundle ships for {{be_profile}}, or create the project with the stack's own tooling under that path. Any ecosystem manifest marks it — .sln/.csproj, pom.xml, build.gradle(.kts), package.json, go.mod, Cargo.toml or pyproject.toml."
|
|
405
|
+
- id: warn_fe_plan
|
|
406
|
+
condition: "{{fe_declared}} == true"
|
|
407
|
+
action: verify_context
|
|
408
|
+
require_true: fe_plan_ok
|
|
409
|
+
on_false: continue
|
|
410
|
+
on_false_message: "FRONTEND LANE SKIPPED — lane `{{fe_lane_id}}` ({{fe_title}}) is declared in .appweaver/stacks.json, but the plan this run generates from is not there: {{fe_plan}}. Run /appweaver plan (then /appweaver analyze-plan for the masters, or /appweaver analyze-plan-delta for a change) and re-run implement. Nothing is generated for this lane; any other declared lane still runs."
|
|
411
|
+
- id: warn_fe_project
|
|
412
|
+
condition: "{{fe_declared}} == true"
|
|
413
|
+
action: verify_context
|
|
414
|
+
require_true: fe_project_ok
|
|
415
|
+
on_false: continue
|
|
416
|
+
on_false_message: "FRONTEND LANE SKIPPED — no frontend project found under the {{fe_lane_id}} lane's source path `{{fe_source_path}}`. detect-project searched that subtree and then the whole repository, and answered at scope `{{fe_project_scope}}`; `lane` is the only scope this lane accepts. Scaffold a project there — /appweaver foundation <FoundationName> <AppName> where a bundle ships for {{fe_profile}}, or the stack's own tooling — and re-run implement. A manifest found elsewhere in the repository does NOT count: that one belongs to another lane, and generating this lane's code against it is how a frontend that was never built reports green."
|
|
417
|
+
|
|
418
|
+
# 6. RUNNABLE = declared AND its plan is there AND its project is there, as an ordered
|
|
419
|
+
# assignment plus a knockdown, since the engine has no compound conditions. An
|
|
420
|
+
# undeclared lane stays false either way — its `declared` value is what the raise
|
|
421
|
+
# assigns.
|
|
422
|
+
- id: be_runnable_raise
|
|
423
|
+
condition: "{{be_plan_ok}} == true"
|
|
424
|
+
action: set
|
|
425
|
+
vars: { be_runnable: "{{be_declared}}" }
|
|
426
|
+
- id: be_runnable_clear
|
|
427
|
+
condition: "{{be_project_ok}} == false"
|
|
428
|
+
action: set
|
|
429
|
+
vars: { be_runnable: "false" }
|
|
430
|
+
- id: fe_runnable_raise
|
|
431
|
+
condition: "{{fe_plan_ok}} == true"
|
|
432
|
+
action: set
|
|
433
|
+
vars: { fe_runnable: "{{fe_declared}}" }
|
|
434
|
+
- id: fe_runnable_clear
|
|
435
|
+
condition: "{{fe_project_ok}} == false"
|
|
436
|
+
action: set
|
|
437
|
+
vars: { fe_runnable: "false" }
|
|
438
|
+
|
|
439
|
+
# 7. CONFIRM EACH RUNNABLE LANE. Generation writes into the repository and costs a model
|
|
440
|
+
# pass per lane, so the operator says which lanes this run is for, with the stack, the
|
|
441
|
+
# plan and the target subtree in front of them. Declining one lane leaves the other
|
|
442
|
+
# free to run. A lane that is not runnable is never asked about — its reason was
|
|
443
|
+
# already reported above.
|
|
444
|
+
- id: ask_backend
|
|
445
|
+
condition: "{{be_runnable}} == true"
|
|
446
|
+
action: ask
|
|
447
|
+
prompt: "Implement the BACKEND lane now? Lane `{{be_lane_id}}` — {{be_title}}, {{be_language}} ({{be_ecosystem}}) — generating from {{be_plan}} into {{be_source_path}}."
|
|
448
|
+
options: ["yes", "no"]
|
|
449
|
+
answer_var: be_answer
|
|
450
|
+
- id: be_go_set
|
|
451
|
+
condition: "{{be_answer}} == yes"
|
|
452
|
+
action: set
|
|
453
|
+
vars: { be_go: "{{be_runnable}}", generate_any: "true" }
|
|
454
|
+
|
|
455
|
+
- id: ask_frontend
|
|
456
|
+
condition: "{{fe_runnable}} == true"
|
|
457
|
+
action: ask
|
|
458
|
+
prompt: "Implement the FRONTEND lane now? Lane `{{fe_lane_id}}` — {{fe_title}}, {{fe_language}} ({{fe_ecosystem}}) — generating from {{fe_plan}} into {{fe_source_path}}."
|
|
459
|
+
options: ["yes", "no"]
|
|
460
|
+
answer_var: fe_answer
|
|
461
|
+
- id: fe_go_set
|
|
462
|
+
condition: "{{fe_answer}} == yes"
|
|
463
|
+
action: set
|
|
464
|
+
vars:
|
|
465
|
+
fe_go: "{{fe_runnable}}"
|
|
466
|
+
generate_any: "true"
|
|
467
|
+
pre_ui_phase: "before_implement_ui"
|
|
468
|
+
post_ui_phase: "after_implement_ui"
|
|
469
|
+
|
|
470
|
+
- id: require_a_lane
|
|
471
|
+
action: verify_context
|
|
472
|
+
require_true: generate_any
|
|
292
473
|
on_false: stop
|
|
293
|
-
on_false_message: "
|
|
474
|
+
on_false_message: "Nothing to implement — no declared lane is both ready and confirmed. Either every declared lane was skipped for a reason printed above (its plan or its project is missing), or each one that was ready was declined at the prompt. Backend lane declared {{be_declared}}, ready {{be_runnable}}; frontend lane declared {{fe_declared}}, ready {{fe_runnable}}. Fix the reasons named above, or re-run and confirm a lane."
|
|
475
|
+
|
|
476
|
+
# 8. EACH GENERATING LANE'S authoring MODE — the run's mode where the lane is going, and
|
|
477
|
+
# `skip` where it is not, which silences both of that lane's branches without a
|
|
478
|
+
# compound condition.
|
|
479
|
+
- id: be_mode_set
|
|
480
|
+
condition: "{{be_go}} == true"
|
|
481
|
+
action: set
|
|
482
|
+
vars: { be_mode: "{{mode}}" }
|
|
483
|
+
- id: fe_mode_set
|
|
484
|
+
condition: "{{fe_go}} == true"
|
|
485
|
+
action: set
|
|
486
|
+
vars: { fe_mode: "{{mode}}" }
|
|
487
|
+
|
|
488
|
+
# 9. Each generating lane's code-generation rule files → sticky standards (configuration,
|
|
489
|
+
# not hardcoded). Loaded only for a lane that is actually going to generate, and the
|
|
490
|
+
# backend's heading is keyed by mode (see the header). No blocking gate: an
|
|
491
|
+
# unconfigured project soft-continues, since the constitution halves still carry each
|
|
492
|
+
# lane's ratified base.
|
|
493
|
+
- id: tech_select
|
|
494
|
+
condition: "{{be_go}} == true"
|
|
495
|
+
action: include
|
|
496
|
+
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
497
|
+
with: { command: "{{be_tech_command}}" }
|
|
294
498
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
action: execute_script
|
|
301
|
-
on_error: continue
|
|
302
|
-
script: { ref: xref }
|
|
303
|
-
subcommand: concept-coverage
|
|
304
|
-
args: ["--feature-dir", "{{feature_dir}}", "--ids", "{{exercises_csv}}"]
|
|
305
|
-
outputs:
|
|
306
|
-
- { name: okf_concept, from: "$.concept" }
|
|
307
|
-
- { name: coverage_digest, from: "$.digest" }
|
|
308
|
-
- { name: be_source_csv, from: "$.backend_csv" }
|
|
309
|
-
- { name: be_source_count, from: "$.backend_count" }
|
|
310
|
-
- { name: fe_source_count, from: "$.frontend_count" }
|
|
499
|
+
- id: tech_select_ui
|
|
500
|
+
condition: "{{fe_go}} == true"
|
|
501
|
+
action: include
|
|
502
|
+
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
503
|
+
with: { command: aw-spec-implement-ui }
|
|
311
504
|
|
|
312
|
-
#
|
|
505
|
+
# 10. Pre-execution hooks — see the hook-phase vars above.
|
|
313
506
|
- id: resolve_pre_hooks
|
|
314
507
|
action: execute_script
|
|
315
508
|
script: { ref: actions }
|
|
316
509
|
subcommand: resolve-hooks
|
|
317
|
-
args: ["--phase", "before_implement"]
|
|
510
|
+
args: ["--phase", "before_implement", "--phase", "{{pre_ui_phase}}", "--phase", "{{pre_cr_phase}}"]
|
|
318
511
|
outputs:
|
|
319
512
|
- { name: before_hooks, from: "$.hooks" }
|
|
320
513
|
- id: dispatch_pre_hooks
|
|
321
514
|
action: dispatch_hooks
|
|
322
515
|
hooks_from_variable: before_hooks
|
|
323
516
|
|
|
324
|
-
#
|
|
325
|
-
# The
|
|
326
|
-
#
|
|
327
|
-
#
|
|
328
|
-
|
|
517
|
+
# ═══════════ BACKEND LANE — bootstrap, generate from the MASTER plan ═════════
|
|
518
|
+
# The plan constrains WHAT to build; the constitution + the lane fix HOW. The spec is
|
|
519
|
+
# deliberately NOT in context — the plan is the single source here. build_context
|
|
520
|
+
# REPLACES the per-request envelope and PERSISTS, so each generating branch scopes its
|
|
521
|
+
# own; sticky standards stay prepended.
|
|
522
|
+
- id: ctx_backend
|
|
523
|
+
condition: "{{be_mode}} == bootstrap"
|
|
329
524
|
action: build_context
|
|
330
|
-
vars:
|
|
331
|
-
story_id: "{{story}}"
|
|
332
|
-
story_title: "{{story_title}}"
|
|
333
|
-
exercises_csv: "{{exercises_csv}}"
|
|
334
|
-
constraints_csv: "{{constraints_csv}}"
|
|
335
|
-
pass_number: "{{design_pass}}"
|
|
336
|
-
coverage_digest: "{{coverage_digest}}"
|
|
337
|
-
source_files_csv: "{{be_source_csv}}"
|
|
338
|
-
source_file_count: "{{be_source_count}}"
|
|
339
|
-
stale_note: "{{stale_note}}"
|
|
340
525
|
context:
|
|
341
|
-
- { path: "{{
|
|
342
|
-
- { path: "{{
|
|
343
|
-
- { path:
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
- id: generate
|
|
526
|
+
- { path: "{{be_plan}}" }
|
|
527
|
+
- { path: "{{domain_model}}", optional: true } # the CONTEXT'S canonical model — what each cited BR-nnn REQUIRES
|
|
528
|
+
- { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain rules
|
|
529
|
+
- id: gen_backend
|
|
530
|
+
condition: "{{be_mode}} == bootstrap"
|
|
347
531
|
action: agent_generate
|
|
348
|
-
tools: "Read,Write,Edit"
|
|
532
|
+
tools: "Read,Write,Edit"
|
|
349
533
|
permission_mode: acceptEdits
|
|
350
534
|
task: |
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
hand edits that were deliberate. Read the last one and build exactly it. Do NOT
|
|
357
|
-
consult or infer from spec.md — it is not in context on purpose: the design
|
|
358
|
-
already went through the gates that conform it, and generating from the spec
|
|
359
|
-
routes around all of them.
|
|
360
|
-
|
|
361
|
-
THIS IS ONE SLICE OF AN EXISTING FEATURE, NOT THE WHOLE FEATURE.
|
|
362
|
-
- The pass's "### Changes" section names, file by file, what to create and what
|
|
363
|
-
to modify. Create ONLY the files it names as new; modify the named existing
|
|
364
|
-
files IN PLACE. Every other file in this feature belongs to work that is
|
|
365
|
-
already done — do not regenerate, reformat or "tidy" it.
|
|
366
|
-
- The story delivers {{exercises_csv}} and must satisfy {{constraints_csv}}.
|
|
367
|
-
Anything outside those ids is another story's work, even if you can see it is
|
|
368
|
-
missing. If the slice genuinely cannot be built without touching shared
|
|
369
|
-
structure, make the smallest change that works and SAY SO in your summary.
|
|
370
|
-
- The code that already exists for this feature, per lane, is below. The service
|
|
371
|
-
files ({{source_file_count}} on disk) are yours to read before you edit; the UI
|
|
372
|
-
list is there so you do not change a surface that already has a client.
|
|
373
|
-
|
|
374
|
-
{{coverage_digest}}
|
|
375
|
-
|
|
376
|
-
{{source_files_csv}}
|
|
377
|
-
|
|
378
|
-
BIND THE STACK: this lane is `{{lane_id}}` — {{lane_title}}, language
|
|
379
|
-
{{lane_language}} ({{lane_ecosystem}}), declared in .appweaver/stacks.json and
|
|
380
|
-
compiled with `{{lane_build}}`, tested with `{{lane_test}}`. Generate in THAT
|
|
381
|
-
language and framework and no other, using its idioms, its project layout and its
|
|
382
|
-
test framework. Take persistence, messaging and the remaining technology choices
|
|
383
|
-
from the constitution's technology base + the loaded standards; the design is
|
|
384
|
-
technology-agnostic, so do not re-decide anything already fixed there. If the
|
|
385
|
-
loaded standards describe a DIFFERENT stack than the one above, the lane wins on
|
|
386
|
-
language and layout — follow the standards' INTENT and STOP to surface the
|
|
387
|
-
mismatch in your summary.
|
|
388
|
-
|
|
389
|
-
WRITE ONLY UNDER `{{lane_source_path}}` — the source path the `{{lane_id}}` lane
|
|
390
|
-
declares. That subtree is this lane's; a file outside it belongs to another lane
|
|
391
|
-
or to no lane at all. Do not create a parallel source tree because the layout
|
|
392
|
-
inside it is unfamiliar; if the pass needs a place this subtree has no home for,
|
|
393
|
-
STOP and surface it.
|
|
394
|
-
|
|
535
|
+
Generate the production code AND paired tests for this aggregate STRICTLY from its
|
|
536
|
+
backend-plan.md (in context). The plan is the complete, authoritative source of WHAT
|
|
537
|
+
to build — do NOT consult or infer from the spec; analyze-plan already conformed the
|
|
538
|
+
plan to it. This phase is the HOME of the technology-specific, solution-wide
|
|
539
|
+
code-generation rules. Guidance: {{USER_INPUT}}
|
|
395
540
|
Rules:
|
|
396
|
-
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
541
|
+
- TWO DOMAIN INPUTS, WITH DIFFERENT JOBS. The PLAN is the only authority on WHAT TO
|
|
542
|
+
BUILD — which elements exist, where each one lives, what surface they publish. The
|
|
543
|
+
bounded context's DOMAIN MODEL (domain-model.md, in context) is the authority on
|
|
544
|
+
WHAT THE DOMAIN IS — the entity and value-object NAMES, the aggregate boundary,
|
|
545
|
+
the lifecycle states, and the TEXT of every invariant the plan cites by id. The
|
|
546
|
+
plan records WHERE a BR-nnn is enforced and deliberately never restates it, so the
|
|
547
|
+
model is where you read what it requires; enforce the rule the model states, in the
|
|
548
|
+
place the plan names. You have NO domain rule files and NO spec — introduce nothing
|
|
549
|
+
neither document states, and if a domain specific is in neither, STOP and surface
|
|
550
|
+
it rather than inferring it from a name, from surrounding code or from memory.
|
|
551
|
+
- THE MODEL IS READ, NEVER MINED FOR WORK. It describes the WHOLE bounded context,
|
|
552
|
+
while this run builds one feature. Realise only what the PLAN calls for: an entity,
|
|
553
|
+
event or invariant in the model that no plan element realises is a PLANNING gap —
|
|
554
|
+
surface it, do not implement it. `{{feature}}` is the aggregate this feature owns;
|
|
555
|
+
any other `## Aggregate:` section belongs to another slice, and you read it only to
|
|
556
|
+
honour a reference the plan makes, never to generate its code.
|
|
557
|
+
- BIND THE STACK: this lane is `{{be_lane_id}}` — {{be_title}}, language
|
|
558
|
+
{{be_language}} ({{be_ecosystem}}), declared in .appweaver/stacks.json and compiled
|
|
559
|
+
with `{{be_build}}`, tested with `{{be_test}}`. Generate in THAT language and
|
|
560
|
+
framework and no other, using its idioms, its project layout and its test
|
|
561
|
+
framework. Take persistence, messaging and the remaining technology choices from
|
|
562
|
+
the constitution's technology base + the loaded standards; the plan is
|
|
563
|
+
technology-agnostic, so do not re-decide anything already fixed there. If the
|
|
564
|
+
loaded standards describe a DIFFERENT stack than the one above, the lane wins on
|
|
565
|
+
language and layout — the standards' stack-specific mechanics do not apply, so
|
|
566
|
+
follow their INTENT and STOP to surface the mismatch in your summary.
|
|
567
|
+
- WRITE ONLY UNDER `{{be_source_path}}` — the source path the `{{be_lane_id}}` lane
|
|
568
|
+
declares in .appweaver/stacks.json. That subtree is this lane's, and a file outside
|
|
569
|
+
it belongs to another lane or to no lane at all. NEVER write into the frontend
|
|
570
|
+
lane's subtree, not even to consume a contract. Do not create a parallel source
|
|
571
|
+
tree because the layout inside it is unfamiliar; if the plan needs a place this
|
|
572
|
+
subtree has no home for, STOP and surface it.
|
|
400
573
|
- Follow the project's folder/layer convention + the loaded coding-standards,
|
|
401
|
-
dependency-policy and testing standards for HOW to structure, name and place
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
- Author a paired test for every
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
- Do NOT add behaviour the
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
-
|
|
428
|
-
|
|
574
|
+
dependency-policy and testing standards for HOW to structure, name and place files
|
|
575
|
+
WITHIN that subtree — the plan says WHAT to build; these standards (identical for
|
|
576
|
+
every feature) say HOW.
|
|
577
|
+
- COMPONENTS: when the plan's External Integrations table depends on a framework
|
|
578
|
+
component (e.g. `IEmailService`, `IJobScheduler`), code against that component's
|
|
579
|
+
ABSTRACTION / port as the plan declares — do NOT hand-roll it. Installing the
|
|
580
|
+
component into the project (packages, provider wiring, config) is a SEPARATE step:
|
|
581
|
+
/appweaver add-component <Component>. If the port isn't present yet, code to it and
|
|
582
|
+
note add-component as the prerequisite; do not load component rule docs here.
|
|
583
|
+
- Realise EVERY plan element as code, changing nothing about WHAT it specifies: one
|
|
584
|
+
file per Domain type / Application use-case / API endpoint / Infrastructure
|
|
585
|
+
adapter, placed by layer. Preserve the ubiquitous-language names verbatim.
|
|
586
|
+
- Enforce EXACTLY as the plan states: every Domain invariant, Application handler
|
|
587
|
+
orchestration and API error contract; the cross-cutting constraints in the
|
|
588
|
+
Cross-Cutting Concerns table (audit / retention / data-exposure — the CN-/DC-
|
|
589
|
+
realisations); and each integration contract (IC-) in the External Integrations
|
|
590
|
+
table. Implement every port the plan declares.
|
|
591
|
+
- Author a paired test for every plan behaviour / use-case / endpoint; derive the
|
|
592
|
+
cases from the plan's pre/post/invariant contracts and cite the spec id (FR / BR /
|
|
593
|
+
CN / DC / IC / US) from its Traceability section. Include tests for the constraints
|
|
594
|
+
— e.g. an audit record is emitted, a DC-restricted field is NOT exposed on the
|
|
595
|
+
public surface, an IC- projection round-trips.
|
|
596
|
+
- Do NOT add behaviour the plan does not state, and do NOT omit any it does. If the
|
|
597
|
+
plan looks wrong or incomplete, STOP and surface it (it is fixed in plan /
|
|
598
|
+
analyze-plan) — never compensate from the spec or from memory.
|
|
599
|
+
- This aggregate OWNS its files; never regenerate a file another feature owns.
|
|
600
|
+
- Where generated code depends on a plan Assumption (DA-n) or Workaround (WA-n), mark
|
|
601
|
+
it with a comment citing that id so it is easy to revisit later.
|
|
602
|
+
outputs:
|
|
603
|
+
- { name: be_files, from: "$.files" }
|
|
604
|
+
|
|
605
|
+
# ═══════════ BACKEND LANE — delta, generate ONLY what the change adds ════════
|
|
606
|
+
# The delta plan is the authoritative source of WHAT changed — analyze-plan-delta already
|
|
607
|
+
# conformed it to the delta spec. The MASTER plan is the BASE (the unchanged elements the
|
|
608
|
+
# delta depends on), and the delta spec is here ONLY so the paired tests can cite
|
|
609
|
+
# traceability ids.
|
|
610
|
+
- id: ctx_backend_delta
|
|
611
|
+
condition: "{{be_mode}} == delta"
|
|
612
|
+
action: build_context
|
|
613
|
+
context:
|
|
614
|
+
- { path: "{{be_plan}}" } # this change's delta-backend-plan.md
|
|
615
|
+
- { path: "{{delta_spec}}", optional: true }
|
|
616
|
+
- { path: "{{delta_model}}", optional: true } # this change's MODEL half — what it ADDS to / CHANGES in the model
|
|
617
|
+
- { path: "{{backend_plan}}", optional: true } # the master plan — READ ONLY, never amended here
|
|
618
|
+
- { path: "{{domain_model}}", optional: true } # the master model — the domain as it stands BEFORE this change
|
|
619
|
+
- { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain rules
|
|
620
|
+
- id: gen_backend_delta
|
|
621
|
+
condition: "{{be_mode}} == delta"
|
|
622
|
+
action: agent_generate
|
|
623
|
+
tools: "Read,Write,Edit"
|
|
624
|
+
permission_mode: acceptEdits
|
|
625
|
+
task: |
|
|
626
|
+
Implement ONLY change {{cr_id}} (wave {{wave}}) into the existing project tree,
|
|
627
|
+
STRICTLY from its delta-backend-plan.md (in context). The delta plan is the
|
|
628
|
+
authoritative source of WHAT changed — analyze-plan-delta already conformed it to the
|
|
629
|
+
delta spec. The master backend-plan.md (in context) is the BASE: the unchanged
|
|
630
|
+
elements the delta depends on; do NOT regenerate them. Guidance: {{USER_INPUT}}
|
|
631
|
+
Rules:
|
|
632
|
+
- SCOPE = THE DELTA ONLY. Realise exactly the elements the delta plan marks ADDED
|
|
633
|
+
(create the new file) or MODIFIED (edit the existing file in place). Touch NOTHING
|
|
634
|
+
the delta does not list, except the regression re-tests below.
|
|
635
|
+
- TWO DOMAIN INPUTS, WITH DIFFERENT JOBS. The DELTA PLAN is the only authority on
|
|
636
|
+
WHAT TO BUILD in this change — which elements are added or edited, where each one
|
|
637
|
+
lives, what surface they publish. The DOMAIN MODEL is the authority on WHAT THE
|
|
638
|
+
DOMAIN IS, and it reaches you in TWO halves, both in context: the master
|
|
639
|
+
domain-model.md is the domain as it stands BEFORE this change, and this change's
|
|
640
|
+
model delta (domain-model.{{cr_id}}.delta.md) is what the change ADDS to, MODIFIES
|
|
641
|
+
in or REMOVES from it. THE EFFECTIVE MODEL IS THE TWO READ TOGETHER, the delta
|
|
642
|
+
winning wherever they differ — it is the newer statement, and the master has not
|
|
643
|
+
been merged yet (finalize-delta does that after this phase). Read there what every
|
|
644
|
+
BR-nnn the delta plan cites actually requires; the plan records WHERE each is
|
|
645
|
+
enforced and never restates it. You have NO domain rule files and NO spec beyond
|
|
646
|
+
the delta-spec's ids — if a domain specific is in none of them, STOP and surface it
|
|
647
|
+
(it is fixed in specify / plan / analyze-plan-delta), never invent it from memory.
|
|
648
|
+
- THE MODEL IS READ, NEVER MINED FOR WORK. Realise only what the DELTA PLAN calls
|
|
649
|
+
for: an item in the model delta that no delta-plan element realises is a PLANNING
|
|
650
|
+
gap — surface it, do not implement it. Everything in the master model that this
|
|
651
|
+
change does not touch is ALREADY BUILT; do not regenerate it. `{{feature}}` is the
|
|
652
|
+
aggregate this feature owns, and another `## Aggregate:` section belongs to another
|
|
653
|
+
slice — read it only to honour a reference the delta plan makes.
|
|
654
|
+
- NO OTHER CHANGE'S MODEL DELTA IS IN CONTEXT, deliberately. A predecessor this
|
|
655
|
+
change builds on was already accounted for when the delta plan was authored and
|
|
656
|
+
recorded under its "Depends on"; implement it from the PLAN, not by reaching for
|
|
657
|
+
another change's pending files.
|
|
658
|
+
- BIND THE STACK: this lane is `{{be_lane_id}}` — {{be_title}}, language
|
|
659
|
+
{{be_language}} ({{be_ecosystem}}), declared in .appweaver/stacks.json and compiled
|
|
660
|
+
with `{{be_build}}`, tested with `{{be_test}}`. Generate in THAT language and
|
|
661
|
+
framework and no other. Take persistence, messaging and the remaining technology
|
|
662
|
+
choices from the constitution's technology base + the loaded standards; the plan is
|
|
663
|
+
technology-agnostic, so do not re-decide anything already fixed there. If the
|
|
664
|
+
loaded standards describe a DIFFERENT stack than the one above, the lane wins on
|
|
665
|
+
language and layout — follow the standards' INTENT and STOP to surface the
|
|
666
|
+
mismatch in your summary.
|
|
667
|
+
- WRITE ONLY UNDER `{{be_source_path}}` — the source path the `{{be_lane_id}}` lane
|
|
668
|
+
declares. That subtree is this lane's, and a file outside it belongs to another
|
|
669
|
+
lane or to no lane at all. NEVER write into the frontend lane's subtree.
|
|
670
|
+
- Follow the project's EXISTING folder/layer convention + the loaded
|
|
671
|
+
coding-standards, dependency-policy and testing standards for HOW to structure,
|
|
672
|
+
name and place files WITHIN that subtree. Match the surrounding code already there.
|
|
673
|
+
- Realise EVERY ADDED/MODIFIED delta element as code: one file per Domain type /
|
|
674
|
+
Application use-case / API endpoint / Infrastructure adapter, placed by layer.
|
|
675
|
+
Preserve the ubiquitous-language names verbatim. Enforce each new/changed Domain
|
|
676
|
+
invariant, handler orchestration, API error contract, the Cross-Cutting Concerns
|
|
677
|
+
realisations (audit / retention / data-exposure — CN-/DC-) and the External
|
|
678
|
+
Integrations table's contracts (IC-) exactly as the delta plan states.
|
|
679
|
+
- COMPONENTS: if a delta element depends on a framework component, code against its
|
|
680
|
+
ABSTRACTION / port as the delta plan declares — do NOT hand-roll it. Installing the
|
|
681
|
+
component (packages, wiring, config) is /appweaver add-component <Component>; if
|
|
682
|
+
the port isn't present, code to it and note that as the prerequisite.
|
|
683
|
+
- Author a paired test for every ADDED/MODIFIED behaviour / use-case / endpoint;
|
|
684
|
+
derive the cases from the delta plan's pre/post/invariant contracts and cite the
|
|
685
|
+
delta-spec id (FR / BR / CN / DC / IC / US).
|
|
686
|
+
- REGRESSION: for each element in the delta plan's "Regression targets", verify (add
|
|
687
|
+
or adjust a test where needed) that the existing behaviour still holds after a
|
|
688
|
+
shared element changed — but do NOT rewrite that existing behaviour.
|
|
689
|
+
- Where generated code depends on a delta Assumption (DA-n) or Workaround (WA-n),
|
|
690
|
+
mark it with a comment citing that id.
|
|
691
|
+
outputs:
|
|
692
|
+
- { name: be_files, from: "$.files" }
|
|
693
|
+
|
|
694
|
+
- id: verify_be_files
|
|
695
|
+
condition: "{{be_go}} == true"
|
|
696
|
+
action: verify_context
|
|
697
|
+
require_non_empty: be_files
|
|
698
|
+
on_empty: stop
|
|
699
|
+
on_empty_message: "The backend lane generated no files. Ensure {{be_plan}} is a FILLED plan (not the empty template) and that it passed its gate — /appweaver analyze-plan for a master, /appweaver analyze-plan-delta for a change — then re-run implement."
|
|
700
|
+
|
|
701
|
+
# OKF knowledge — the feature has ONE concept across both lanes and both modes, so every
|
|
702
|
+
# lane's files JOIN it rather than replace it (--merge unions them with what is already
|
|
703
|
+
# recorded). Without the union a backend run would erase the frontend lane's source_files
|
|
704
|
+
# and vice-versa, and either lane may have run first. ⌛ The trade-off is that a file this
|
|
705
|
+
# lane DELETED stays listed until something prunes it; a lane-scoped replace needs
|
|
706
|
+
# concept-upsert to learn about lane roots, which is a script change and not built.
|
|
707
|
+
- id: xref_concept_backend
|
|
708
|
+
condition: "{{be_go}} == true"
|
|
709
|
+
action: execute_script
|
|
710
|
+
on_error: continue
|
|
711
|
+
script: { ref: xref }
|
|
712
|
+
subcommand: concept-upsert
|
|
713
|
+
args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{be_files}}", "--merge", "--origin", "greenfield"]
|
|
714
|
+
outputs:
|
|
715
|
+
- { name: okf_concept, from: "$.concept" }
|
|
716
|
+
|
|
717
|
+
# ═══════════ FRONTEND LANE — bootstrap, generate from the MASTER plan ════════
|
|
718
|
+
# The experience twin of the service generation, one lane over. Its own build_context,
|
|
719
|
+
# because the envelope persists across steps and the two lanes must not see each other's
|
|
720
|
+
# payload — the frontend binds to the service's PUBLISHED surface, never its internals.
|
|
721
|
+
- id: ctx_frontend
|
|
722
|
+
condition: "{{fe_mode}} == bootstrap"
|
|
723
|
+
action: build_context
|
|
724
|
+
context:
|
|
725
|
+
- { path: "{{fe_plan}}" }
|
|
726
|
+
- { path: "{{domain_model}}", optional: true } # the UBIQUITOUS LANGUAGE this lane's labels, states and messages use — and what a cited BR-nnn requires
|
|
727
|
+
- { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's ratified UX-* base — conventions, forbidden patterns, gates
|
|
728
|
+
- id: gen_frontend
|
|
729
|
+
condition: "{{fe_mode}} == bootstrap"
|
|
730
|
+
action: agent_generate
|
|
731
|
+
tools: "Read,Write,Edit"
|
|
732
|
+
permission_mode: acceptEdits
|
|
733
|
+
task: |
|
|
734
|
+
Generate the production UI code AND paired tests for this feature STRICTLY from its
|
|
735
|
+
frontend-plan.md (in context). The plan is the complete, authoritative source of WHAT
|
|
736
|
+
to build — do NOT consult or infer from the spec; analyze-plan already conformed the
|
|
737
|
+
plan to it. This phase is the HOME of the framework-specific, lane-wide
|
|
738
|
+
code-generation rules. Guidance: {{USER_INPUT}}
|
|
739
|
+
Rules:
|
|
740
|
+
- TWO DOMAIN INPUTS, WITH DIFFERENT JOBS. The PLAN is the only authority on WHAT TO
|
|
741
|
+
BUILD — the screens, the state, the components, the bindings. The bounded context's
|
|
742
|
+
DOMAIN MODEL (domain-model.md, in context) is the authority on WHAT THE DOMAIN IS:
|
|
743
|
+
use its UBIQUITOUS LANGUAGE exactly — the same terms in labels, states, empty
|
|
744
|
+
copy and messages, no synonyms — and read there what a BR-nnn the plan cites
|
|
745
|
+
actually requires, since the plan records where each is enforced and never restates
|
|
746
|
+
it. You have NO domain rule files and NO spec: introduce nothing neither document
|
|
747
|
+
states, and if a domain specific is in neither, STOP and surface it.
|
|
748
|
+
- THE MODEL IS READ, NEVER MINED FOR WORK, and reading a rule is not permission to
|
|
749
|
+
move it. Realise only what the PLAN calls for. A client-side check restates a
|
|
750
|
+
server-enforced invariant for FEEDBACK — the message says what the model says, in
|
|
751
|
+
the model's words — and it never becomes the place the rule lives. An entity or
|
|
752
|
+
invariant in the model that no plan element realises is a PLANNING gap; surface it.
|
|
753
|
+
`{{feature}}` is this feature's aggregate; another `## Aggregate:` section belongs
|
|
754
|
+
to another slice.
|
|
755
|
+
- BIND THE STACK: this lane is `{{fe_lane_id}}` — {{fe_title}}, language
|
|
756
|
+
{{fe_language}} ({{fe_ecosystem}}), declared in .appweaver/stacks.json and built
|
|
757
|
+
with `{{fe_build}}`, tested with `{{fe_test}}`. Generate in THAT framework and no
|
|
758
|
+
other, using its idioms, its project layout and its test framework. Take the
|
|
759
|
+
router, state approach, component library, design system and styling from the UI
|
|
760
|
+
constitution + the loaded standards; the plan is technology-agnostic, so do not
|
|
761
|
+
re-decide anything already fixed there. If the loaded standards describe a
|
|
762
|
+
DIFFERENT stack than the one above, the lane wins on framework and layout — follow
|
|
763
|
+
the standards' INTENT and STOP to surface the mismatch in your summary.
|
|
764
|
+
- WRITE ONLY UNDER `{{fe_source_path}}` — the source path the `{{fe_lane_id}}` lane
|
|
765
|
+
declares. That subtree is this lane's; a file outside it belongs to the service
|
|
766
|
+
lane or to no lane. NEVER touch the service lane's code, not even to add a field
|
|
767
|
+
the UI wants: a gap in the service surface is a contract delta raised back to that
|
|
768
|
+
lane. Do not create a parallel source tree because the layout inside this one is
|
|
769
|
+
unfamiliar; if the plan needs a place this subtree has no home for, STOP and
|
|
770
|
+
surface it.
|
|
771
|
+
- Follow the UI constitution's folder structure, naming conventions and forbidden
|
|
772
|
+
patterns, plus the loaded standards, for HOW to structure, name and place files
|
|
773
|
+
WITHIN that subtree — the plan says WHAT to build; these say HOW.
|
|
774
|
+
- REALISE EVERY PLAN ELEMENT, changing nothing about WHAT it specifies:
|
|
775
|
+
• §1 — one route/screen per row, with the navigation shape and the access
|
|
776
|
+
conditions the row states.
|
|
777
|
+
• §2 — the client state exactly as owned and scoped there, including its
|
|
778
|
+
invalidation triggers. Anything the plan lists under "what must NOT be held"
|
|
779
|
+
NEVER reaches client state, storage, the URL or telemetry.
|
|
780
|
+
• §3 — one component per row, with the responsibility, the input field shapes and
|
|
781
|
+
the events the plan gives it. Compose the shared components the plan says it
|
|
782
|
+
reuses; do not re-implement them.
|
|
783
|
+
• §4 — EVERY state in EVERY interaction row: loading, empty, error,
|
|
784
|
+
permission-denied and in-flight/disabled, plus the validation timing, the focus
|
|
785
|
+
and keyboard behaviour and the concurrency handling stated there. These are
|
|
786
|
+
requirements, not polish, and they are the first thing a generator drops.
|
|
787
|
+
• §5 — the service binding, through the lane's own client layer, with the write
|
|
788
|
+
semantics and rollback each row states.
|
|
789
|
+
• §6 — the cross-cutting decisions as code: what is hidden vs disabled,
|
|
790
|
+
accessibility obligations, localization, performance and offline behaviour.
|
|
791
|
+
- THE CLIENT IS NOT AN AUTHORITY. A client-side validation or permission check
|
|
792
|
+
restates a server rule for feedback; it never replaces it. Generate no server-side
|
|
793
|
+
behaviour, no persistence and no business rule evaluation here.
|
|
794
|
+
- Author a paired test for every screen, interaction and state the plan names —
|
|
795
|
+
including the unwanted-behaviour states from §4 and an accessibility assertion per
|
|
796
|
+
interactive surface — and cite the spec id (UX-/US-/FR-/CN-/DC-/IC-/SC-) from the
|
|
797
|
+
plan's §7 Traceability in each test. A snapshot alone is not a test.
|
|
798
|
+
- Do NOT add behaviour the plan does not state, and do NOT omit any it does. If the
|
|
799
|
+
plan looks wrong or incomplete, STOP and surface it (it is fixed in plan /
|
|
800
|
+
analyze-plan) — never compensate from the spec or from memory.
|
|
801
|
+
- This feature OWNS its files; never regenerate a file another feature owns, and do
|
|
802
|
+
not modify the app shell, global layout or design system — they belong to no
|
|
803
|
+
feature. If one of them has to change, STOP and surface it.
|
|
804
|
+
- Where generated code depends on a plan Assumption (DA-n) or Workaround (WA-n), mark
|
|
805
|
+
it with a comment citing that id so it is easy to revisit later.
|
|
429
806
|
outputs:
|
|
430
|
-
- { name:
|
|
807
|
+
- { name: fe_files, from: "$.files" }
|
|
808
|
+
|
|
809
|
+
# ═══════════ FRONTEND LANE — delta, generate ONLY what the change adds ═══════
|
|
810
|
+
# The UI half of the CR loop, which never existed as its own command: the ⌛
|
|
811
|
+
# `implement-ui-delta` phase is BUILT HERE by absorption, exactly as the delta UI plan
|
|
812
|
+
# shipped as the frontend half of the delta-plan module.
|
|
813
|
+
- id: ctx_frontend_delta
|
|
814
|
+
condition: "{{fe_mode}} == delta"
|
|
815
|
+
action: build_context
|
|
816
|
+
context:
|
|
817
|
+
- { path: "{{fe_plan}}" } # this change's delta-frontend-plan.md
|
|
818
|
+
- { path: "{{delta_spec}}", optional: true }
|
|
819
|
+
- { path: "{{delta_model}}", optional: true } # this change's MODEL half — new terms, states and rules the UI must speak
|
|
820
|
+
- { path: "{{frontend_plan}}", optional: true } # the master plan — READ ONLY, never amended here
|
|
821
|
+
- { path: "{{domain_model}}", optional: true } # the master model — the ubiquitous language as it stands BEFORE this change
|
|
822
|
+
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
823
|
+
- id: gen_frontend_delta
|
|
824
|
+
condition: "{{fe_mode}} == delta"
|
|
825
|
+
action: agent_generate
|
|
826
|
+
tools: "Read,Write,Edit"
|
|
827
|
+
permission_mode: acceptEdits
|
|
828
|
+
task: |
|
|
829
|
+
Implement ONLY change {{cr_id}} (wave {{wave}}) into the existing frontend tree,
|
|
830
|
+
STRICTLY from its delta-frontend-plan.md (in context). The delta plan is the
|
|
831
|
+
authoritative source of WHAT changed — analyze-plan-delta already conformed it to the
|
|
832
|
+
delta spec. The master frontend-plan.md (in context) is the BASE: the unchanged
|
|
833
|
+
screens, state and components the delta depends on; do NOT regenerate them.
|
|
834
|
+
Guidance: {{USER_INPUT}}
|
|
835
|
+
Rules:
|
|
836
|
+
- SCOPE = THE DELTA ONLY. Realise exactly the screens, components, state and bindings
|
|
837
|
+
the delta plan marks ADDED (create the new file) or MODIFIED (edit the existing
|
|
838
|
+
file in place). A shared component or a shared state container is MODIFIED in
|
|
839
|
+
place, never copied into the change. Touch NOTHING the delta does not list, except
|
|
840
|
+
the regression re-tests below.
|
|
841
|
+
- TWO DOMAIN INPUTS, WITH DIFFERENT JOBS. The DELTA PLAN is the only authority on
|
|
842
|
+
WHAT TO BUILD in this change. The DOMAIN MODEL is the authority on WHAT THE DOMAIN
|
|
843
|
+
IS, and it reaches you in TWO halves, both in context: the master domain-model.md
|
|
844
|
+
is the language and the rules as they stand BEFORE this change, and this change's
|
|
845
|
+
model delta is what it ADDS, MODIFIES or REMOVES. THE EFFECTIVE MODEL IS THE TWO
|
|
846
|
+
READ TOGETHER, the delta winning wherever they differ — the master has not been
|
|
847
|
+
merged yet. A TERM THIS CHANGE INTRODUCES OR RENAMES IS IN THE DELTA, and the UI
|
|
848
|
+
must speak it exactly as the delta spells it, in labels, states and messages. Read
|
|
849
|
+
there what any BR-nnn the delta plan cites requires. NO domain rule files, and no
|
|
850
|
+
spec beyond the delta-spec's ids; if a domain specific is in none of them, STOP and
|
|
851
|
+
surface it.
|
|
852
|
+
- THE MODEL IS READ, NEVER MINED FOR WORK, and reading a rule is not permission to
|
|
853
|
+
move it. Realise only what the DELTA PLAN calls for — an item in the model delta
|
|
854
|
+
that no delta-plan element realises is a PLANNING gap, and a client-side check
|
|
855
|
+
restates a server-enforced rule for FEEDBACK and never replaces it.
|
|
856
|
+
- BIND THE STACK: this lane is `{{fe_lane_id}}` — {{fe_title}}, language
|
|
857
|
+
{{fe_language}} ({{fe_ecosystem}}), built with `{{fe_build}}`, tested with
|
|
858
|
+
`{{fe_test}}`. Generate in THAT framework and no other. Take the router, state
|
|
859
|
+
approach, component library, design system and styling from the UI constitution +
|
|
860
|
+
the loaded standards, and MATCH the code already in the tree.
|
|
861
|
+
- WRITE ONLY UNDER `{{fe_source_path}}`. NEVER touch the service lane's code, not
|
|
862
|
+
even to add a field the UI wants — a gap in the service surface is a contract delta
|
|
863
|
+
raised back to that lane, never worked around in the client.
|
|
864
|
+
- STATES ARE PART OF THE CHANGE. Every interaction this delta adds or alters ships
|
|
865
|
+
its FULL state set — loading, empty, error, permission-denied, in-flight — plus the
|
|
866
|
+
validation timing, focus placement and keyboard reachability the delta plan states.
|
|
867
|
+
A new interaction without them is an incomplete implementation, not a later
|
|
868
|
+
refinement.
|
|
869
|
+
- BIND AGAINST THE PUBLISHED SURFACE ONLY: the delta plan's binding rows and the
|
|
870
|
+
service delta's "Contract deltas for the frontend lane" table as the delta plan
|
|
871
|
+
cites them. Generate no server-side behaviour, no persistence and no business rule
|
|
872
|
+
evaluation.
|
|
873
|
+
- Author a paired test for every ADDED/MODIFIED screen, interaction and state,
|
|
874
|
+
including the unwanted-behaviour states and an accessibility assertion per
|
|
875
|
+
interactive surface, citing the delta-spec id (UX-/US-/FR-/CN-/DC-/IC-/SC-).
|
|
876
|
+
- REGRESSION: for each element in the delta plan's regression targets, verify (add or
|
|
877
|
+
adjust a test where needed) that the existing behaviour still holds after a shared
|
|
878
|
+
component or state container changed — but do NOT rewrite that behaviour.
|
|
879
|
+
- Do not modify the app shell, global layout or design system — they belong to no
|
|
880
|
+
feature. If one of them has to change, STOP and surface it.
|
|
881
|
+
- Where generated code depends on a delta Assumption (DA-n) or Workaround (WA-n),
|
|
882
|
+
mark it with a comment citing that id.
|
|
883
|
+
outputs:
|
|
884
|
+
- { name: fe_files, from: "$.files" }
|
|
431
885
|
|
|
432
|
-
- id:
|
|
886
|
+
- id: verify_fe_files
|
|
887
|
+
condition: "{{fe_go}} == true"
|
|
433
888
|
action: verify_context
|
|
434
|
-
require_non_empty:
|
|
889
|
+
require_non_empty: fe_files
|
|
435
890
|
on_empty: stop
|
|
436
|
-
on_empty_message: "
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
# story implements. --lane backend puts them in `source_files`, the service lane's
|
|
441
|
-
# key, leaving `ui_source_files` for implement-ui.
|
|
442
|
-
# Known limit: a file the slice DELETED stays in the list; concept-coverage reports
|
|
443
|
-
# it as declared-but-missing drift rather than silently carrying it as real.
|
|
444
|
-
- id: xref_concept
|
|
891
|
+
on_empty_message: "The frontend lane generated no files. Ensure {{fe_plan}} is a FILLED plan (not the empty template) and that it passed its gate — /appweaver analyze-plan for a master, /appweaver analyze-plan-delta for a change — then re-run implement."
|
|
892
|
+
|
|
893
|
+
- id: xref_concept_frontend
|
|
894
|
+
condition: "{{fe_go}} == true"
|
|
445
895
|
action: execute_script
|
|
896
|
+
on_error: continue
|
|
446
897
|
script: { ref: xref }
|
|
447
898
|
subcommand: concept-upsert
|
|
448
|
-
args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{
|
|
899
|
+
args: ["--feature-dir", "{{feature_dir}}", "--source-files", "{{fe_files}}", "--merge", "--origin", "greenfield"]
|
|
449
900
|
outputs:
|
|
450
901
|
- { name: okf_concept, from: "$.concept" }
|
|
451
902
|
|
|
452
|
-
#
|
|
453
|
-
#
|
|
454
|
-
#
|
|
455
|
-
#
|
|
456
|
-
#
|
|
457
|
-
#
|
|
903
|
+
# 11. CROSS-SLICE EDGES — a Cross-References row written into every SIBLING aggregate
|
|
904
|
+
# this one uses, read off the master backend plan (§1 References-by-id + its External
|
|
905
|
+
# integrations table), reconcile-on-write so a dropped dependency self-heals and a
|
|
906
|
+
# used sibling with no concept yet is stubbed.
|
|
907
|
+
# BOOTSTRAP ONLY. On the delta path the master plan still holds the PRE-change
|
|
908
|
+
# dependencies, so recording edges now would publish stale references — finalize-delta
|
|
909
|
+
# records them after the change merges. Enrichment downstream of a successful
|
|
910
|
+
# generation; it never re-decides WHAT was built.
|
|
911
|
+
- id: record_edges_flag
|
|
912
|
+
condition: "{{mode}} == bootstrap"
|
|
913
|
+
action: set
|
|
914
|
+
vars: { record_edges: "{{be_go}}" }
|
|
458
915
|
- id: xref_record
|
|
459
|
-
condition: "{{
|
|
916
|
+
condition: "{{record_edges}} == true"
|
|
460
917
|
action: execute_script
|
|
461
918
|
on_error: continue
|
|
462
919
|
script: { ref: xref }
|
|
@@ -466,118 +923,72 @@ workflow:
|
|
|
466
923
|
- { name: xref_wrote, from: "$.wrote" }
|
|
467
924
|
- { name: xref_edge_count, from: "$.edge_count" }
|
|
468
925
|
|
|
469
|
-
#
|
|
470
|
-
# It describes the
|
|
471
|
-
#
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
#
|
|
475
|
-
#
|
|
476
|
-
#
|
|
477
|
-
#
|
|
926
|
+
# 12. The concept's Overview — the ONE part of the OKF concept a deterministic script
|
|
927
|
+
# cannot produce. It describes the aggregate ROOT, the invariants it guards and its
|
|
928
|
+
# transaction boundary, and since the model split those are literally the CONTEXT'S
|
|
929
|
+
# DOMAIN MODEL's content: the plan only records where each invariant is enforced. So
|
|
930
|
+
# the model is the source where there is one, and the backend plan is the fallback
|
|
931
|
+
# for a feature that predates the split. Its own build_context — the envelope
|
|
932
|
+
# persists, and distilling the concept prose from a generation payload would describe
|
|
933
|
+
# the code rather than the aggregate.
|
|
934
|
+
# BOOTSTRAP ONLY: on the delta path finalize-delta re-authors it from the merged
|
|
935
|
+
# model, after the change has actually landed in it.
|
|
936
|
+
- id: overview_from_model
|
|
937
|
+
condition: "{{domain_model_exists}} == true"
|
|
938
|
+
action: set
|
|
939
|
+
vars: { overview_ok: "true" }
|
|
940
|
+
- id: overview_from_plan
|
|
941
|
+
condition: "{{be_go}} == true"
|
|
942
|
+
action: set
|
|
943
|
+
vars: { overview_ok: "true" }
|
|
944
|
+
- id: overview_delta_off
|
|
945
|
+
condition: "{{mode}} == delta"
|
|
946
|
+
action: set
|
|
947
|
+
vars: { overview_ok: "false" }
|
|
948
|
+
|
|
478
949
|
- id: ctx_overview
|
|
479
|
-
condition: "{{
|
|
950
|
+
condition: "{{overview_ok}} == true"
|
|
480
951
|
action: build_context
|
|
481
952
|
context:
|
|
482
|
-
- { path: "{{
|
|
953
|
+
- { path: "{{domain_model}}", optional: true }
|
|
954
|
+
- { path: "{{backend_plan}}", optional: true }
|
|
483
955
|
- id: okf_overview
|
|
484
|
-
condition: "{{
|
|
956
|
+
condition: "{{overview_ok}} == true"
|
|
485
957
|
action: agent_query
|
|
486
958
|
include_standards: false
|
|
487
959
|
schema: { overview: "<2-4 sentences of prose>" }
|
|
488
960
|
mock: { overview: "" }
|
|
489
961
|
task: |
|
|
490
|
-
Write the OKF knowledge-concept Overview for the aggregate
|
|
491
|
-
|
|
962
|
+
Write the OKF knowledge-concept Overview for the aggregate `{{feature}}`. 2-4
|
|
963
|
+
sentences of PROSE:
|
|
492
964
|
- name the aggregate ROOT and what the aggregate IS;
|
|
493
|
-
- state the INVARIANTS it guards;
|
|
494
|
-
- state its TRANSACTION BOUNDARY — what it OWNS inside the boundary vs what it
|
|
495
|
-
|
|
965
|
+
- state the INVARIANTS it guards (its BR-nnn — what they require, not their ids);
|
|
966
|
+
- state its TRANSACTION BOUNDARY — what it OWNS inside the boundary vs what it only
|
|
967
|
+
REFERENCES by id.
|
|
968
|
+
The bounded context's domain-model.md is the source where it is in context: read
|
|
969
|
+
ONLY its `## Aggregate: {{feature}}` section — the other aggregates are other
|
|
970
|
+
slices' concepts. Where it is not in context, distil the same three things from
|
|
971
|
+
backend-plan.md's domain section instead.
|
|
496
972
|
Plain prose only: no headings, no bullets, no code fences, no file paths, and no
|
|
497
973
|
restating of the dependency edges (those live in the Depends-On / Cross-References
|
|
498
|
-
tables). Describe only what the
|
|
499
|
-
whole aggregate rather than the one story that happens to be building now.
|
|
974
|
+
tables). Describe only what the document states — invent nothing.
|
|
500
975
|
outputs:
|
|
501
976
|
- { name: okf_overview, from: "$.overview" }
|
|
977
|
+
|
|
502
978
|
- id: okf_overview_write
|
|
503
|
-
condition: "{{
|
|
979
|
+
condition: "{{overview_ok}} == true"
|
|
504
980
|
action: execute_script
|
|
981
|
+
on_error: continue
|
|
505
982
|
script: { ref: xref }
|
|
506
983
|
subcommand: concept-overview
|
|
507
984
|
args: ["--feature-dir", "{{feature_dir}}", "--text", "{{okf_overview}}"]
|
|
508
985
|
|
|
509
|
-
#
|
|
510
|
-
# Generation is design-only by contract; this is the check that the design was not
|
|
511
|
-
# the wrong design. READ-ONLY and REPORT-ONLY: it never edits code, never edits the
|
|
512
|
-
# pass, and never stops the run. A gap here means one of two documents is wrong, and
|
|
513
|
-
# which one is a human decision — the message names both routes.
|
|
514
|
-
- id: ctx_accept
|
|
515
|
-
action: build_context
|
|
516
|
-
vars:
|
|
517
|
-
story_id: "{{story}}"
|
|
518
|
-
exercises_csv: "{{exercises_csv}}"
|
|
519
|
-
constraints_csv: "{{constraints_csv}}"
|
|
520
|
-
files_csv: "{{files}}"
|
|
521
|
-
context:
|
|
522
|
-
- { path: "{{story_path}}" } # the acceptance criteria
|
|
523
|
-
- { path: "{{spec_file}}" } # the contract — the ONLY step in this workflow that reads it
|
|
524
|
-
- { path: "{{design_doc}}" } # what was designed
|
|
525
|
-
- id: accept_check
|
|
526
|
-
action: agent_query
|
|
527
|
-
tools: Read # the generated files arrive as paths, not inlined
|
|
528
|
-
include_standards: false
|
|
529
|
-
schema: { ok: "<true|false>", gaps: "<one line per gap, or empty>" }
|
|
530
|
-
mock: { ok: "true", gaps: "" }
|
|
531
|
-
task: |
|
|
532
|
-
Story {{story_id}} was just implemented. Judge ONLY this: does the code that now
|
|
533
|
-
exists satisfy the requirements the story delivers, AS THE SPEC WORDS THEM?
|
|
534
|
-
|
|
535
|
-
In scope: {{exercises_csv}} (delivered) and {{constraints_csv}} (must hold).
|
|
536
|
-
Resolve each id's wording from spec.md in context — that wording, not the design's
|
|
537
|
-
restatement of it, is what the code is measured against.
|
|
538
|
-
|
|
539
|
-
Read the files this run wrote:
|
|
540
|
-
{{files_csv}}
|
|
541
|
-
plus anything they call that you need to follow. Do not modify a single file.
|
|
542
|
-
|
|
543
|
-
Report a gap ONLY where the code fails to deliver an in-scope id as the spec words
|
|
544
|
-
it, or breaks an in-scope constraint. These are NOT gaps, and reporting them is
|
|
545
|
-
noise that hides the real ones:
|
|
546
|
-
- a requirement no story has claimed yet, or one another story delivers;
|
|
547
|
-
- style, naming, structure or test-coverage opinions — other gates own those;
|
|
548
|
-
- anything the design pass deliberately deferred and named under its risks.
|
|
549
|
-
|
|
550
|
-
One line per gap: the id, what the spec requires, and what the code does instead.
|
|
551
|
-
Set ok=false only when you found at least one. If the SPEC and the DESIGN disagree
|
|
552
|
-
about an in-scope id, that IS a gap — say which document you believe is wrong and
|
|
553
|
-
why, because the two routes out of it are different commands.
|
|
554
|
-
outputs:
|
|
555
|
-
- { name: accept_ok, from: "$.ok" }
|
|
556
|
-
- { name: accept_gaps, from: "$.gaps" }
|
|
557
|
-
- id: accept_notice
|
|
558
|
-
condition: "{{accept_ok}} != true"
|
|
559
|
-
action: verify_context
|
|
560
|
-
require_value: accept_ok
|
|
561
|
-
equals: "true"
|
|
562
|
-
on_mismatch: continue
|
|
563
|
-
on_mismatch_message: |
|
|
564
|
-
ACCEPTANCE — story {{story}} does not yet satisfy the spec wording of the ids it delivers:
|
|
565
|
-
|
|
566
|
-
{{accept_gaps}}
|
|
567
|
-
|
|
568
|
-
Nothing was changed to fix this: the code follows design pass {{design_pass}}, so a gap
|
|
569
|
-
here means either the design was wrong or the spec has moved. Route by which it is —
|
|
570
|
-
the design is wrong: /appweaver story-plan {{story}} (a new pass supersedes this one);
|
|
571
|
-
the spec is wrong: /appweaver sync-spec, then sync-stories, then story-plan.
|
|
572
|
-
Run /appweaver build either way — a design that is right and code that does not compile
|
|
573
|
-
is a third thing, and build is where that one surfaces.
|
|
574
|
-
|
|
575
|
-
# 9. Post-execution hooks.
|
|
986
|
+
# 13. Post-execution hooks — the same phase set as the pre-hooks above.
|
|
576
987
|
- id: resolve_post_hooks
|
|
577
988
|
action: execute_script
|
|
578
989
|
script: { ref: actions }
|
|
579
990
|
subcommand: resolve-hooks
|
|
580
|
-
args: ["--phase", "after_implement"]
|
|
991
|
+
args: ["--phase", "after_implement", "--phase", "{{post_ui_phase}}", "--phase", "{{post_cr_phase}}"]
|
|
581
992
|
outputs:
|
|
582
993
|
- { name: after_hooks, from: "$.hooks" }
|
|
583
994
|
- id: dispatch_post_hooks
|
|
@@ -585,12 +996,19 @@ workflow:
|
|
|
585
996
|
hooks_from_variable: after_hooks
|
|
586
997
|
|
|
587
998
|
outputs:
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
999
|
+
feature_dir: "{{feature_dir}}"
|
|
1000
|
+
bounded_context: "{{context}}"
|
|
1001
|
+
mode: "{{mode}}"
|
|
1002
|
+
cr_id: "{{active_change}}"
|
|
1003
|
+
backend_implemented: "{{be_go}}"
|
|
1004
|
+
backend_plan: "{{be_plan}}" # the document the backend lane generated FROM
|
|
1005
|
+
backend_files: "{{be_files}}"
|
|
1006
|
+
frontend_implemented: "{{fe_go}}"
|
|
1007
|
+
frontend_plan: "{{fe_plan}}" # the document the frontend lane generated FROM
|
|
1008
|
+
frontend_files: "{{fe_files}}"
|
|
1009
|
+
domain_model: "{{domain_model}}" # the master model every generating lane read
|
|
1010
|
+
delta_model: "{{delta_model}}" # its second half on the delta path — empty on the bootstrap path
|
|
1011
|
+
okf_concept: "{{okf_concept}}"
|
|
1012
|
+
xref_edge_count: "{{xref_edge_count}}"
|
|
1013
|
+
note: "Lanes come from .appweaver/stacks.json — backend declared {{be_declared}} (ready {{be_runnable}}, implemented {{be_go}}), frontend declared {{fe_declared}} (ready {{fe_runnable}}, implemented {{fe_go}}). A declared lane that was skipped printed its reason above and did not stop the other one; an undeclared lane produces nothing and that is not a failure. Each generating lane read its PLAN for what to build and {{domain_model}} for what the domain is — plus {{delta_model}} on the delta path, since the master model does not carry the change until finalize-delta merges it. Every one of those documents was READ and none was written: this phase generates code and nothing else."
|
|
1014
|
+
next: "This run was mode `{{mode}}`, change `{{active_change}}` (empty on the bootstrap path). BOOTSTRAP → /appweaver build → analyze-slice → run. DELTA → /appweaver build → analyze-slice-delta {{active_change}} → finalize-delta → analyze-slice → close-delta, with the change still OPEN throughout — compile first, then analyze-slice-delta verifies that every element the delta plans marked ADDED or MODIFIED is actually in the code with a paired test (the gate that catches a lane you declined at the prompt above), then finalize-delta merges it into spec.md + the context's domain-model.md while leaving it open, analyze-slice verifies the slice against those merged masters while a red result is still recoverable, and close-delta LAST archives the change and bumps the version."
|