aw-spec-cli 0.1.22 → 0.1.24
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/ORCH-DIALECT.md +3 -3
- package/assets/.appweaver/RULE-PARTITION.md +41 -28
- package/assets/.appweaver/orchestrator/orchestrator.py +139 -17
- package/assets/.appweaver/orchestrator/rev-templates/ai-rules.reference.md +3 -3
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +19 -12
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +25 -17
- package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +10 -10
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +28 -17
- package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +22 -24
- package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +29 -19
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +27 -20
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +184 -37
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +18 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +19 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +18 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-dependency-tree.orch.yaml +4 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +5 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +58 -43
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +650 -205
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +19 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +3 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +5 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +4 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +34 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +40 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-workflow-model.orch.yaml +23 -4
- package/assets/.appweaver/scripts/bash/aw-rev-constitution.sh +1 -1
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +82 -10
- package/assets/.appweaver/scripts/powershell/aw-rev-constitution.ps1 +1 -1
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +67 -2
- package/assets/.appweaver/scripts/python/aw-inventory.py +1809 -156
- package/assets/.appweaver/scripts/python/aw-rev-constitution.py +106 -145
- package/assets/.appweaver/scripts/python/aw-stack.py +45 -1
- package/assets/.appweaver/scripts/python/aw-xref.py +73 -2
- package/assets/.appweaver/scripts/python/validate_okf.py +10 -5
- package/assets/.appweaver/templates/okf-concept-template.md +10 -2
- package/assets/.appweaver/templates/proposed-inventory-template.md +100 -48
- package/assets/.appweaver/templates/source-map-template.md +14 -6
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +3 -3
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +5 -123
- package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +784 -0
- package/assets/foundations/java/Hexagonal/pack-foundation.sh +357 -0
- package/assets/foundations/java/Hexagonal/unpack-foundation.sh +399 -0
- package/package.json +1 -1
|
@@ -54,7 +54,9 @@
|
|
|
54
54
|
# only, NO domain files — domain reaches design as spec ids.
|
|
55
55
|
#
|
|
56
56
|
# Adopt does NOT generate code and does NOT modify source — writes land under
|
|
57
|
-
# specs/<Feature>/, .
|
|
57
|
+
# specs/<Context>/<Feature>/, in the context's specs/<Context>/domain-model.md, and in the
|
|
58
|
+
# aggregate's OKF concept; the constitution halves and guardrails/projects/<project>/ are
|
|
59
|
+
# written only when the reconcile block below is enabled.
|
|
58
60
|
# Fidelity checks spec/design ↔ CODE; the Adopt review is REPORTED for out-of-band
|
|
59
61
|
# closure — it blocks only on structural failures (unmapped code / fabricated spec/design).
|
|
60
62
|
# There is NO migration content here (characterize is an optional on-touch fence, not a
|
|
@@ -69,7 +71,13 @@
|
|
|
69
71
|
# =============================================================================
|
|
70
72
|
|
|
71
73
|
name: aw-spec-adopt
|
|
72
|
-
description: The JIT per-feature step of brownfield development — reverse-engineer ONE existing feature into a living spec.md, an aggregate section in the bounded context's shared domain-model.md, and backend-plan.md (+ frontend-plan.md where a frontend lane is declared and this slice owns part of its surface) + source-map.md (origin reverse-engineered, per-item confidence, code-derived plan template), then COMPLETE its shallow OKF concept (deepen it to the reversed reality + link the deep artifacts) and record its cross-slice edges. The spec cites BR-nnn by id and the model defines them, exactly as on the greenfield path, so a reversed slice is checkable by the same gates as any other. Fidelity gate is spec + model + plans vs code, and an invented entity, boundary or invariant is a fabrication exactly as an invented FR- is; the review is reported for out-of-band closure. The governance reconcile — carrying this feature's real evidence into the provisional rule files + constitution onboard wrote — is HELD (reconcile_governance=false), so an ordinary run does not touch the governance tier. A NEW feature is authored via the forward pipeline instead. Output is a normal feature for the CR + forward loop.
|
|
74
|
+
description: The JIT per-feature step of brownfield development — reverse-engineer ONE existing feature into a living spec.md, an aggregate section in the bounded context's shared domain-model.md, and backend-plan.md (+ frontend-plan.md where a frontend lane is declared and this slice owns part of its surface) + source-map.md (origin reverse-engineered, per-item confidence, code-derived plan template), then COMPLETE its shallow OKF concept (deepen it to the reversed reality + link the deep artifacts) and record its cross-slice edges. The spec cites BR-nnn by id and the model defines them, exactly as on the greenfield path, so a reversed slice is checkable by the same gates as any other. Fidelity gate is spec + model + plans vs code, and an invented entity, boundary or invariant is a fabrication exactly as an invented FR- is; the review is reported for out-of-band closure. Every reverse step is scoped to the feature's own operations from the confirmed boundary map, so two features over one aggregate never claim the same operation, and the spec continues the context's BR-nnn numbering from its domain model. Where a frontend lane is declared and this slice owns none of its surface, the feature records lanes=backend so later phases neither author nor demand a frontend plan for it. The governance reconcile — carrying this feature's real evidence into the provisional rule files + constitution onboard wrote — is HELD (reconcile_governance=false), so an ordinary run does not touch the governance tier. A NEW feature is authored via the forward pipeline instead. Output is a normal feature for the CR + forward loop.
|
|
75
|
+
# 2.1.0 — every reverse step is scoped to the FEATURE, not its aggregate: the boundary map's
|
|
76
|
+
# operation list (and a sibling feature's operations, which are not this one's)
|
|
77
|
+
# reaches the spec, model, plan, UI plan, source-map and fidelity steps. The spec step
|
|
78
|
+
# reads the context's domain model so BR-nnn ids continue the context's numbering.
|
|
79
|
+
# The UI plan is authored under the frontend rule set. A slice with no part of a
|
|
80
|
+
# declared frontend's surface records lanes=backend for the later phases.
|
|
73
81
|
# 1.5.0 — user_input is now OPTIONAL: adopt CONSUMES the ACTIVE feature (select-feature's
|
|
74
82
|
# .appweaver/feature.json) when no arg is passed, so onboard→select-feature→adopt needs
|
|
75
83
|
# no re-typing; an explicit <Feature> still selects directly (and wins). One coalesced
|
|
@@ -79,7 +87,7 @@ description: The JIT per-feature step of brownfield development — reverse-engi
|
|
|
79
87
|
# 1.3.0 — reverse design scaffolds from the DEFAULT `design-template` (onboard's code-derived
|
|
80
88
|
# override) instead of the never-shipped `brownfield-design-template`, unifying the
|
|
81
89
|
# whole brownfield pipeline on one code-derived template.
|
|
82
|
-
version: 2.
|
|
90
|
+
version: 2.1.0
|
|
83
91
|
agent_agnostic: true
|
|
84
92
|
dialect: aw-spec.orchestrator/v1
|
|
85
93
|
|
|
@@ -116,12 +124,24 @@ vars:
|
|
|
116
124
|
bundle_dir: "knowledge" # where /appweaver onboard writes the OKF feature map (repo-root default)
|
|
117
125
|
feature_name: "" # the aggregate to adopt: USER_INPUT if passed, else the active feature (set-feature canonicalises)
|
|
118
126
|
concept_exists: "false"
|
|
119
|
-
fe_declared: "false" # lane_readback runs on_error:continue, so
|
|
127
|
+
fe_declared: "false" # lane_readback runs on_error:continue, so its bindings need declared defaults
|
|
128
|
+
be_declared: "false"
|
|
129
|
+
fe_no_surface: "false" # a frontend lane is declared, this slice owns none of its surface, and a backend lane carries it
|
|
130
|
+
feature_lanes: "" # what record_lanes wrote — empty when the feature follows every declared lane
|
|
120
131
|
fe_root: ""
|
|
121
132
|
ui_lane: "false" # THIS slice reverses a UI half: a frontend lane is declared AND the slice owns UI files
|
|
122
133
|
ui_count: "0" # how many UI files the concept attributes to this aggregate (aw-inventory ui_source_files)
|
|
123
134
|
ui_source_files: ""
|
|
124
135
|
frontend_plan: ""
|
|
136
|
+
# THE FEATURE'S SCOPE INSIDE ITS AGGREGATE, from feature-aggregates (the boundary map).
|
|
137
|
+
operations_note: "" # this feature's operations + the ones only a sibling feature owns, as one line
|
|
138
|
+
operations_csv: ""
|
|
139
|
+
one_to_one: "true"
|
|
140
|
+
owned_sections: ""
|
|
141
|
+
shared_with_csv: ""
|
|
142
|
+
model_scope_plan: "" # which part of the model section the backend plan realises
|
|
143
|
+
baseline_commit: "" # the inventory HEAD the boundary map was drawn at
|
|
144
|
+
baseline_time: ""
|
|
125
145
|
ui_tpl_source: ""
|
|
126
146
|
# THE LANE PLANS, under the names the forward pipeline reads. A reversed slice is a
|
|
127
147
|
# NORMAL feature, so it carries backend-plan.md / frontend-plan.md like any other —
|
|
@@ -149,18 +169,6 @@ vars:
|
|
|
149
169
|
|
|
150
170
|
workflow:
|
|
151
171
|
|
|
152
|
-
# 0. TECHNICAL rule files → sticky standards (configuration, not hardcoded). tech-select
|
|
153
|
-
# reads guardrails/projects/<project>/tech-rule-bindings.md under "## /aw-spec-adopt" and
|
|
154
|
-
# loads that reference set into the sticky standards. Loaded FIRST so it is present for
|
|
155
|
-
# reverse_design (include_standards:true) — the reversed SPEC step still turns it off
|
|
156
|
-
# (include_standards:false), keeping design domain-free (rule partition). No domain rule
|
|
157
|
-
# file here — the tech map lists only technical files. No blocking gate: an unconfigured
|
|
158
|
-
# project soft-continues (constitution.technical.md still carries the tech base).
|
|
159
|
-
- id: tech_select
|
|
160
|
-
action: include
|
|
161
|
-
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
162
|
-
with: { command: aw-spec-adopt }
|
|
163
|
-
|
|
164
172
|
# 1. Resolve the aggregate to adopt. TWO ways in (arg wins over active):
|
|
165
173
|
# a) `/appweaver adopt <Feature>` — the explicit arg selects directly.
|
|
166
174
|
# b) `/appweaver adopt` (no arg) — CONSUME the feature select-feature already
|
|
@@ -253,6 +261,34 @@ workflow:
|
|
|
253
261
|
- { name: aggregate_sections, from: "$.sections" }
|
|
254
262
|
- { name: aggregate_source, from: "$.source" }
|
|
255
263
|
- { name: aggregate_primary, from: "$.primary" } # the concept this feature's OKF work lands on
|
|
264
|
+
# THE FEATURE'S SCOPE INSIDE THE AGGREGATE. The concept lists every file of the
|
|
265
|
+
# aggregate, and two features over one aggregate share them — only the boundary map's
|
|
266
|
+
# operation list tells their scopes apart. operations_note names this feature's
|
|
267
|
+
# operations and the ones only a sibling owns, as a line every reverse step quotes.
|
|
268
|
+
- { name: operations_note, from: "$.operations_note" }
|
|
269
|
+
- { name: operations_csv, from: "$.operations_csv" }
|
|
270
|
+
- { name: one_to_one, from: "$.one_to_one" }
|
|
271
|
+
- { name: owned_sections, from: "$.owned_sections" }
|
|
272
|
+
- { name: shared_with_csv, from: "$.shared_with_csv" }
|
|
273
|
+
# The inventory HEAD the map was drawn at — the source-map's baseline commit and the
|
|
274
|
+
# concept's timestamp. Empty time where git cannot resolve the commit.
|
|
275
|
+
- { name: baseline_commit, from: "$.baseline_commit" }
|
|
276
|
+
- { name: baseline_time, from: "$.baseline_time" }
|
|
277
|
+
|
|
278
|
+
# HOW MUCH OF THE MODEL SECTION THE BACKEND PLAN REALISES — the rule the forward `plan`
|
|
279
|
+
# applies. A feature that alone answers for its aggregate realises the whole section; one
|
|
280
|
+
# that shares it realises only what its own operations act on, or two plans would realise
|
|
281
|
+
# the same element. The engine has no conditional prompt text, so the rule is an ordered
|
|
282
|
+
# pair of sets handed to the prompt as one line.
|
|
283
|
+
- id: model_scope_shared
|
|
284
|
+
action: set
|
|
285
|
+
vars:
|
|
286
|
+
model_scope_plan: "{{owned_sections}}, which this feature SHARES with {{shared_with_csv}}. Realise only the entities, value objects, states and BR-nnn this feature's operations act on, under the model's names and boundary; an element of that section only another feature's operations use belongs to that feature's plan and is left out — never redefined, renamed or narrowed"
|
|
287
|
+
- id: model_scope_sole
|
|
288
|
+
condition: "{{one_to_one}} == true"
|
|
289
|
+
action: set
|
|
290
|
+
vars:
|
|
291
|
+
model_scope_plan: "{{owned_sections}}. This feature is the only one that answers for it, so the plan realises that whole section"
|
|
256
292
|
|
|
257
293
|
# 1a. One-time baseline: refuse to clobber an already-adopted / authored spec.
|
|
258
294
|
# After adoption, changes go through the CR delta loop, not adopt.
|
|
@@ -329,6 +365,20 @@ workflow:
|
|
|
329
365
|
on_false: stop
|
|
330
366
|
on_false_message: "No active governance for adopt to work against — run /appweaver onboard first. onboard establishes the PROJECT constitution + guardrails/projects/<project>/ rule set (provisional-from-scan) and activates it. Reversing '{{feature_name}}' without that scaffold would author the spec and design against no code-derived rules at all, and leave them with no governance home."
|
|
331
367
|
|
|
368
|
+
# TECHNICAL rule files → sticky standards (configuration, not hardcoded). tech-select
|
|
369
|
+
# reads guardrails/projects/<project>/tech-rule-bindings.md under "## /aw-spec-adopt" and
|
|
370
|
+
# loads that reference set into the sticky standards for reverse_plan
|
|
371
|
+
# (include_standards:true) — the reversed SPEC and MODEL steps turn it off
|
|
372
|
+
# (include_standards:false), keeping them free of architecture (rule partition). No domain
|
|
373
|
+
# rule file here — the tech map lists only technical files. No blocking gate: an
|
|
374
|
+
# unconfigured project soft-continues (constitution.technical.md still carries the tech
|
|
375
|
+
# base). Placed after every gate that can stop the run, so no selection box is shown for a
|
|
376
|
+
# run that stops anyway.
|
|
377
|
+
- id: tech_select
|
|
378
|
+
action: include
|
|
379
|
+
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
380
|
+
with: { command: aw-spec-adopt }
|
|
381
|
+
|
|
332
382
|
# 2. Scaffold the living docs. The SPEC reuses the forward spec-template (it is
|
|
333
383
|
# tech-agnostic WHAT/WHY — no architectural bias). The BACKEND PLAN scaffolds from the
|
|
334
384
|
# DEFAULT `design-template` — which /appweaver onboard REPLACED with the repo's
|
|
@@ -405,6 +455,7 @@ workflow:
|
|
|
405
455
|
outputs:
|
|
406
456
|
- { name: fe_declared, from: "$.frontend_declared" }
|
|
407
457
|
- { name: fe_root, from: "$.frontend_root" }
|
|
458
|
+
- { name: be_declared, from: "$.backend_declared" }
|
|
408
459
|
- id: ui_lane_off
|
|
409
460
|
action: set
|
|
410
461
|
vars: { ui_lane: "false" }
|
|
@@ -417,6 +468,40 @@ workflow:
|
|
|
417
468
|
condition: "{{ui_count}} == 0"
|
|
418
469
|
vars: { ui_lane: "false" }
|
|
419
470
|
|
|
471
|
+
# 2c. RECORD A SERVICE-ONLY SLICE, so the later phases know it is one. A declared
|
|
472
|
+
# frontend lane whose surface this slice does not touch is a DECISION adopt just
|
|
473
|
+
# made, and nothing downstream can re-derive it: `plan` would otherwise read the repo's
|
|
474
|
+
# declaration, find no frontend-plan.md, and author one from the spec for screens that
|
|
475
|
+
# do not exist, and `analyze-plan` would stop on the missing document. feature-lanes
|
|
476
|
+
# writes `lanes: [backend]` into this feature's active_version.json, and
|
|
477
|
+
# `lane-resolve --feature-dir` reports the frontend lane as not declared FOR THIS
|
|
478
|
+
# FEATURE. Recorded only where a backend lane carries the slice — with none declared
|
|
479
|
+
# there is no lane to narrow to. `feature-lanes --clear` undoes it when the feature
|
|
480
|
+
# gains a UI. Four ordered sets, because the engine has no compound conditions.
|
|
481
|
+
- id: fe_no_surface_off
|
|
482
|
+
action: set
|
|
483
|
+
vars: { fe_no_surface: "false" }
|
|
484
|
+
- id: fe_no_surface_declared
|
|
485
|
+
action: set
|
|
486
|
+
condition: "{{fe_declared}}"
|
|
487
|
+
vars: { fe_no_surface: "true" }
|
|
488
|
+
- id: fe_no_surface_has_ui
|
|
489
|
+
action: set
|
|
490
|
+
condition: "{{ui_count}} != 0"
|
|
491
|
+
vars: { fe_no_surface: "false" }
|
|
492
|
+
- id: fe_no_surface_no_backend
|
|
493
|
+
action: set
|
|
494
|
+
condition: "{{be_declared}} != true"
|
|
495
|
+
vars: { fe_no_surface: "false" }
|
|
496
|
+
- id: record_lanes
|
|
497
|
+
condition: "{{fe_no_surface}} == true"
|
|
498
|
+
action: execute_script
|
|
499
|
+
script: { ref: actions }
|
|
500
|
+
subcommand: feature-lanes
|
|
501
|
+
args: ["--feature-dir", "{{feature_dir}}", "--set", "backend"]
|
|
502
|
+
outputs:
|
|
503
|
+
- { name: feature_lanes, from: "$.lanes" }
|
|
504
|
+
|
|
420
505
|
# Scaffold the LANE's plan from ITS template pair (--lane frontend resolves
|
|
421
506
|
# ui-design-template -> frontend-plan.md), the twin of scaffold_plan above. onboard
|
|
422
507
|
# GENERATED that override from this repo's own UI source, so the reversed frontend plan
|
|
@@ -445,7 +530,7 @@ workflow:
|
|
|
445
530
|
action: verify_context
|
|
446
531
|
require_true: ui_tpl_override
|
|
447
532
|
on_false: stop
|
|
448
|
-
on_false_message: "A frontend lane is declared ({{fe_root}}) and '{{feature_name}}' owns {{ui_count}} UI file(s), but no CODE-DERIVED UI design template is installed at .appweaver/templates/overrides/ui-design-template.md — only the kit's greenfield template, which would impose a section structure this app's UI did not choose. onboard GENERATES the override from the UI source
|
|
533
|
+
on_false_message: "A frontend lane is declared ({{fe_root}}) and '{{feature_name}}' owns {{ui_count}} UI file(s), but no CODE-DERIVED UI design template is installed at .appweaver/templates/overrides/ui-design-template.md — only the kit's greenfield template, which would impose a section structure this app's UI did not choose. onboard GENERATES the override from the UI source, together with the lane's constitution half and rule set, when it first sees the lane declared. Run /appweaver onboard {{context}} — naming an onboarded context re-runs its governance pass, which authors the missing lane tier — then adopt this feature again."
|
|
449
534
|
|
|
450
535
|
# 3. Pre-execution hooks.
|
|
451
536
|
- id: resolve_pre_hooks
|
|
@@ -474,6 +559,7 @@ workflow:
|
|
|
474
559
|
context:
|
|
475
560
|
- { path: "{{spec_file}}" } # the scaffolded template = section structure + the modelling vocabulary
|
|
476
561
|
- { path: "{{concept_path}}" } # the OKF concept: source_files, seam, open_questions
|
|
562
|
+
- { path: "{{domain_model}}" } # the CONTEXT's model — the BR-nnn ids already issued, which this spec cites and continues
|
|
477
563
|
- { path: .appweaver/memory/constitution.domain.md, optional: true } # the domain frame
|
|
478
564
|
- { path: .appweaver/memory/constitution.ui.md, optional: true } # the frontend lane's UX-* base — absent on a backend-only repo, which onboard leaves unwritten by design
|
|
479
565
|
- id: reverse_spec
|
|
@@ -498,26 +584,40 @@ workflow:
|
|
|
498
584
|
whatever it is named: its user stories are that capability's operations and no
|
|
499
585
|
others. Sharing a name with an aggregate does not make it that aggregate's only
|
|
500
586
|
feature — other features may act on the same aggregate. Keep US-1.
|
|
501
|
-
-
|
|
587
|
+
- THE FEATURE'S SCOPE. {{operations_note}} The source files below are the whole
|
|
588
|
+
aggregate's, and a sibling feature over it shares them — so an operation that
|
|
589
|
+
belongs only to another feature is in this code and is still NOT this spec's: no
|
|
590
|
+
user story, requirement or success criterion for it. A read both features call is
|
|
591
|
+
this feature's too.
|
|
592
|
+
- Each of THIS feature's commands / endpoints / use-cases is a USER STORY (US-n),
|
|
502
593
|
never a separate feature. Derive its Given/When/Then from the code's paths.
|
|
503
594
|
- System Requirements in EARS (THE SYSTEM SHALL / WHEN / IF…THEN / WHILE /
|
|
504
595
|
WHERE), each atomic + testable, derived from observed behaviour.
|
|
505
596
|
- Always-true invariants the code enforces get stable BR-nnn ids, CITED here and
|
|
506
|
-
DEFINED in the domain model — see the next bullet.
|
|
597
|
+
DEFINED in the domain model — see the next bullet. The context's domain-model.md
|
|
598
|
+
is in context: still the template on the context's first adopted feature, and on
|
|
599
|
+
a later one it already defines aggregates and BR-nnn ids that other specs cite. A
|
|
600
|
+
rule the model ALREADY defines is cited by ITS id. A rule it lacks takes the next
|
|
601
|
+
id after the highest BR-nnn the model carries (BR-001 on an unfilled model), and
|
|
602
|
+
the next step defines it there. Never cite an id the model uses for a DIFFERENT
|
|
603
|
+
rule — not even when a code comment names that id: the model's numbering wins,
|
|
604
|
+
and the code's citation is recorded as drift.
|
|
507
605
|
- THE DOMAIN MODEL IS NOT THIS DOCUMENT'S. Fill "### Domain Model Reference" with
|
|
508
606
|
the aggregate's NAME and the ids of the invariants this spec relies on
|
|
509
607
|
(BR-nnn), and nothing more. Do NOT write the ubiquitous language, the aggregate
|
|
510
608
|
boundary, the entity/value-object split, the lifecycle states, or the TEXT of any
|
|
511
|
-
invariant: all of that lives in the bounded context's domain-model.md
|
|
512
|
-
NEXT step
|
|
513
|
-
so a per-spec copy drifts. Cite by id; never restate.
|
|
609
|
+
invariant: all of that lives in the bounded context's domain-model.md (in
|
|
610
|
+
context), which the NEXT step fills or extends from this same code, and several
|
|
611
|
+
specs act on one aggregate so a per-spec copy drifts. Cite by id; never restate.
|
|
514
612
|
- DOMAIN LIFT — where the CODE enforces them, lift into the three sections
|
|
515
613
|
(cite the source file): Constraints & NFRs (CN-, e.g. retention /
|
|
516
614
|
immutability / append-only audit the code implements), Data Classification
|
|
517
615
|
(DC-, sensitive fields + where they may/mustn't appear), Integration
|
|
518
616
|
Contracts (IC-, external services/feeds the code calls). "None (as of v1.0)"
|
|
519
617
|
if the code shows none — never delete a section.
|
|
520
|
-
- EXPERIENCE LIFT — only where this slice HAS a UI.
|
|
618
|
+
- EXPERIENCE LIFT — only where this slice HAS a UI. Here that is {{ui_lane}} (true
|
|
619
|
+
= a frontend lane is declared and this aggregate owns UI files, listed below —
|
|
620
|
+
read them). Fill Experience Scope from
|
|
521
621
|
the surface the code exposes (UI actors, channels, and the device / browser /
|
|
522
622
|
assistive-technology floors the UI source actually targets), and lift the
|
|
523
623
|
project-wide standards that UI already holds into Experience Standards (UX-n),
|
|
@@ -545,6 +645,8 @@ workflow:
|
|
|
545
645
|
OBLIGATION is an IC- requirement, never the adapter design).
|
|
546
646
|
|
|
547
647
|
Source files to read (the slice's code): {{source_files}}
|
|
648
|
+
UI source files (the Experience lift's evidence; empty where the aggregate has no UI —
|
|
649
|
+
a sibling feature's UI folder is not this feature's surface): {{ui_source_files}}
|
|
548
650
|
|
|
549
651
|
# ══════════ REVERSE DOMAIN MODEL — the CONTEXT's structure, from the CODE ══════
|
|
550
652
|
# The greenfield split, applied to the reverse: the spec says what the system DOES, the
|
|
@@ -596,6 +698,16 @@ workflow:
|
|
|
596
698
|
(owns / references by id / out of scope), entities, value objects, domain events,
|
|
597
699
|
lifecycle states, and every always-true invariant the code enforces as a stable
|
|
598
700
|
BR-nnn with its RULE TEXT. The spec cites those ids and never restates them.
|
|
701
|
+
- THE SECTION MODELS THE WHOLE AGGREGATE, as its code has it — every entity, value
|
|
702
|
+
object, lifecycle state, event and invariant the root carries, including the parts
|
|
703
|
+
only another feature's operations exercise. {{operations_note}} Structure belongs
|
|
704
|
+
to the aggregate, not to one capability: a later feature over it cites the ids
|
|
705
|
+
already here instead of defining its part a second time. `Serving specs` names the
|
|
706
|
+
features that have a spec so far, this one included — never one that has none yet.
|
|
707
|
+
- NUMBERING. Define every BR-nnn the reversed spec cites under exactly the id it
|
|
708
|
+
cites. An invariant the spec does not cite takes the next free id after the highest
|
|
709
|
+
in the model — or the id the code's own comments give it, where that id is free.
|
|
710
|
+
An id the model already uses is never given to a different rule.
|
|
599
711
|
- IF THE MODEL ALREADY HAS CONTENT, this is the context's SECOND (or later) adopted
|
|
600
712
|
slice. ADD any of this feature's aggregates not already there as a new section and
|
|
601
713
|
EXTEND the ubiquitous language. Do NOT rewrite, renumber or "tidy" the aggregates
|
|
@@ -672,10 +784,15 @@ workflow:
|
|
|
672
784
|
Domain/Application/API/Infrastructure layering. A component the code has that
|
|
673
785
|
fits no textbook layer (a UI tier, a worker, a shared library) gets its own
|
|
674
786
|
section; a layer the code lacks does NOT appear.
|
|
787
|
+
- THE FEATURE'S SCOPE. {{operations_note}} The source files below are the whole
|
|
788
|
+
aggregate's, shared with any sibling feature over it: the plan's operation
|
|
789
|
+
components realise THIS feature's operations only, and a component that serves only
|
|
790
|
+
another feature's operation is left out even though its file is listed.
|
|
675
791
|
- KEEP THE NEUTRAL ANCHORS (the template pins these): §1.2 is the domain/data
|
|
676
|
-
MODEL and
|
|
677
|
-
|
|
678
|
-
|
|
792
|
+
MODEL and follows the CONTEXT'S DOMAIN MODEL exactly within this feature's part of
|
|
793
|
+
it — {{model_scope_plan}}. Within that part: the same entity and value-object NAMES,
|
|
794
|
+
the same boundary, the same lifecycle states — with cross-slice References-by-id
|
|
795
|
+
kept here. Every BR-nnn in that part of the model gets a
|
|
679
796
|
named ENFORCEMENT POINT in this plan: cite it by id and NEVER restate what it
|
|
680
797
|
says, because the model is the only place that text lives. One §2 subsection is
|
|
681
798
|
the EXTERNAL INTEGRATIONS table (heading contains "Integration") realising the
|
|
@@ -701,6 +818,17 @@ workflow:
|
|
|
701
818
|
# NOT its parent: backend-plan.md realises the spec's FR/BR/CN-, frontend-plan.md
|
|
702
819
|
# realises the spec's UX- and the user stories' interaction, and it CONSUMES the backend
|
|
703
820
|
# plan's contracts rather than restating them. Both are in context for exactly that reason.
|
|
821
|
+
# THE FRONTEND'S RULES REPLACE THE BACKEND'S before the lane's plan is authored — the
|
|
822
|
+
# swap the forward `plan` makes at the same boundary. Sticky standards only accumulate,
|
|
823
|
+
# so without it the UI plan was authored under the service rule set and none of the
|
|
824
|
+
# lane's own. `## /aw-spec-plan-ui` is the set the forward plan authors this same
|
|
825
|
+
# document under, so a reversed frontend plan and a planned one answer to one rule set.
|
|
826
|
+
- id: tech_select_ui
|
|
827
|
+
condition: "{{ui_lane}} == true"
|
|
828
|
+
action: include
|
|
829
|
+
module: .appweaver/orchestrator/modules/tech-select.orch.yaml
|
|
830
|
+
with: { command: aw-spec-plan-ui, replace: "true" }
|
|
831
|
+
|
|
704
832
|
- id: ctx_reverse_ui_plan
|
|
705
833
|
condition: "{{ui_lane}} == true"
|
|
706
834
|
action: build_context
|
|
@@ -728,6 +856,10 @@ workflow:
|
|
|
728
856
|
a target to bend it toward. An inconsistent app is recorded as inconsistent; a
|
|
729
857
|
surface with no loading states is recorded as having none, NEVER as needing them.
|
|
730
858
|
Rules:
|
|
859
|
+
- THE FEATURE'S SCOPE. {{operations_note}} The UI files below are the aggregate's
|
|
860
|
+
whole surface: a screen, component or action that serves only another feature's
|
|
861
|
+
operation — typically that feature's own UI folder — is that feature's. Where this
|
|
862
|
+
feature's screen hosts it, cite it as the sibling's; never plan it here.
|
|
731
863
|
- STAY IN THE LANE. Realise the spec's Experience Standards (UX-) and the
|
|
732
864
|
interaction its User Stories imply. The backend plan (in context) supplies
|
|
733
865
|
the CONTRACTS this surface consumes — cite them; do NOT restate, re-derive or
|
|
@@ -776,8 +908,9 @@ workflow:
|
|
|
776
908
|
(certain | inferred). EVERY source file MUST appear — the Fidelity gate
|
|
777
909
|
checks full coverage both ways.
|
|
778
910
|
- THE CONCEPT IS THE AGGREGATE'S ('{{aggregate_primary}}') and lists every feature's
|
|
779
|
-
code on it. Where its `features:` names features besides
|
|
780
|
-
file that serves ONLY one of those sibling capabilities
|
|
911
|
+
code on it. {{operations_note}} Where its `features:` names features besides
|
|
912
|
+
'{{feature_name}}', a file that serves ONLY one of those sibling capabilities (only
|
|
913
|
+
their operations) is not this feature's:
|
|
781
914
|
record it under Unmapped / open as `sibling: <Feature>`, never as a mapped row.
|
|
782
915
|
The mapped rows ARE this feature's file list — later commands hand exactly those
|
|
783
916
|
files to their fix loops.
|
|
@@ -791,7 +924,8 @@ workflow:
|
|
|
791
924
|
analysis (not a migration item).
|
|
792
925
|
- Record as-built quirks captured verbatim (candidate deviations), and any
|
|
793
926
|
unmapped code / unanchored spec ids.
|
|
794
|
-
- Baseline commit
|
|
927
|
+
- Baseline commit: `{{baseline_commit}}` — the inventory HEAD the boundary map was
|
|
928
|
+
drawn at. Write `unknown` when that is empty; never guess one.
|
|
795
929
|
Source files to map: {{source_files}}
|
|
796
930
|
|
|
797
931
|
# ═══════ FIDELITY — do spec + model + plans describe the CODE? ══════════════
|
|
@@ -817,7 +951,7 @@ workflow:
|
|
|
817
951
|
- "{{concept_path}}"
|
|
818
952
|
include_standards: false
|
|
819
953
|
tools: Read # the check is documents vs CODE; with no code it degrades to doc vs doc and certifies fabrication as ok
|
|
820
|
-
schema: { ok: true, report_needed: false, blocking_gaps: [ { severity: "high", artefact: "<source file / spec id / model element / plan element>", kind: "unmapped-code | fabricated-spec | fabricated-model | fabricated-plan", detail: "<why>" } ], review_items: [ { kind: "inferred | as-built-quirk | missing-invariant | confidence", where: "<spec/model/plan/source-map location>", note: "<what to confirm out of band>" } ] }
|
|
954
|
+
schema: { ok: true, report_needed: false, blocking_gaps: [ { severity: "high", artefact: "<source file / spec id / model element / plan element>", kind: "unmapped-code | fabricated-spec | fabricated-model | fabricated-plan | foreign-operation", detail: "<why>" } ], review_items: [ { kind: "inferred | as-built-quirk | missing-invariant | confidence", where: "<spec/model/plan/source-map location>", note: "<what to confirm out of band>" } ] }
|
|
821
955
|
mock: { ok: true, report_needed: false, blocking_gaps: [], review_items: [] }
|
|
822
956
|
task: |
|
|
823
957
|
FIDELITY CHECK — do the reversed documents actually describe the CODE
|
|
@@ -836,6 +970,14 @@ workflow:
|
|
|
836
970
|
value object the code does not have, an aggregate boundary it does not draw,
|
|
837
971
|
a lifecycle state it never enters, or a BR-nnn whose rule nothing enforces.
|
|
838
972
|
An anemic slice reversing as an anemic model is CORRECT, not a gap.
|
|
973
|
+
(3) foreign-operation: a user story, requirement, plan element or UI-plan element
|
|
974
|
+
that realises an operation belonging ONLY to another feature over the same
|
|
975
|
+
aggregate. {{operations_note}} Its code is real, so it is not a fabrication —
|
|
976
|
+
but two specs owning one operation is the overlap the boundary map exists to
|
|
977
|
+
prevent, and every later change to it would land in the wrong feature.
|
|
978
|
+
- THE MODEL SECTION COVERS THE WHOLE AGGREGATE. An entity, state, event or invariant
|
|
979
|
+
that only a sibling feature's operations exercise is NOT fabricated when the code
|
|
980
|
+
has it: the section models the aggregate, not this feature's slice of it.
|
|
839
981
|
- ONLY THIS FEATURE'S SECTIONS ARE THIS RUN'S. domain-model.md is the CONTEXT's and
|
|
840
982
|
may already carry sections from earlier adopted slices — judge ONLY
|
|
841
983
|
{{aggregate_sections}}. Do not report a sibling aggregate as unmapped because this
|
|
@@ -892,7 +1034,7 @@ workflow:
|
|
|
892
1034
|
action: verify_context
|
|
893
1035
|
require_true: fidelity_ok
|
|
894
1036
|
on_false: stop
|
|
895
|
-
on_false_message: "Fidelity FAILED for {{feature_name}} — the reversed documents do not match the code (blocking gaps: {{blocking_gaps}}). See {{feature_dir}}/adopt-review-report.md. Fix the unmapped code / remove fabricated items (re-read the source, refine spec.md, {{domain_model}}, backend-plan.md and source-map.md), then re-run /appweaver adopt (after removing the dir) or resolve out of band. A `fabricated-model` gap means the reverse gave this slice structure its code does not have — an entity, a boundary, a lifecycle state or a BR-nnn nothing enforces; the fix is to record the domain as thin as the code really is, never to leave it flattering."
|
|
1037
|
+
on_false_message: "Fidelity FAILED for {{feature_name}} — the reversed documents do not match the code (blocking gaps: {{blocking_gaps}}). See {{feature_dir}}/adopt-review-report.md. Fix the unmapped code / remove fabricated items (re-read the source, refine spec.md, {{domain_model}}, backend-plan.md and source-map.md), then re-run /appweaver adopt (after removing the dir) or resolve out of band. A `fabricated-model` gap means the reverse gave this slice structure its code does not have — an entity, a boundary, a lifecycle state or a BR-nnn nothing enforces; the fix is to record the domain as thin as the code really is, never to leave it flattering. A `foreign-operation` gap means a document claims an operation the boundary map gives only to another feature; remove it here — it belongs to that feature's adopt."
|
|
896
1038
|
|
|
897
1039
|
# ═══════════ OKF CONCEPT — COMPLETE the shallow scan concept (deep reverse) ═══
|
|
898
1040
|
# onboard wrote this aggregate's OKF concept SHALLOW (a discovery stub — enough to pick the
|
|
@@ -937,12 +1079,14 @@ workflow:
|
|
|
937
1079
|
points at the deep truth. Keep every link another feature's adopt added.
|
|
938
1080
|
- UNION `source_files` with the source-map's mapped files (grouped by the code's OWN
|
|
939
1081
|
folders, per OKF) — never drop a file already listed, which may be a sibling
|
|
940
|
-
capability's
|
|
1082
|
+
capability's. The boundary map was drawn at the baseline commit, whose time is
|
|
1083
|
+
[{{baseline_time}}]: set `timestamp` to that value, and where the brackets are empty
|
|
1084
|
+
(git could not resolve the commit) leave `timestamp` exactly as it is.
|
|
941
1085
|
- Update frontmatter: keep `origin: reverse-engineered`; make sure `features:`
|
|
942
|
-
includes '{{feature_name}}'; stamp `generated_by: aw-spec-adopt`; and
|
|
943
|
-
'{{feature_name}}'
|
|
944
|
-
|
|
945
|
-
|
|
1086
|
+
includes '{{feature_name}}'; stamp `generated_by: aw-spec-adopt`; and add
|
|
1087
|
+
'{{feature_name}}' to `adopted_features:` (a block list, like `features:`), keeping
|
|
1088
|
+
every feature already listed there. A feature in `features:` and not in
|
|
1089
|
+
`adopted_features:` is still the shallow stub — never mark the whole concept done.
|
|
946
1090
|
- Resolve any `open_questions` the deep reverse answered; keep the rest.
|
|
947
1091
|
- Do NOT touch the concept's Depends-On / Cross-References sections (xref_record
|
|
948
1092
|
maintains those next), and do NOT edit any OTHER aggregate concept.
|
|
@@ -1188,6 +1332,9 @@ outputs:
|
|
|
1188
1332
|
bounded_context: "{{context}}"
|
|
1189
1333
|
ui_lane: "{{ui_lane}}"
|
|
1190
1334
|
ui_count: "{{ui_count}}"
|
|
1335
|
+
feature_lanes: "{{feature_lanes}}" # empty = every declared lane; [backend] = a service-only slice in a repo with a frontend
|
|
1336
|
+
operations: "{{operations_csv}}" # this feature's operations, from the boundary map
|
|
1337
|
+
baseline_commit: "{{baseline_commit}}"
|
|
1191
1338
|
source_map: "{{feature_dir}}/source-map.md"
|
|
1192
1339
|
feature: "{{feature_name}}"
|
|
1193
1340
|
# What the feature acts on. Equal to the feature on a 1:1 slice; not on a feature that
|
|
@@ -1204,4 +1351,4 @@ outputs:
|
|
|
1204
1351
|
composed_file: "{{composed_file}}"
|
|
1205
1352
|
technical_reconcile_ok: "{{technical_ok}}"
|
|
1206
1353
|
domain_reconcile_ok: "{{domain_ok}}"
|
|
1207
|
-
next: "Adopted {{feature_name}} (spec.md + backend-plan.md + source-map.md, v1.0) and wrote {{aggregate_sections}} — the model section(s) for the aggregate(s) it acts on, resolved from the {{aggregate_source}} — in the bounded context `{{context}}`'s shared {{domain_model}}. The spec cites BR-nnn by id, the model carries their text, so this slice is checkable by the same gates as a greenfield one and analyze-plan's check 4 now has a model to check against. A section another feature over the same aggregate wrote first was EXTENDED, never duplicated; every other section is untouched. COMPLETED the OKF concept of {{aggregate_primary}} (deepened + linked the deep artifacts; {{xref_edge_count}} cross-slice edge(s) recorded). UI HALF: ui_lane={{ui_lane}} — where it is true this slice also has frontend-plan.md, reversed from the {{ui_count}} UI file(s) the feature map attributes to it; where it is false the repo declared no frontend lane, or this aggregate owns no part of its surface, and a service-only adoption is the correct outcome rather than a gap. The SHARED UI surface (app shell, layout, design system) belongs to no slice and stays in {{bundle_dir}}/architecture/frontend.md. NOTE — governance reconcile is currently HELD (reconcile_governance=false): this run did NOT modify the constitution halves or the guardrails/projects/{{gov_domain}}/ rule set, so they keep onboard's provisional scaffold. Close any items in {{feature_dir}}/adopt-review-report.md out of band. The slice is now a NORMAL feature: refine via /appweaver analyze-plan, then drive every change through the CR delta loop (/appweaver specify → plan → analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta). If a change edits untested legacy behaviour with blast radius, characterize just that surface first (optional on-touch fence). To restore governance reconcile, set reconcile_governance to \"true\" in the workflow vars."
|
|
1354
|
+
next: "Adopted {{feature_name}} (spec.md + backend-plan.md + source-map.md, v1.0) and wrote {{aggregate_sections}} — the model section(s) for the aggregate(s) it acts on, resolved from the {{aggregate_source}} — in the bounded context `{{context}}`'s shared {{domain_model}}. The spec cites BR-nnn by id, the model carries their text, so this slice is checkable by the same gates as a greenfield one and analyze-plan's check 4 now has a model to check against. A section another feature over the same aggregate wrote first was EXTENDED, never duplicated; every other section is untouched. COMPLETED the OKF concept of {{aggregate_primary}} (deepened + linked the deep artifacts; {{xref_edge_count}} cross-slice edge(s) recorded). UI HALF: ui_lane={{ui_lane}} — where it is true this slice also has frontend-plan.md, reversed from the {{ui_count}} UI file(s) the feature map attributes to it; where it is false the repo declared no frontend lane, or this aggregate owns no part of its surface, and a service-only adoption is the correct outcome rather than a gap. In the second case, where a frontend lane IS declared, the feature records lanes=[backend] in its active_version.json (feature_lanes shows it), so plan and the analyze gates treat the frontend lane as absent for THIS feature; run `aw-spec-actions feature-lanes --clear` if it later gains a UI. The SHARED UI surface (app shell, layout, design system) belongs to no slice and stays in {{bundle_dir}}/architecture/frontend.md. NOTE — governance reconcile is currently HELD (reconcile_governance=false): this run did NOT modify the constitution halves or the guardrails/projects/{{gov_domain}}/ rule set, so they keep onboard's provisional scaffold. Close any items in {{feature_dir}}/adopt-review-report.md out of band. The slice is now a NORMAL feature: refine via /appweaver analyze-plan, then drive every change through the CR delta loop (/appweaver specify → plan → analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta). If a change edits untested legacy behaviour with blast radius, characterize just that surface first (optional on-touch fence). To restore governance reconcile, set reconcile_governance to \"true\" in the workflow vars."
|
|
@@ -77,6 +77,7 @@ vars:
|
|
|
77
77
|
lanes_declared: "false"
|
|
78
78
|
be_declared: "false"
|
|
79
79
|
fe_declared: "false"
|
|
80
|
+
fe_excluded: "false" # this feature records no surface in the declared frontend lane (feature-lanes)
|
|
80
81
|
has_changes: "false"
|
|
81
82
|
cr_choice: ""
|
|
82
83
|
cr_id: ""
|
|
@@ -149,6 +150,23 @@ workflow:
|
|
|
149
150
|
- { name: backend_plan_exists, from: "$.backend_plan_exists" }
|
|
150
151
|
- { name: frontend_plan_exists, from: "$.frontend_plan_exists" }
|
|
151
152
|
|
|
153
|
+
# THIS FEATURE'S LANES. lane_resolve above read the REPO's declaration; one feature may
|
|
154
|
+
# own no part of a lane's surface — adopt records `lanes: [backend]` in
|
|
155
|
+
# active_version.json for a service-only slice in a repo that also has a UI. Re-read with
|
|
156
|
+
# --feature-dir so a lane the feature records no surface in is absent here: no delta plan
|
|
157
|
+
# is expected of it. With no feature resolved the argument is dropped and the repo's
|
|
158
|
+
# answer stands.
|
|
159
|
+
- id: feature_lanes
|
|
160
|
+
action: execute_script
|
|
161
|
+
on_error: continue
|
|
162
|
+
script: { ref: actions }
|
|
163
|
+
subcommand: lane-resolve
|
|
164
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
165
|
+
outputs:
|
|
166
|
+
- { name: be_declared, from: "$.backend_declared" }
|
|
167
|
+
- { name: fe_declared, from: "$.frontend_declared" }
|
|
168
|
+
- { name: fe_excluded, from: "$.frontend_feature_excluded" }
|
|
169
|
+
|
|
152
170
|
- id: require_feature
|
|
153
171
|
action: verify_context
|
|
154
172
|
require_non_empty: feature_dir
|
|
@@ -119,6 +119,7 @@ vars:
|
|
|
119
119
|
lanes_declared: "false"
|
|
120
120
|
be_declared: "false"
|
|
121
121
|
fe_declared: "false"
|
|
122
|
+
fe_excluded: "false" # this feature records no surface in the declared frontend lane (feature-lanes)
|
|
122
123
|
# PER-LANE PRESENCE. A lane is CHECKED when it is declared and its plan document is on
|
|
123
124
|
# disk, and MISSING when it is declared and the document is not. The engine has no
|
|
124
125
|
# compound conditions, so each AND is one ordered assignment taking the lane's own
|
|
@@ -190,6 +191,23 @@ workflow:
|
|
|
190
191
|
- { name: backend_plan_exists, from: "$.backend_plan_exists" }
|
|
191
192
|
- { name: frontend_plan_exists, from: "$.frontend_plan_exists" }
|
|
192
193
|
|
|
194
|
+
# THIS FEATURE'S LANES. lane_resolve above read the REPO's declaration; one feature may
|
|
195
|
+
# own no part of a lane's surface — adopt records `lanes: [backend]` in
|
|
196
|
+
# active_version.json for a service-only slice in a repo that also has a UI. Re-read with
|
|
197
|
+
# --feature-dir so a lane the feature records no surface in is absent here: its missing
|
|
198
|
+
# plan is not a gap this gate stops on. With no feature resolved the argument is dropped
|
|
199
|
+
# and the repo's answer stands.
|
|
200
|
+
- id: feature_lanes
|
|
201
|
+
action: execute_script
|
|
202
|
+
on_error: continue
|
|
203
|
+
script: { ref: actions }
|
|
204
|
+
subcommand: lane-resolve
|
|
205
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
206
|
+
outputs:
|
|
207
|
+
- { name: be_declared, from: "$.backend_declared" }
|
|
208
|
+
- { name: fe_declared, from: "$.frontend_declared" }
|
|
209
|
+
- { name: fe_excluded, from: "$.frontend_feature_excluded" }
|
|
210
|
+
|
|
193
211
|
# The BOUNDED CONTEXT's domain model — where the aggregate root, its entities and its
|
|
194
212
|
# invariants (BR-n) live. The spec cites them by id and the plans record where each is
|
|
195
213
|
# ENFORCED, so check 4 has nothing to check without it in context.
|
|
@@ -773,6 +791,7 @@ outputs:
|
|
|
773
791
|
bounded_context: "{{context}}"
|
|
774
792
|
backend_plan: "{{backend_plan}}" # empty when no backend lane is declared
|
|
775
793
|
frontend_plan: "{{frontend_plan}}" # empty when no frontend lane is declared
|
|
794
|
+
frontend_excluded: "{{fe_excluded}}" # true = a frontend lane is declared and this feature records no surface in it (feature-lanes)
|
|
776
795
|
domain_model: "{{domain_model}}"
|
|
777
796
|
conformant: "{{analyze_ok}}"
|
|
778
797
|
gaps: "{{analyze_gaps}}"
|
|
@@ -115,6 +115,7 @@ vars:
|
|
|
115
115
|
# ordered assignment; the engine has no compound conditions.
|
|
116
116
|
be_declared: "false"
|
|
117
117
|
fe_declared: "false"
|
|
118
|
+
fe_excluded: "false" # this feature records no surface in the declared frontend lane (feature-lanes)
|
|
118
119
|
be_gated: "false"
|
|
119
120
|
fe_gated: "false"
|
|
120
121
|
any_lane: "false"
|
|
@@ -193,6 +194,23 @@ workflow:
|
|
|
193
194
|
- { name: backend_plan, from: "$.backend_plan" }
|
|
194
195
|
- { name: frontend_plan, from: "$.frontend_plan" }
|
|
195
196
|
|
|
197
|
+
# THIS FEATURE'S LANES. lane_resolve above read the REPO's declaration; one feature may
|
|
198
|
+
# own no part of a lane's surface — adopt records `lanes: [backend]` in
|
|
199
|
+
# active_version.json for a service-only slice in a repo that also has a UI. Re-read with
|
|
200
|
+
# --feature-dir so a lane the feature records no surface in is absent here: its code is
|
|
201
|
+
# not gated against a plan it never had. With no feature resolved the argument is dropped
|
|
202
|
+
# and the repo's answer stands.
|
|
203
|
+
- id: feature_lanes
|
|
204
|
+
action: execute_script
|
|
205
|
+
on_error: continue
|
|
206
|
+
script: { ref: actions }
|
|
207
|
+
subcommand: lane-resolve
|
|
208
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
209
|
+
outputs:
|
|
210
|
+
- { name: be_declared, from: "$.backend_declared" }
|
|
211
|
+
- { name: fe_declared, from: "$.frontend_declared" }
|
|
212
|
+
- { name: fe_excluded, from: "$.frontend_feature_excluded" }
|
|
213
|
+
|
|
196
214
|
- id: require_feature
|
|
197
215
|
action: verify_context
|
|
198
216
|
require_non_empty: feature_dir
|
|
@@ -724,4 +724,4 @@ outputs:
|
|
|
724
724
|
ui_validation_ok: "{{ui_ok}}"
|
|
725
725
|
ui_validation_issues: "{{ui_issues}}"
|
|
726
726
|
note: "Split constitution authored, one half per LANE the project declared in .appweaver/stacks.json. service_lane / ui_lane report which lanes were found there, and the declaration is the ONLY signal read: a half is a lane's standing governance, so a lane declared `(none)` in select-stack gets no half AND no file. constitution.technical.md is the service lane's base (standing input to the SERVICE lane of plan + implement); constitution.ui.md is the UI lane's UX-* base (standing input to their FRONTEND lane); constitution.domain.md is the domain addenda (standing input to specify + a gate checklist), and it is always authored because domain is not a lane — it addends whichever halves exist. All are composed into constitution.md, which now tolerates an absent technical half: a UI-only project's domain half opens the roman-numeral sequence at I instead of continuing one, and numbering_checked reports false because that check has no subject rather than because it silently passed. Mapping ## /aw-spec-ui-constitution supplies the rules the UI half ratifies but no longer creates the lane on its own — a mapped rule set with no declared lane produces a warning naming both fixes. ui_lane false with ui_included true means a lane was retired after its half was authored: the file is left alone and the log says so, because a ratified governance file is never deleted automatically. If a validation_ok is false, the matching validation_issues lists what still needs a manual touch. See each half's Sync Impact Report for its version bump. Suggested commit: 'docs: amend split constitution'."
|
|
727
|
-
next: "
|
|
727
|
+
next: "/appweaver select-feature → specify. select-feature picks the feature to build and every stage from specify onward reads that active feature and stops without it; on a brownfield workspace pick the bounded context first with /appweaver select-bounded-context. From specify the greenfield sequence runs plan → analyze-plan → implement → build → analyze-slice → run → deploy. THE HALVES ARE LOADED PER CONCERN, not all at once — /appweaver specify loads constitution.domain.md; the SERVICE lane of plan + implement loads constitution.technical.md; their FRONTEND lane loads constitution.ui.md. Re-run /appweaver constitution to amend, including to pick up a lane declared or dropped after this run — re-run /appweaver select-stack first to change which lanes exist."
|
|
@@ -66,4 +66,7 @@ outputs:
|
|
|
66
66
|
tree_file: "{{tree_file}}"
|
|
67
67
|
sequence_file: "{{sequence_file}}"
|
|
68
68
|
note: "Migration Hierarchy Tree generated in {{tree_file}} and {{sequence_file}}."
|
|
69
|
-
|
|
69
|
+
# Migration is a MODE of the ordinary brownfield loop, not a pipeline of its own:
|
|
70
|
+
# the tree only fixes the ORDER the slices are taken in, and each slice then runs
|
|
71
|
+
# adopt → (characterize on touch) → the CR delta loop like any other.
|
|
72
|
+
next: "Human Gate H0.1: Review {{tree_file}} — it orders {{aggregate_count}} aggregate(s) into {{wave_count}} migration wave(s), and that order is the only thing it decides. Take the slices in it ONE AT A TIME through the ordinary brownfield loop: /appweaver adopt <Feature> reverse-engineers the slice into spec.md + backend-plan.md + source-map.md, then every change to it goes through the CR delta loop (/appweaver specify → plan → analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta). If the slice has untested legacy behaviour with blast radius, run /appweaver characterize first to fence it. /appweaver parity is the cutover gate — run it per slice before the seam is routed away from the legacy path."
|
|
@@ -366,4 +366,8 @@ outputs:
|
|
|
366
366
|
sentinel_clean: "{{clean}}"
|
|
367
367
|
sentinel_residue: "{{residue}}"
|
|
368
368
|
note: "Foundation scaffolded into the {{lane_id}} lane at {{lane_root}} ({{written}} file(s), sentinel renamed to {{app}}); {{tests_message}}. Everything written is inside the subtree that lane owns, which is where implement generates and build/test/run act. Configure your AppWeaver.* NuGet feed and run the AppHost to confirm the scaffold starts before building on it."
|
|
369
|
-
|
|
369
|
+
# constitution is named here because specify declares constitution.domain.md a
|
|
370
|
+
# REQUIRED context file. It announces rather than stops when the file is absent,
|
|
371
|
+
# so a sequence that skips this step produces a spec authored with no domain
|
|
372
|
+
# frame and only a [context] MISSING line to show for it.
|
|
373
|
+
next: "Another lane still to scaffold? Run /appweaver foundation again and pick it — one bundle fills one lane. Otherwise carry on with /appweaver select-domain → constitution → select-feature → specify. select-domain binds this workspace to a governed project under guardrails/projects/ and is one-time; skip it only where no such project exists. /appweaver constitution is NOT optional — it authors the constitution halves, and specify declares constitution.domain.md as REQUIRED context while plan and implement read constitution.technical.md (plus constitution.ui.md on a frontend lane); it also needs the lane declaration select-stack wrote, which is why it runs after this. /appweaver select-feature then picks the feature to build — every stage from specify onward reads that active feature and stops without it."
|