aw-spec-cli 0.1.18 → 0.1.20
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 +63 -31
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +62 -14
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +10 -6
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +9 -1
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +21 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +23 -1
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +22 -13
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +6 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +399 -131
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +199 -38
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +159 -25
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +20 -9
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +183 -40
- package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +311 -36
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +221 -40
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +217 -174
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +494 -109
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +83 -18
- package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +319 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +29 -23
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +143 -34
- package/assets/.appweaver/scripts/bash/aw-inventory.sh +15 -0
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +878 -51
- package/assets/.appweaver/scripts/bash/aw-xref.sh +3 -0
- package/assets/.appweaver/scripts/bash/common.sh +28 -1
- package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +15 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +868 -51
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +3 -0
- package/assets/.appweaver/scripts/python/aw-inventory.py +1832 -65
- package/assets/.appweaver/scripts/python/aw-xref.py +500 -81
- package/assets/.appweaver/templates/delta-design-template.md +1 -1
- package/assets/.appweaver/templates/delta-domain-model-template.md +1 -1
- package/assets/.appweaver/templates/delta-spec-template.md +4 -3
- package/assets/.appweaver/templates/design-template.md +56 -40
- package/assets/.appweaver/templates/okf-concept-template.md +11 -1
- package/assets/.appweaver/templates/proposed-inventory-template.md +127 -0
- package/assets/.appweaver/templates/source-map-template.md +5 -3
- package/assets/.appweaver/templates/spec-template.md +24 -21
- package/assets/.appweaver/templates/ui-delta-design-template.md +1 -1
- package/assets/.appweaver/templates/ui-design-template.md +11 -7
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +4489 -361
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +2274 -125
- package/assets/guardrails/domain/healthcare/auditing.domain.md +3 -3
- package/assets/guardrails/domain/pharma/auditing.domain.md +3 -3
- package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/authorization.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/data-classification.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/eventing.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/persistence.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +1 -1
- package/assets/guardrails/technology/nextjs/accessibility-implementation.md +1 -1
- package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +1 -1
- package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +1 -1
- package/assets/guardrails/technology/nextjs/auth-session.md +1 -1
- package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
- package/assets/guardrails/technology/nextjs/design-system.md +1 -1
- package/assets/guardrails/technology/nextjs/error-handling.md +1 -1
- package/assets/guardrails/technology/nextjs/forms-and-validation.md +1 -1
- package/assets/guardrails/technology/nextjs/i18n.md +1 -1
- package/assets/guardrails/technology/nextjs/performance-budgets.md +1 -1
- package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
- package/assets/guardrails/technology/nextjs/state-management.md +1 -1
- package/package.json +1 -1
|
@@ -42,6 +42,22 @@
|
|
|
42
42
|
# spec.md — analyze-plan already conformed the plans to it, so the plan is the complete,
|
|
43
43
|
# authoritative source for WHICH elements exist, where each lives and what surface they
|
|
44
44
|
# publish, and anything missing from it is fixed in `plan`, never compensated for here.
|
|
45
|
+
#
|
|
46
|
+
# AND THAT SENTENCE IS NOW CHECKED. "analyze-plan already conformed them" was an assumption
|
|
47
|
+
# about the order the operator ran things in: the lane gates test that the plan FILE EXISTS
|
|
48
|
+
# and nothing more, so `plan` → edit spec.md → `implement` generated at full length from a
|
|
49
|
+
# plan nobody had held against the spec it now contradicts. analyze-plan leaves a receipt of
|
|
50
|
+
# the bytes it proved and plan-gate-check reads it here, on the bootstrap path, before any
|
|
51
|
+
# lane generates. A feature with no receipt has simply never been gated since the receipt
|
|
52
|
+
# existed — reported, never refused. One that HAS a receipt and no longer matches it stops.
|
|
53
|
+
# 3.2.0 — AND NOT delta-spec.md EITHER. The change path used to inline it "only so the
|
|
54
|
+
# paired tests can cite traceability ids", which made the rule true on the bootstrap path
|
|
55
|
+
# and false on the one where changes actually ship: a document in the payload is a document
|
|
56
|
+
# the generator can reason from, so a gap in a delta plan could be quietly filled from the
|
|
57
|
+
# requirement text and the plan never found to be wrong. Both paths now carry plan + model
|
|
58
|
+
# and nothing else. The ids are still cited and still required — the delta plan names the
|
|
59
|
+
# spec id each element serves, which is exactly what analyze-plan-delta conformed, and an
|
|
60
|
+
# element naming none is a planning gap to surface rather than a traceability to invent.
|
|
45
61
|
# But since the model split a plan CITES an invariant by id and never restates it, so the
|
|
46
62
|
# bounded context's domain-model.md is in context too, as the authority on the entity
|
|
47
63
|
# names, the aggregate boundary, the lifecycle states and the TEXT of every BR-nnn the plan
|
|
@@ -76,6 +92,14 @@
|
|
|
76
92
|
# found only elsewhere in the repository is the service lane's, and accepting it is how a
|
|
77
93
|
# frontend that was never scaffolded is generated into anyway and reports green.
|
|
78
94
|
#
|
|
95
|
+
# A LANE'S GAP DISABLES THAT LANE, NOT THE RUN. Three things a lane needs are checked per
|
|
96
|
+
# lane and reported per lane: its source path is on disk, its plan is there, and a project
|
|
97
|
+
# is scaffolded under it. Any of them missing skips that lane with the command that fixes
|
|
98
|
+
# it named, and every other declared lane still generates; only when NOTHING can run does
|
|
99
|
+
# the run stop, with all the reasons printed. The whole-run stop is reserved for states
|
|
100
|
+
# with no correct lane to fall back on — no lane declared at all, a lane declaring no
|
|
101
|
+
# source path, and two lanes claiming overlapping subtrees.
|
|
102
|
+
#
|
|
79
103
|
# WHAT IT DOES NOT DO. It never writes spec.md, a master plan or the domain model, and it
|
|
80
104
|
# never closes a change — finalize-delta merges the change's spec and model halves,
|
|
81
105
|
# analyze-slice verifies the slice, and close-delta archives it and bumps the version.
|
|
@@ -91,8 +115,8 @@
|
|
|
91
115
|
# =============================================================================
|
|
92
116
|
|
|
93
117
|
name: aw-spec-implement
|
|
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
|
|
95
|
-
version: 3.
|
|
118
|
+
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 source path, its own plan and its own scaffolded project, and a lane missing any of them 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."
|
|
119
|
+
version: 3.4.0
|
|
96
120
|
agent_agnostic: true
|
|
97
121
|
dialect: aw-spec.orchestrator/v1
|
|
98
122
|
|
|
@@ -138,6 +162,20 @@ vars:
|
|
|
138
162
|
delta_frontend_plan: ""
|
|
139
163
|
delta_frontend_plan_exists: "false"
|
|
140
164
|
wave: ""
|
|
165
|
+
# THE GATE RECEIPT — was this feature's plan pair ever PROVEN against this spec, and has
|
|
166
|
+
# anything moved since. analyze-plan writes it on a green master run; this phase is the
|
|
167
|
+
# reader. Bootstrap path only: a delta generates from delta plans, which analyze-plan-delta
|
|
168
|
+
# gates, and the master receipt says nothing about those.
|
|
169
|
+
gate_tracked: "false"
|
|
170
|
+
gate_fresh: "false"
|
|
171
|
+
gate_reason: ""
|
|
172
|
+
gate_at: ""
|
|
173
|
+
# DEFAULTED PERMISSIVE, DELIBERATELY, and it is the one flag here that is. Every tree that
|
|
174
|
+
# predates the receipt has none, and "never gated" is an unknown, not a stale plan —
|
|
175
|
+
# refusing on a missing file would block every existing feature on its first run. The flag
|
|
176
|
+
# is lowered only by a receipt that EXISTS and no longer matches: that tree has been through
|
|
177
|
+
# the gate once, so the bytes moving since is a fact, not an absence of one.
|
|
178
|
+
may_generate: "true"
|
|
141
179
|
# PER-LANE STATE. Each lane resolves its own plan, its own project and its own answer;
|
|
142
180
|
# a lane that is not declared never resolves any of them and its steps never run.
|
|
143
181
|
# The engine has no compound conditions, so each AND is an ordered assignment: a step
|
|
@@ -182,22 +220,32 @@ workflow:
|
|
|
182
220
|
|
|
183
221
|
# 0. THE LANE MODEL. lane-verify with NO --require, because this phase requires neither
|
|
184
222
|
# 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.
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
223
|
+
# lanes exist is exactly what it is here to find out.
|
|
224
|
+
#
|
|
225
|
+
# --allow-missing-root, because a lane whose source path is not on disk is THAT LANE'S
|
|
226
|
+
# gap, not the run's. This phase is per-lane throughout — a missing plan or a missing
|
|
227
|
+
# project is reported at step 5 and the other lane still generates — and a missing root
|
|
228
|
+
# is the same kind of gap, most often a declared frontend that `foundation` has not
|
|
229
|
+
# unpacked yet. With the check left on it arrives here instead, as a whole-run stop
|
|
230
|
+
# that takes the ready lane down with it. It is re-checked per lane at step 5 from
|
|
231
|
+
# `*_root_exists`, which lane-verify reports whether or not the on-disk half is armed.
|
|
232
|
+
#
|
|
233
|
+
# What still stops the run: no lane declared at all, a lane declaring no source path,
|
|
234
|
+
# and two lanes claiming overlapping subtrees. Those are declaration errors with no
|
|
235
|
+
# correct lane to fall back to, and none of them is a state any phase can work around.
|
|
189
236
|
- id: lane_verify
|
|
190
237
|
action: execute_script
|
|
191
238
|
on_error: continue
|
|
192
239
|
script: { ref: actions }
|
|
193
240
|
subcommand: lane-verify
|
|
194
|
-
args: ["--command", "/appweaver implement"]
|
|
241
|
+
args: ["--command", "/appweaver implement", "--allow-missing-root"]
|
|
195
242
|
outputs:
|
|
196
243
|
- { name: lane_ok, from: "$.ok" }
|
|
197
244
|
- { name: lane_issues, from: "$.message" }
|
|
198
245
|
- { name: be_declared, from: "$.backend_declared" }
|
|
199
246
|
- { name: be_lane_id, from: "$.backend_lane_id" }
|
|
200
247
|
- { name: be_source_path, from: "$.backend_source_path" }
|
|
248
|
+
- { name: be_root_exists, from: "$.backend_root_exists" }
|
|
201
249
|
- { name: be_profile, from: "$.backend_profile" }
|
|
202
250
|
- { name: be_title, from: "$.backend_title" }
|
|
203
251
|
- { name: be_language, from: "$.backend_language" }
|
|
@@ -207,6 +255,7 @@ workflow:
|
|
|
207
255
|
- { name: fe_declared, from: "$.frontend_declared" }
|
|
208
256
|
- { name: fe_lane_id, from: "$.frontend_lane_id" }
|
|
209
257
|
- { name: fe_source_path, from: "$.frontend_source_path" }
|
|
258
|
+
- { name: fe_root_exists, from: "$.frontend_root_exists" }
|
|
210
259
|
- { name: fe_profile, from: "$.frontend_profile" }
|
|
211
260
|
- { name: fe_title, from: "$.frontend_title" }
|
|
212
261
|
- { name: fe_language, from: "$.frontend_language" }
|
|
@@ -244,7 +293,7 @@ workflow:
|
|
|
244
293
|
action: verify_context
|
|
245
294
|
require_non_empty: feature_dir
|
|
246
295
|
on_empty: stop
|
|
247
|
-
on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the
|
|
296
|
+
on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the feature, e.g. Order or PatientRegistration), then re-run implement."
|
|
248
297
|
|
|
249
298
|
# The bounded context's DOMAIN MODEL — in every generating lane's payload as the authority
|
|
250
299
|
# on what the plan's cited BR-nnn actually require and on the ubiquitous language, and the
|
|
@@ -260,6 +309,21 @@ workflow:
|
|
|
260
309
|
- { name: domain_model, from: "$.domain_model" }
|
|
261
310
|
- { name: domain_model_exists, from: "$.domain_model_exists" }
|
|
262
311
|
|
|
312
|
+
# WHICH PART OF THAT MODEL IS THIS FEATURE'S. The model is filed by aggregate, the specs
|
|
313
|
+
# tree by feature, and the two names differ for a feature that is one capability over a
|
|
314
|
+
# shared aggregate (PatientRegistration over Patient) — reading the model by the
|
|
315
|
+
# feature's name finds no section there. Resolved from the spec's Acts-on line, then the
|
|
316
|
+
# boundary map, then the feature name, so a 1:1 feature resolves to exactly the section
|
|
317
|
+
# this workflow always read.
|
|
318
|
+
- id: feature_aggregates
|
|
319
|
+
action: execute_script
|
|
320
|
+
script: { ref: xref }
|
|
321
|
+
subcommand: feature-aggregates
|
|
322
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
323
|
+
outputs:
|
|
324
|
+
- { name: owned_csv, from: "$.owned_csv" }
|
|
325
|
+
- { name: owned_sections, from: "$.owned_sections" }
|
|
326
|
+
|
|
263
327
|
# 2. WHICH CHANGES ARE OPEN — the evidence is changes/<CR-id>/ on disk; a closed change
|
|
264
328
|
# has moved to changes/archive/. Any open change makes this run a DELTA run.
|
|
265
329
|
# `matched_label` resolves a CR id passed as the argument to the whole option string,
|
|
@@ -318,6 +382,10 @@ workflow:
|
|
|
318
382
|
outputs:
|
|
319
383
|
- { name: cr_id, from: "$.cr_id" }
|
|
320
384
|
- { name: cr_dir, from: "$.cr_dir" }
|
|
385
|
+
# Resolved for reporting and DELIBERATELY NOT put in any context — see the header.
|
|
386
|
+
# This phase realises the delta PLAN; the requirement text reaching the generator is
|
|
387
|
+
# what lets a gap in the plan be compensated for instead of found. Do not add it to
|
|
388
|
+
# ctx_backend_delta / ctx_frontend_delta.
|
|
321
389
|
- { name: delta_spec, from: "$.delta_spec" }
|
|
322
390
|
# THIS CHANGE'S MODEL HALF — the structure the delta plans realise. It is context for
|
|
323
391
|
# every generating lane, exactly as the master model is: the delta plan cites the
|
|
@@ -350,6 +418,49 @@ workflow:
|
|
|
350
418
|
action: set
|
|
351
419
|
vars: { fe_plan: "{{delta_frontend_plan}}", fe_plan_ok: "{{delta_frontend_plan_exists}}" }
|
|
352
420
|
|
|
421
|
+
# 3b. WERE THESE PLANS PROVEN AGAINST THIS SPEC. This phase does not read spec.md — the
|
|
422
|
+
# header's reason is "analyze-plan already conformed the plans to it", which was an
|
|
423
|
+
# assumption about the order the operator ran things in and was checked by nothing:
|
|
424
|
+
# the lane gates below test that the plan FILE EXISTS and stop there. `plan`, edit
|
|
425
|
+
# spec.md, `implement` therefore generated from a plan that was never held against the
|
|
426
|
+
# spec it now contradicts, silently and at full length.
|
|
427
|
+
#
|
|
428
|
+
# plan-gate-check compares today's bytes against the receipt analyze-plan left when it
|
|
429
|
+
# passed — spec.md, the context's domain-model.md and each gated lane plan. The plans'
|
|
430
|
+
# own `**Version**:` headers cannot answer this and must not be used: close-delta stamps
|
|
431
|
+
# them and spec.md to the same number in lockstep whether or not a plan was re-derived,
|
|
432
|
+
# so the comparison reads equal always.
|
|
433
|
+
#
|
|
434
|
+
# BOOTSTRAP ONLY. A delta run generates from delta plans that analyze-plan-delta gates;
|
|
435
|
+
# the master receipt is silent about them, and reading it there would refuse a change
|
|
436
|
+
# for drift in documents this run is not building from.
|
|
437
|
+
- id: plan_gate_check
|
|
438
|
+
condition: "{{mode}} == bootstrap"
|
|
439
|
+
action: execute_script
|
|
440
|
+
script: { ref: actions }
|
|
441
|
+
subcommand: plan-gate-check
|
|
442
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
443
|
+
outputs:
|
|
444
|
+
- { name: gate_tracked, from: "$.tracked" }
|
|
445
|
+
- { name: gate_fresh, from: "$.gate_fresh" }
|
|
446
|
+
- { name: gate_reason, from: "$.reason" }
|
|
447
|
+
- { name: gate_at, from: "$.gated" }
|
|
448
|
+
|
|
449
|
+
# The ordered assignment is the AND: only a TRACKED feature can lower the flag, and
|
|
450
|
+
# what lowers it is the gate no longer being fresh. An untracked one never reaches
|
|
451
|
+
# this step's assignment and generates, which is the whole accommodation for trees
|
|
452
|
+
# that have not been gated since the receipt existed.
|
|
453
|
+
- id: flag_gate_block
|
|
454
|
+
condition: "{{gate_tracked}} == true"
|
|
455
|
+
action: set
|
|
456
|
+
vars: { may_generate: "{{gate_fresh}}" }
|
|
457
|
+
|
|
458
|
+
- id: require_gate_fresh
|
|
459
|
+
action: verify_context
|
|
460
|
+
require_true: may_generate
|
|
461
|
+
on_false: stop
|
|
462
|
+
on_false_message: "implement REFUSED — the master plans are no longer the ones analyze-plan passed: {{gate_reason}}. This phase generates from the plans and does NOT read the spec, on the strength of that gate having conformed them to it, so generating now would realise a plan against requirements nobody checked it against. Re-run /appweaver analyze-plan — it re-checks all four checks, repairs the drifting plan on approval, and re-issues the receipt — then re-run /appweaver implement. If what changed was a plan you re-derived with /appweaver plan main, that is the same path: the re-derived plans have not been gated yet. Nothing was generated."
|
|
463
|
+
|
|
353
464
|
# 4. THE PROJECT each declared lane generates INTO. Deterministic structure gate:
|
|
354
465
|
# detect-project searches the LANE'S OWN subtree for an ecosystem manifest (.sln/
|
|
355
466
|
# .csproj, pom.xml, build.gradle, package.json, go.mod, Cargo.toml, pyproject.toml),
|
|
@@ -396,6 +507,12 @@ workflow:
|
|
|
396
507
|
require_true: be_plan_ok
|
|
397
508
|
on_false: continue
|
|
398
509
|
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."
|
|
510
|
+
- id: warn_be_root
|
|
511
|
+
condition: "{{be_declared}} == true"
|
|
512
|
+
action: verify_context
|
|
513
|
+
require_true: be_root_exists
|
|
514
|
+
on_false: continue
|
|
515
|
+
on_false_message: "BACKEND LANE SKIPPED — lane `{{be_lane_id}}` ({{be_title}}) declares source path `{{be_source_path}}` in .appweaver/stacks.json, and that path is not a directory in this repository. A lane owns its subtree and this phase writes into it, so there is nothing to generate into: unpack a project there with /appweaver foundation <FoundationName> <AppName> where a bundle ships for {{be_profile}}, create one with the stack's own tooling, or re-run /appweaver select-stack and point the lane at the directory that really holds its source. Nothing is generated for this lane; any other declared lane still runs."
|
|
399
516
|
- id: warn_be_project
|
|
400
517
|
condition: "{{be_declared}} == true"
|
|
401
518
|
action: verify_context
|
|
@@ -408,6 +525,12 @@ workflow:
|
|
|
408
525
|
require_true: fe_plan_ok
|
|
409
526
|
on_false: continue
|
|
410
527
|
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."
|
|
528
|
+
- id: warn_fe_root
|
|
529
|
+
condition: "{{fe_declared}} == true"
|
|
530
|
+
action: verify_context
|
|
531
|
+
require_true: fe_root_exists
|
|
532
|
+
on_false: continue
|
|
533
|
+
on_false_message: "FRONTEND LANE SKIPPED — lane `{{fe_lane_id}}` ({{fe_title}}) declares source path `{{fe_source_path}}` in .appweaver/stacks.json, and that path is not a directory in this repository. A lane owns its subtree and this phase writes into it, so there is nothing to generate into: unpack a project there with /appweaver foundation <FoundationName> <AppName> where a bundle ships for {{fe_profile}}, create one with the stack's own tooling, or re-run /appweaver select-stack and point the lane at the directory that really holds its source. Nothing is generated for this lane; any other declared lane still runs."
|
|
411
534
|
- id: warn_fe_project
|
|
412
535
|
condition: "{{fe_declared}} == true"
|
|
413
536
|
action: verify_context
|
|
@@ -415,14 +538,18 @@ workflow:
|
|
|
415
538
|
on_false: continue
|
|
416
539
|
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
540
|
|
|
418
|
-
# 6. RUNNABLE = declared AND its
|
|
419
|
-
#
|
|
420
|
-
# undeclared lane stays false either way —
|
|
421
|
-
# assigns.
|
|
541
|
+
# 6. RUNNABLE = declared AND its source path is on disk AND its plan is there AND its
|
|
542
|
+
# project is there, as one ordered assignment plus a knockdown per condition, since
|
|
543
|
+
# the engine has no compound conditions. An undeclared lane stays false either way —
|
|
544
|
+
# its `declared` value is what the raise assigns.
|
|
422
545
|
- id: be_runnable_raise
|
|
423
546
|
condition: "{{be_plan_ok}} == true"
|
|
424
547
|
action: set
|
|
425
548
|
vars: { be_runnable: "{{be_declared}}" }
|
|
549
|
+
- id: be_runnable_clear_root
|
|
550
|
+
condition: "{{be_root_exists}} == false"
|
|
551
|
+
action: set
|
|
552
|
+
vars: { be_runnable: "false" }
|
|
426
553
|
- id: be_runnable_clear
|
|
427
554
|
condition: "{{be_project_ok}} == false"
|
|
428
555
|
action: set
|
|
@@ -431,6 +558,10 @@ workflow:
|
|
|
431
558
|
condition: "{{fe_plan_ok}} == true"
|
|
432
559
|
action: set
|
|
433
560
|
vars: { fe_runnable: "{{fe_declared}}" }
|
|
561
|
+
- id: fe_runnable_clear_root
|
|
562
|
+
condition: "{{fe_root_exists}} == false"
|
|
563
|
+
action: set
|
|
564
|
+
vars: { fe_runnable: "false" }
|
|
434
565
|
- id: fe_runnable_clear
|
|
435
566
|
condition: "{{fe_project_ok}} == false"
|
|
436
567
|
action: set
|
|
@@ -471,7 +602,7 @@ workflow:
|
|
|
471
602
|
action: verify_context
|
|
472
603
|
require_true: generate_any
|
|
473
604
|
on_false: stop
|
|
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."
|
|
605
|
+
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 source path, 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
606
|
|
|
476
607
|
# 8. EACH GENERATING LANE'S authoring MODE — the run's mode where the lane is going, and
|
|
477
608
|
# `skip` where it is not, which silences both of that lane's branches without a
|
|
@@ -490,18 +621,21 @@ workflow:
|
|
|
490
621
|
# backend's heading is keyed by mode (see the header). No blocking gate: an
|
|
491
622
|
# unconfigured project soft-continues, since the constitution halves still carry each
|
|
492
623
|
# lane's ratified base.
|
|
624
|
+
#
|
|
625
|
+
# EACH LANE'S RULES ARE LOADED BESIDE ITS OWN STEPS, AND THE SECOND LANE REPLACES
|
|
626
|
+
# THE FIRST'S. Both includes used to run here, before either lane generated — and
|
|
627
|
+
# sticky standards only accumulate, so on a full-stack run the backend generator was
|
|
628
|
+
# shown `## /aw-spec-implement-ui` and the frontend generator the service set. Each
|
|
629
|
+
# prompt says which lane it is in, but it said so inside an envelope that contradicted
|
|
630
|
+
# it, and the envelope is the part the model cannot argue with. The frontend include
|
|
631
|
+
# now sits immediately before the FRONTEND sections below and passes replace, which
|
|
632
|
+
# resets to the workflow's declared seed (ai-rules) before loading the UI set.
|
|
493
633
|
- id: tech_select
|
|
494
634
|
condition: "{{be_go}} == true"
|
|
495
635
|
action: include
|
|
496
636
|
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
497
637
|
with: { command: "{{be_tech_command}}" }
|
|
498
638
|
|
|
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 }
|
|
504
|
-
|
|
505
639
|
# 10. Pre-execution hooks — see the hook-phase vars above.
|
|
506
640
|
- id: resolve_pre_hooks
|
|
507
641
|
action: execute_script
|
|
@@ -551,9 +685,9 @@ workflow:
|
|
|
551
685
|
- THE MODEL IS READ, NEVER MINED FOR WORK. It describes the WHOLE bounded context,
|
|
552
686
|
while this run builds one feature. Realise only what the PLAN calls for: an entity,
|
|
553
687
|
event or invariant in the model that no plan element realises is a PLANNING gap —
|
|
554
|
-
surface it, do not implement it.
|
|
555
|
-
any other `## Aggregate:` section
|
|
556
|
-
|
|
688
|
+
surface it, do not implement it. The section(s) this feature answers for are
|
|
689
|
+
{{owned_sections}}; any other `## Aggregate:` section you read only to honour a
|
|
690
|
+
reference the plan makes, never to generate its code.
|
|
557
691
|
- BIND THE STACK: this lane is `{{be_lane_id}}` — {{be_title}}, language
|
|
558
692
|
{{be_language}} ({{be_ecosystem}}), declared in .appweaver/stacks.json and compiled
|
|
559
693
|
with `{{be_build}}`, tested with `{{be_test}}`. Generate in THAT language and
|
|
@@ -596,7 +730,14 @@ workflow:
|
|
|
596
730
|
- Do NOT add behaviour the plan does not state, and do NOT omit any it does. If the
|
|
597
731
|
plan looks wrong or incomplete, STOP and surface it (it is fixed in plan /
|
|
598
732
|
analyze-plan) — never compensate from the spec or from memory.
|
|
599
|
-
-
|
|
733
|
+
- SHARED AGGREGATE FILES ARE EXTENDED, NEVER REGENERATED. A feature is a capability
|
|
734
|
+
scope, and other features may act on the same aggregate: its root, entities,
|
|
735
|
+
value objects and repository are shared code. Where such a file already exists,
|
|
736
|
+
ADD only the members this plan needs and leave every existing member, signature
|
|
737
|
+
and test exactly as it is — another feature's code depends on them. Create a file
|
|
738
|
+
only when it does not exist yet. A file that serves only another feature (its
|
|
739
|
+
use-case, endpoint or handler) is not this feature's to touch; if this plan needs
|
|
740
|
+
one changed, STOP and surface it.
|
|
600
741
|
- Where generated code depends on a plan Assumption (DA-n) or Workaround (WA-n), mark
|
|
601
742
|
it with a comment citing that id so it is easy to revisit later.
|
|
602
743
|
outputs:
|
|
@@ -605,14 +746,22 @@ workflow:
|
|
|
605
746
|
# ═══════════ BACKEND LANE — delta, generate ONLY what the change adds ════════
|
|
606
747
|
# The delta plan is the authoritative source of WHAT changed — analyze-plan-delta already
|
|
607
748
|
# conformed it to the delta spec. The MASTER plan is the BASE (the unchanged elements the
|
|
608
|
-
# delta depends on)
|
|
609
|
-
#
|
|
749
|
+
# delta depends on).
|
|
750
|
+
#
|
|
751
|
+
# NO DELTA-SPEC HERE, exactly as the bootstrap path carries no spec. It used to be
|
|
752
|
+
# inlined "only so the paired tests can cite traceability ids", but a document in the
|
|
753
|
+
# payload is a document the generator can reason from, and the whole point of the
|
|
754
|
+
# contract is that this phase realises the PLAN: with the requirement text in reach, a
|
|
755
|
+
# gap in the plan can be silently compensated for from the spec and the plan is never
|
|
756
|
+
# found to be wrong. The ids are still available and still required — the delta plan
|
|
757
|
+
# cites the spec id each element serves, which is what analyze-plan-delta conformed.
|
|
758
|
+
# A plan that does not name an id is a planning gap to surface, and that is now visible
|
|
759
|
+
# instead of being papered over here.
|
|
610
760
|
- id: ctx_backend_delta
|
|
611
761
|
condition: "{{be_mode}} == delta"
|
|
612
762
|
action: build_context
|
|
613
763
|
context:
|
|
614
764
|
- { path: "{{be_plan}}" } # this change's delta-backend-plan.md
|
|
615
|
-
- { path: "{{delta_spec}}", optional: true }
|
|
616
765
|
- { path: "{{delta_model}}", optional: true } # this change's MODEL half — what it ADDS to / CHANGES in the model
|
|
617
766
|
- { path: "{{backend_plan}}", optional: true } # the master plan — READ ONLY, never amended here
|
|
618
767
|
- { path: "{{domain_model}}", optional: true } # the master model — the domain as it stands BEFORE this change
|
|
@@ -642,9 +791,12 @@ workflow:
|
|
|
642
791
|
winning wherever they differ — it is the newer statement, and the master has not
|
|
643
792
|
been merged yet (finalize-delta does that after this phase). Read there what every
|
|
644
793
|
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
|
|
646
|
-
the
|
|
647
|
-
|
|
794
|
+
enforced and never restates it. You have NO domain rule files and NO spec AT ALL —
|
|
795
|
+
not the master and not this change's delta-spec. The requirement ids are in the
|
|
796
|
+
delta plan, which cites the one each element serves; that is the whole of what you
|
|
797
|
+
get. If a domain specific is in neither the plan nor the model, STOP and surface it
|
|
798
|
+
(it is fixed in specify / plan / analyze-plan-delta), never invent it from memory
|
|
799
|
+
and never infer it from an id.
|
|
648
800
|
- THE MODEL IS READ, NEVER MINED FOR WORK. Realise only what the DELTA PLAN calls
|
|
649
801
|
for: an item in the model delta that no delta-plan element realises is a PLANNING
|
|
650
802
|
gap — surface it, do not implement it. Everything in the master model that this
|
|
@@ -682,7 +834,10 @@ workflow:
|
|
|
682
834
|
the port isn't present, code to it and note that as the prerequisite.
|
|
683
835
|
- Author a paired test for every ADDED/MODIFIED behaviour / use-case / endpoint;
|
|
684
836
|
derive the cases from the delta plan's pre/post/invariant contracts and cite the
|
|
685
|
-
|
|
837
|
+
requirement id THE DELTA PLAN NAMES for that element (FR / BR / CN / DC / IC / US).
|
|
838
|
+
The spec is not in context: take the id from the plan and cite it verbatim, never
|
|
839
|
+
guess one from the element's shape. A delta-plan element that names no id is a
|
|
840
|
+
planning gap — surface it rather than inventing the traceability.
|
|
686
841
|
- REGRESSION: for each element in the delta plan's "Regression targets", verify (add
|
|
687
842
|
or adjust a test where needed) that the existing behaviour still holds after a
|
|
688
843
|
shared element changed — but do NOT rewrite that existing behaviour.
|
|
@@ -714,6 +869,22 @@ workflow:
|
|
|
714
869
|
outputs:
|
|
715
870
|
- { name: okf_concept, from: "$.concept" }
|
|
716
871
|
|
|
872
|
+
# ══════════════ THE LANE SWAP — the frontend's rules replace the backend's ═══
|
|
873
|
+
# build_context already scoped the two lanes' PAYLOADS apart; this is the same boundary
|
|
874
|
+
# for their STANDARDS, which the envelope carries and which nothing used to reset. It
|
|
875
|
+
# runs here rather than up with the backend's include because standards accumulate:
|
|
876
|
+
# loading both before either lane generated is precisely what showed each generator the
|
|
877
|
+
# other lane's rule set. `replace` resets to the declared seed (ai-rules stays) and then
|
|
878
|
+
# loads `## /aw-spec-implement-ui`, so from here down the envelope is the frontend's.
|
|
879
|
+
#
|
|
880
|
+
# Nothing after the frontend sections regenerates backend code — the OKF steps that
|
|
881
|
+
# follow run include_standards: false — so the swap needs no undo.
|
|
882
|
+
- id: tech_select_ui
|
|
883
|
+
condition: "{{fe_go}} == true"
|
|
884
|
+
action: include
|
|
885
|
+
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
886
|
+
with: { command: aw-spec-implement-ui, replace: "true" }
|
|
887
|
+
|
|
717
888
|
# ═══════════ FRONTEND LANE — bootstrap, generate from the MASTER plan ════════
|
|
718
889
|
# The experience twin of the service generation, one lane over. Its own build_context,
|
|
719
890
|
# because the envelope persists across steps and the two lanes must not see each other's
|
|
@@ -815,7 +986,8 @@ workflow:
|
|
|
815
986
|
action: build_context
|
|
816
987
|
context:
|
|
817
988
|
- { path: "{{fe_plan}}" } # this change's delta-frontend-plan.md
|
|
818
|
-
-
|
|
989
|
+
# No delta-spec — see the service lane's note above: this phase realises the PLAN,
|
|
990
|
+
# and the ids the tests cite come from the plan's own rows.
|
|
819
991
|
- { path: "{{delta_model}}", optional: true } # this change's MODEL half — new terms, states and rules the UI must speak
|
|
820
992
|
- { path: "{{frontend_plan}}", optional: true } # the master plan — READ ONLY, never amended here
|
|
821
993
|
- { path: "{{domain_model}}", optional: true } # the master model — the ubiquitous language as it stands BEFORE this change
|
|
@@ -846,9 +1018,10 @@ workflow:
|
|
|
846
1018
|
READ TOGETHER, the delta winning wherever they differ — the master has not been
|
|
847
1019
|
merged yet. A TERM THIS CHANGE INTRODUCES OR RENAMES IS IN THE DELTA, and the UI
|
|
848
1020
|
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
|
|
850
|
-
spec
|
|
851
|
-
|
|
1021
|
+
there what any BR-nnn the delta plan cites requires. NO domain rule files, and NO
|
|
1022
|
+
spec at all — not the master and not this change's delta-spec; the requirement ids
|
|
1023
|
+
come from the delta plan, which cites the one each element serves. If a domain
|
|
1024
|
+
specific is in neither the plan nor the model, STOP and surface it.
|
|
852
1025
|
- THE MODEL IS READ, NEVER MINED FOR WORK, and reading a rule is not permission to
|
|
853
1026
|
move it. Realise only what the DELTA PLAN calls for — an item in the model delta
|
|
854
1027
|
that no delta-plan element realises is a PLANNING gap, and a client-side check
|
|
@@ -872,7 +1045,9 @@ workflow:
|
|
|
872
1045
|
evaluation.
|
|
873
1046
|
- Author a paired test for every ADDED/MODIFIED screen, interaction and state,
|
|
874
1047
|
including the unwanted-behaviour states and an accessibility assertion per
|
|
875
|
-
interactive surface, citing the
|
|
1048
|
+
interactive surface, citing the requirement id THE DELTA PLAN NAMES for that row
|
|
1049
|
+
(UX-/US-/FR-/CN-/DC-/IC-/SC-). The spec is not in context: take the id from the
|
|
1050
|
+
plan and cite it verbatim. A row that names no id is a planning gap — surface it.
|
|
876
1051
|
- REGRESSION: for each element in the delta plan's regression targets, verify (add or
|
|
877
1052
|
adjust a test where needed) that the existing behaviour still holds after a shared
|
|
878
1053
|
component or state container changed — but do NOT rewrite that behaviour.
|
|
@@ -959,15 +1134,15 @@ workflow:
|
|
|
959
1134
|
schema: { overview: "<2-4 sentences of prose>" }
|
|
960
1135
|
mock: { overview: "" }
|
|
961
1136
|
task: |
|
|
962
|
-
Write the OKF knowledge-concept Overview for the
|
|
963
|
-
sentences of PROSE:
|
|
1137
|
+
Write the OKF knowledge-concept Overview for the feature `{{feature}}` — that is,
|
|
1138
|
+
for the aggregate it answers for, {{owned_csv}}. 2-4 sentences of PROSE:
|
|
964
1139
|
- name the aggregate ROOT and what the aggregate IS;
|
|
965
1140
|
- state the INVARIANTS it guards (its BR-nnn — what they require, not their ids);
|
|
966
1141
|
- state its TRANSACTION BOUNDARY — what it OWNS inside the boundary vs what it only
|
|
967
1142
|
REFERENCES by id.
|
|
968
1143
|
The bounded context's domain-model.md is the source where it is in context: read
|
|
969
|
-
ONLY
|
|
970
|
-
|
|
1144
|
+
ONLY {{owned_sections}} — the other sections belong to other aggregates. Where more
|
|
1145
|
+
than one is named, describe the FIRST. Where it is not in context, distil the same three things from
|
|
971
1146
|
backend-plan.md's domain section instead.
|
|
972
1147
|
Plain prose only: no headings, no bullets, no code fences, no file paths, and no
|
|
973
1148
|
restating of the dependency edges (those live in the Depends-On / Cross-References
|
|
@@ -1010,5 +1185,11 @@ outputs:
|
|
|
1010
1185
|
delta_model: "{{delta_model}}" # its second half on the delta path — empty on the bootstrap path
|
|
1011
1186
|
okf_concept: "{{okf_concept}}"
|
|
1012
1187
|
xref_edge_count: "{{xref_edge_count}}"
|
|
1013
|
-
|
|
1188
|
+
# Bootstrap path only. tracked=false means this feature has never passed analyze-plan
|
|
1189
|
+
# since the gate began leaving a receipt — an unknown, not a stale plan, and it blocks
|
|
1190
|
+
# nothing.
|
|
1191
|
+
plan_gate_tracked: "{{gate_tracked}}"
|
|
1192
|
+
plan_gate_fresh: "{{gate_fresh}}"
|
|
1193
|
+
plan_gate_at: "{{gate_at}}"
|
|
1194
|
+
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. On the bootstrap path the plans were checked against analyze-plan's receipt before anything generated (tracked={{gate_tracked}}, fresh={{gate_fresh}}, gated {{gate_at}}) — tracked=false means this feature has never passed analyze-plan since the gate began recording what it proved, which is an unknown rather than a stale plan and blocks nothing; run /appweaver analyze-plan once to start the record."
|
|
1014
1195
|
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."
|