aw-spec-cli 0.1.19 → 0.1.21
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 +17 -9
- package/assets/.appweaver/ORCH-DIALECT.md +8 -3
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +26 -3
- package/assets/.appweaver/orchestrator/orchestrator.py +65 -3
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +4 -2
- 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 +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +121 -42
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +7 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +145 -52
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +18 -10
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +306 -28
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +114 -30
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +140 -36
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +385 -61
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +217 -174
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +407 -107
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +49 -11
- package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +1 -1
- 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-select-stack.orch.yaml +47 -23
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +19 -11
- package/assets/.appweaver/scripts/bash/aw-inventory.sh +15 -0
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +683 -92
- package/assets/.appweaver/scripts/bash/aw-xref.sh +3 -0
- package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +15 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +625 -64
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +3 -0
- package/assets/.appweaver/scripts/python/aw-crstate.py +194 -0
- package/assets/.appweaver/scripts/python/aw-foundation.py +151 -0
- package/assets/.appweaver/scripts/python/aw-inventory.py +1829 -63
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +3 -0
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +423 -0
- package/assets/.appweaver/scripts/python/aw-stack.py +251 -28
- package/assets/.appweaver/scripts/python/aw-testlog.py +148 -0
- package/assets/.appweaver/scripts/python/aw-xref.py +500 -81
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +1 -0
- package/assets/.appweaver/stack-profiles/java-spring.json +1 -0
- package/assets/.appweaver/stack-profiles/node-express.json +1 -0
- package/assets/.appweaver/stack-profiles/python-fastapi.json +1 -0
- package/assets/.appweaver/templates/constitution-domain-template.md +17 -2
- 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 +16 -11
- 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 +17 -17
- package/assets/.appweaver/templates/ui-delta-design-template.md +1 -1
- package/assets/.appweaver/templates/ui-design-template.md +4 -4
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +4489 -361
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +2274 -125
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +10 -6
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +4 -0
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +3 -1
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +5 -3
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +5 -1
- package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +13 -5
- package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +2 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +6 -2
- package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +70 -0
- package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +3 -0
- package/assets/guardrails/technology/dotnet-webapi/testing.md +10 -0
- package/assets/guardrails/technology/nextjs/ui-constitution.md +8 -1
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/.state/smoke.state.json +0 -32
|
@@ -36,9 +36,11 @@ behaviour
|
|
|
36
36
|
FR/US/SC/CN/DC/IC
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
- **One design per
|
|
39
|
+
- **One design per feature.** The four clean-architecture layers (Domain,
|
|
40
40
|
Application, API, Infrastructure) are **sections** of this one document, each at
|
|
41
|
-
design level.
|
|
41
|
+
design level. A feature is a capability scope the developer chooses — it may act on
|
|
42
|
+
several aggregates, and several features may act on one; its operations are user
|
|
43
|
+
stories inside it. *(This is
|
|
42
44
|
the **greenfield** layout, authored from `design-template.md`. See the Brownfield
|
|
43
45
|
variant below for reverse-engineered / existing-codebase designs.)*
|
|
44
46
|
- **High-level, not code.** It carries entities/value-objects/events, behaviour as
|
|
@@ -170,11 +172,13 @@ the gate and the review able to check coverage, and what keeps CR impact analysi
|
|
|
170
172
|
|
|
171
173
|
## Feature granularity (why a command is a delta, not a new feature)
|
|
172
174
|
|
|
173
|
-
A **feature is
|
|
174
|
-
`
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
A **feature is a capability scope the developer chooses** — `Order`, or
|
|
176
|
+
`PatientRegistration` beside `PatientMerge`. It may act on several aggregates, and
|
|
177
|
+
several features may act on one. The aggregates live above every feature: in the bounded
|
|
178
|
+
context's `domain-model.md` (one `## Aggregate:` section each) and in the OKF bundle (one
|
|
179
|
+
concept each, listing the features that act on it). An operation inside a feature's scope
|
|
180
|
+
(`CreateOrder`, `UpdateOrder`, …) is a **user story / delta** on that feature's
|
|
181
|
+
backend-plan.md:
|
|
178
182
|
|
|
179
183
|
- `CreateOrder` = US-1; `UpdateOrder` = US-2.
|
|
180
184
|
- Adding `UpdateOrder` is an additive CR: a **delta-spec** (`+US-2`), the change's own
|
|
@@ -192,8 +196,12 @@ with a shared entity, **feature = the aggregate** (`Order`), and individual comm
|
|
|
192
196
|
version (`v1.0 → v1.1`). The two are deliberately separate so a red `analyze-slice`
|
|
193
197
|
between them is still "fix and re-run" rather than a change that already shipped.
|
|
194
198
|
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
**Shared aggregate code is extended, never regenerated.** When two features act on one
|
|
200
|
+
aggregate, its root, entities and repository (`src/Domain/Order.cs`) are realised by
|
|
201
|
+
both plans: each names only the members its feature needs, and `implement` adds those
|
|
202
|
+
members to the existing file instead of writing it afresh. What a feature owns outright
|
|
203
|
+
is its own use-cases, endpoints and handlers. The invariants are the model's, so two
|
|
204
|
+
features never disagree about them.
|
|
197
205
|
|
|
198
206
|
## Migration status
|
|
199
207
|
|
|
@@ -90,7 +90,7 @@ the raw object, not its JSON string.
|
|
|
90
90
|
| `set` | engine | assign vars (`vars:` or decl-style `parameters:`); raw-object aware |
|
|
91
91
|
| `build_context` | engine | REPLACE the per-request envelope: `system:`/`context:` file lists (+ `context_from: <var>` holding paths a prior generation reported, + `vars:`) |
|
|
92
92
|
| `load_standards` | engine | F4 — append to the **sticky standards set** prepended to every system block (dedup, `optional:`, `files:`/`files_from_variable`). This is the byte-stable, prompt-cacheable prefix |
|
|
93
|
-
| `execute_script` | engine | F1 — run a `scripts:` ref (`subcommand:` + `args:`; sh/ps1 via `--os`); stdout's last line = JSON, bound via `outputs[]`; `on_error: continue`
|
|
93
|
+
| `execute_script` | engine | F1 — run a `scripts:` ref (`subcommand:` + `args:`; sh/ps1 via `--os`); stdout's last line = JSON, bound via `outputs[]`; `on_error: continue` tolerates a script that ran and exited non-zero (its outputs bind empty) — never an undeclared ref, which the pre-flight refuses before step 1; cached across resumes; runs for real even in mock transport |
|
|
94
94
|
| `verify_context` | engine | F2 — gates: `required_files` (+`allow_missing`), `require_non_empty` (+`on_empty[_message]`), `require_true` (+`on_false[_message]`), `require_value`+`equals` (+`on_mismatch[_message]`). `stop` raises a terminal stop |
|
|
95
95
|
| `agent_generate` | fresh agent | demo contract (code work): agent DECIDES file paths per the loaded standards, writes with its tools, reports `files[]` in the trailing ```json fence. `sdk-apply` = read-only generation + invoking agent applies |
|
|
96
96
|
| `agent_review` | fresh agent | payload-only review of `review_of_var`/`review_of` files (inlined into THIS request, never sticky) → `{pass, issues}` |
|
|
@@ -159,8 +159,13 @@ orchestrator.py <workflow.orch.yaml>
|
|
|
159
159
|
## Validation & smoke
|
|
160
160
|
|
|
161
161
|
- `--validate` every workflow/module (unknown actions/strategies, duplicate
|
|
162
|
-
ids, script refs + files on disk
|
|
163
|
-
|
|
162
|
+
ids, script refs + files on disk — including the refs of every included
|
|
163
|
+
module, which resolve against the caller's `scripts:` — `output_target.path`,
|
|
164
|
+
module paths, unproduced `{{vars}}`, dialect header).
|
|
165
|
+
- Every run starts with the same checks. A workflow with an ERROR is not
|
|
166
|
+
started: the engine exits 1 with `status: stopped` and the defects in the
|
|
167
|
+
reason, before any step runs and before a checkpoint response is ingested.
|
|
168
|
+
Warnings never block.
|
|
164
169
|
- `workflows/_smoke.orch.yaml` exercises every construct with no model and no
|
|
165
170
|
writes outside `.smoke-out`: run it `--non-interactive` (CI) and with
|
|
166
171
|
`--checkpoint` (protocol walk-through).
|
|
@@ -121,8 +121,10 @@ workflow:
|
|
|
121
121
|
to the standard governing every such behaviour is a UX- delta. A bound the SERVICE
|
|
122
122
|
must meet stays a CN-, because the service-side gates trace CN- and never read the
|
|
123
123
|
Experience Standards.
|
|
124
|
-
- The new operation is a NEW USER STORY — assign the next id after
|
|
125
|
-
in the master
|
|
124
|
+
- The new operation is a NEW USER STORY of THIS feature — assign the next id after
|
|
125
|
+
the highest US-n in the master. Whether a capability is its own feature is the
|
|
126
|
+
developer's decision, made when they chose this feature; never open, name or
|
|
127
|
+
imply another feature or a new aggregate here. Record the regression risk (shared
|
|
126
128
|
behaviour that must still hold).
|
|
127
129
|
- STRUCTURE IS NOT YOURS TO WRITE HERE. A new entity or value object, a changed
|
|
128
130
|
aggregate boundary, a new domain event, or a new or amended invariant (BR-n) is a
|
|
@@ -138,7 +140,7 @@ workflow:
|
|
|
138
140
|
touches the same aggregate or the same FR ids, say so under "Other open deltas"
|
|
139
141
|
in Impact. If THIS change only makes sense after one of them lands, say so in the
|
|
140
142
|
Wave rationale — that is a wave dependency, and the wave must then be RAISED above
|
|
141
|
-
that delta's
|
|
143
|
+
that delta's — name it under `## Depends on` and the wave is raised from that. Do not silently assume their
|
|
142
144
|
changes: until merged they are proposals, not the master.
|
|
143
145
|
- Do NOT edit the Wave / Created / Base version headers — scripts stamp those.
|
|
144
146
|
Change request: {{USER_INPUT}}
|
|
@@ -181,4 +183,25 @@ workflow:
|
|
|
181
183
|
entry MUST sit in a LOWER wave than this delta ({{wave}}). "None" when it stands
|
|
182
184
|
alone. Reading another delta is NOT depending on it — only assuming its changes is.
|
|
183
185
|
- Do NOT edit the Wave / Created / Base context version headers — scripts stamp those.
|
|
186
|
+
Naming a delta under "## Depends on" is what RAISES this change's wave, immediately
|
|
187
|
+
after you write it; you never set the number yourself.
|
|
184
188
|
Change request: {{USER_INPUT}}
|
|
189
|
+
|
|
190
|
+
# 4. THE WAVE THE DOCUMENTS THEMSELVES IMPLY. cr-setup stamped a provisional wave before
|
|
191
|
+
# either half existed — the highest already open, which is right for a change that
|
|
192
|
+
# stands alone and wrong for one that builds on another: it lands at its predecessor's
|
|
193
|
+
# wave, where the blocker fence reads the two as independent and lets them merge in
|
|
194
|
+
# either order. Now that "## Depends on" is written, that declaration decides: the wave
|
|
195
|
+
# is raised to one above the highest delta named there, in BOTH halves, so the number
|
|
196
|
+
# and the prose cannot drift apart. A change that names none keeps what it has, and one
|
|
197
|
+
# already sitting higher is left alone. The merge fence re-checks the same rule.
|
|
198
|
+
- id: derive_wave
|
|
199
|
+
action: execute_script
|
|
200
|
+
on_error: continue
|
|
201
|
+
script: { ref: actions }
|
|
202
|
+
subcommand: cr-wave-derive
|
|
203
|
+
args: ["--cr-id", "{{cr_id}}", "--stamp"]
|
|
204
|
+
outputs:
|
|
205
|
+
- { name: wave, from: "$.wave" }
|
|
206
|
+
- { name: wave_depends, from: "$.depends_on" }
|
|
207
|
+
- { name: wave_message, from: "$.message" }
|
|
@@ -1458,6 +1458,39 @@ def _refs_in(v):
|
|
|
1458
1458
|
return found
|
|
1459
1459
|
|
|
1460
1460
|
|
|
1461
|
+
def _module_script_refs(mpath, base_dir, seen):
|
|
1462
|
+
"""(step id, script ref) for every execute_script step an included module runs,
|
|
1463
|
+
following nested includes. Modules that cannot be read contribute nothing — the
|
|
1464
|
+
include check already warns about a module missing from disk."""
|
|
1465
|
+
path = next((c for c in (mpath, os.path.join(base_dir, mpath)) if c and os.path.exists(c)), "")
|
|
1466
|
+
if not path or path in seen:
|
|
1467
|
+
return []
|
|
1468
|
+
seen.add(path)
|
|
1469
|
+
try:
|
|
1470
|
+
with open(path, encoding="utf-8") as fh:
|
|
1471
|
+
mwf = load_yaml(fh.read())
|
|
1472
|
+
except Exception:
|
|
1473
|
+
return []
|
|
1474
|
+
out = []
|
|
1475
|
+
|
|
1476
|
+
def walk(ss):
|
|
1477
|
+
for s in ss or []:
|
|
1478
|
+
if not isinstance(s, dict):
|
|
1479
|
+
continue
|
|
1480
|
+
p = s.get("parameters") or s
|
|
1481
|
+
if s.get("action") == "execute_script":
|
|
1482
|
+
ref = (p.get("script") or {}).get("ref")
|
|
1483
|
+
if ref:
|
|
1484
|
+
out.append((s.get("id") or "step", ref))
|
|
1485
|
+
elif s.get("action") == "include" and s.get("module"):
|
|
1486
|
+
out.extend(_module_script_refs(s["module"], base_dir, seen))
|
|
1487
|
+
elif s.get("action") == "loop":
|
|
1488
|
+
walk(s.get("steps"))
|
|
1489
|
+
|
|
1490
|
+
walk((mwf or {}).get("workflow"))
|
|
1491
|
+
return out
|
|
1492
|
+
|
|
1493
|
+
|
|
1461
1494
|
def validate_workflow(wf, base_dir):
|
|
1462
1495
|
errors, warnings = [], []
|
|
1463
1496
|
if not isinstance(wf, dict):
|
|
@@ -1551,6 +1584,13 @@ def validate_workflow(wf, base_dir):
|
|
|
1551
1584
|
errors.append(f"{loc}: include needs 'module'")
|
|
1552
1585
|
elif not (os.path.exists(m) or os.path.exists(os.path.join(base_dir, m))):
|
|
1553
1586
|
warnings.append(f"{loc}: include module not found on disk: {m}")
|
|
1587
|
+
elif scripts:
|
|
1588
|
+
# A module declares no scripts: of its own — its refs resolve against
|
|
1589
|
+
# THIS workflow's map, so an undeclared one is this workflow's defect.
|
|
1590
|
+
for mid, ref in _module_script_refs(m, base_dir, set()):
|
|
1591
|
+
if ref not in scripts:
|
|
1592
|
+
errors.append(f"{loc}: included step {mid} uses script ref "
|
|
1593
|
+
f"'{ref}', not declared in top-level scripts:")
|
|
1554
1594
|
if action == "agent_query" and not step.get("outputs"):
|
|
1555
1595
|
warnings.append(f"{loc}: agent_query has no outputs[] to bind")
|
|
1556
1596
|
if action == "agent_author" and not (p.get("output_target") or {}).get("path"):
|
|
@@ -1682,6 +1722,31 @@ def main(argv=None):
|
|
|
1682
1722
|
print(f"\n{opts.workflow}: {len(errors)} error(s), {len(warnings)} warning(s)")
|
|
1683
1723
|
return 1 if errors else 0
|
|
1684
1724
|
|
|
1725
|
+
# ---- pre-flight: a workflow --validate rejects is never started ----------
|
|
1726
|
+
# An undeclared script ref, an unknown action or a duplicate step id is a defect in
|
|
1727
|
+
# the workflow file, and no run can recover from it — `on_error: continue` covers a
|
|
1728
|
+
# script that RAN and failed, not one the workflow never declared. Found mid-run, it
|
|
1729
|
+
# strands the operator after the questions were answered; found here, nothing has
|
|
1730
|
+
# happened yet. Runs BEFORE the checkpoint ingest, so a refused resume leaves the
|
|
1731
|
+
# pending response on disk for the run after the fix. Warnings do not block.
|
|
1732
|
+
try:
|
|
1733
|
+
with open(opts.workflow, encoding="utf-8") as fh:
|
|
1734
|
+
wf = load_yaml(fh.read())
|
|
1735
|
+
errors, _ = validate_workflow(wf, os.path.dirname(os.path.abspath(opts.workflow)))
|
|
1736
|
+
except Exception as e:
|
|
1737
|
+
wf, errors = None, [f"parse: {e}"]
|
|
1738
|
+
if errors:
|
|
1739
|
+
reason = (f"workflow {opts.workflow} has {len(errors)} defect(s) and was not started: "
|
|
1740
|
+
+ "; ".join(errors)
|
|
1741
|
+
+ " — fix the workflow file (python3 .appweaver/orchestrator/orchestrator.py "
|
|
1742
|
+
"<workflow> --validate lists them) and re-run.")
|
|
1743
|
+
if opts.json:
|
|
1744
|
+
print(json.dumps({"status": "stopped", "reason": reason, "errors": errors}, indent=2))
|
|
1745
|
+
else:
|
|
1746
|
+
print("\n=== STOPPED ===")
|
|
1747
|
+
print(reason)
|
|
1748
|
+
return 1
|
|
1749
|
+
|
|
1685
1750
|
# ---- checkpoint mode: load prior state + ingest the agent's response ----
|
|
1686
1751
|
# (before profile/strategy resolution, so a bare `--checkpoint` resume
|
|
1687
1752
|
# restores the original run's profile/strategy/model/os and prefills)
|
|
@@ -1787,9 +1852,6 @@ def main(argv=None):
|
|
|
1787
1852
|
print("ERROR: --transport real needs --strategy (or --agent-profile / --checkpoint)")
|
|
1788
1853
|
return 2
|
|
1789
1854
|
|
|
1790
|
-
with open(opts.workflow, encoding="utf-8") as fh:
|
|
1791
|
-
wf = load_yaml(fh.read())
|
|
1792
|
-
|
|
1793
1855
|
orch = Orchestrator(wf, opts)
|
|
1794
1856
|
try:
|
|
1795
1857
|
result = orch.run()
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
-->
|
|
54
54
|
# Design: [AGGREGATE]
|
|
55
55
|
|
|
56
|
-
**
|
|
56
|
+
**Feature**: `[Feature]`
|
|
57
57
|
|
|
58
58
|
**Plan home**: `specs/[Context]/[Feature]/backend-plan.md`
|
|
59
59
|
|
|
@@ -81,7 +81,9 @@
|
|
|
81
81
|
aggregate boundary and the BR-nnn invariants; where it does not, the spec
|
|
82
82
|
carries that structure alone. A domain rule neither states is recorded as a
|
|
83
83
|
§5 assumption, never smuggled in.
|
|
84
|
-
-
|
|
84
|
+
- One feature = one plan. A feature is the capability scope the developer chose
|
|
85
|
+
and may act on several aggregates, as several features may act on one. A later
|
|
86
|
+
requirement on this feature is a DELTA on this document.
|
|
85
87
|
|
|
86
88
|
STRUCTURE FOLLOWS THIS PROJECT'S CODE
|
|
87
89
|
- [GENERATED_STRUCTURE_NOTE] — form: one or two sentences naming the
|
|
@@ -189,7 +189,7 @@ workflow:
|
|
|
189
189
|
action: verify_context
|
|
190
190
|
require_non_empty: feature_name
|
|
191
191
|
on_empty: stop
|
|
192
|
-
on_empty_message: "No feature to adopt. Either run /appweaver select-feature first (pick the
|
|
192
|
+
on_empty_message: "No feature to adopt. Either run /appweaver select-feature first (pick the feature from the boundary map onboard laid out), then /appweaver adopt; or name it directly: /appweaver adopt <Feature>."
|
|
193
193
|
|
|
194
194
|
# WHICH BOUNDED CONTEXT THIS FEATURE LANDS IN, and a refusal to guess — the same gate
|
|
195
195
|
# select-feature carries, and needed here for the same reason: this stage also creates a
|
|
@@ -234,6 +234,26 @@ workflow:
|
|
|
234
234
|
- { name: spec_file, from: "$.spec_file" }
|
|
235
235
|
- { name: spec_exists, from: "$.spec_exists" }
|
|
236
236
|
|
|
237
|
+
# WHICH AGGREGATE THIS FEATURE ACTS ON. Usually the same name — one aggregate, one
|
|
238
|
+
# feature, the 1:1 default — but not when the boundary map split an aggregate into
|
|
239
|
+
# capabilities (PatientRegistration and PatientMerge over Patient). The reverse steps
|
|
240
|
+
# below write and judge the model section BY THIS NAME, and every later command finds
|
|
241
|
+
# the feature's part of the model the same way; writing it under the feature's name
|
|
242
|
+
# instead defines one root twice as soon as a sibling feature is adopted. No spec
|
|
243
|
+
# exists yet at this point, so it resolves from the confirmed boundary map, and a
|
|
244
|
+
# feature the map does not split resolves to its own name — exactly what this
|
|
245
|
+
# workflow always used.
|
|
246
|
+
- id: feature_aggregates
|
|
247
|
+
action: execute_script
|
|
248
|
+
script: { ref: xref }
|
|
249
|
+
subcommand: feature-aggregates
|
|
250
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
251
|
+
outputs:
|
|
252
|
+
- { name: aggregates_csv, from: "$.aggregates_csv" }
|
|
253
|
+
- { name: aggregate_sections, from: "$.sections" }
|
|
254
|
+
- { name: aggregate_source, from: "$.source" }
|
|
255
|
+
- { name: aggregate_primary, from: "$.primary" } # the concept this feature's OKF work lands on
|
|
256
|
+
|
|
237
257
|
# 1a. One-time baseline: refuse to clobber an already-adopted / authored spec.
|
|
238
258
|
# After adoption, changes go through the CR delta loop, not adopt.
|
|
239
259
|
# set-feature returns spec_exists as a JSON boolean; the condition path
|
|
@@ -247,14 +267,16 @@ workflow:
|
|
|
247
267
|
on_empty: stop
|
|
248
268
|
on_empty_message: "specs/ already has a spec.md for {{feature_name}} — adopt is a one-time baseline. For a change to an adopted slice use the CR delta loop (/appweaver specify → plan → analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta); to re-adopt from scratch, remove the feature dir first."
|
|
249
269
|
|
|
250
|
-
# 1b. Resolve
|
|
251
|
-
#
|
|
252
|
-
#
|
|
270
|
+
# 1b. Resolve the OKF concept of the aggregate this feature acts on → its source_files
|
|
271
|
+
# + module dir. The bundle is the PROJECT's knowledge and a concept is an aggregate,
|
|
272
|
+
# so a feature that is one capability over a shared aggregate reads the SHARED
|
|
273
|
+
# concept, which lists every capability's code; the source-map below narrows it to
|
|
274
|
+
# this feature's. The code itself is the source of truth (read during authoring).
|
|
253
275
|
- id: concept
|
|
254
276
|
action: execute_script
|
|
255
277
|
script: { ref: inventory }
|
|
256
278
|
subcommand: concept
|
|
257
|
-
args: ["--bundle", "{{bundle_dir}}", "--aggregate", "{{
|
|
279
|
+
args: ["--bundle", "{{bundle_dir}}", "--aggregate", "{{aggregate_primary}}"]
|
|
258
280
|
outputs:
|
|
259
281
|
- { name: concept_exists, from: "$.exists" }
|
|
260
282
|
- { name: concept_path, from: "$.concept_path" }
|
|
@@ -274,7 +296,7 @@ workflow:
|
|
|
274
296
|
action: verify_context
|
|
275
297
|
require_true: concept_exists
|
|
276
298
|
on_false: stop
|
|
277
|
-
on_false_message: "No feature '{{feature_name}}' in the onboard
|
|
299
|
+
on_false_message: "No concept for the aggregate '{{aggregate_primary}}' — the one feature '{{feature_name}}' acts on — in the onboard bundle under {{bundle_dir}}. If its bounded context has not been onboarded, run /appweaver onboard <Context> first; {{bundle_dir}}/aggregates/index.md lists each aggregate with the features that act on it. If '{{feature_name}}' is a NEW feature (no existing code), do NOT adopt it — author it through the forward pipeline (/appweaver specify → plan → implement), which onboard already made brownfield-ready."
|
|
278
300
|
|
|
279
301
|
# Governance prerequisite: the reverse steps below are authored AGAINST the project
|
|
280
302
|
# onboard established (the constitution halves + guardrails/projects/<project>/ rule set).
|
|
@@ -291,6 +313,16 @@ workflow:
|
|
|
291
313
|
- { name: gov_domain, from: "$.domain" }
|
|
292
314
|
- { name: rulemap_exists, from: "$.rulemap_exists" }
|
|
293
315
|
|
|
316
|
+
# ⌛ OPEN, now that onboard runs once per BOUNDED CONTEXT: this gate asks whether
|
|
317
|
+
# ANY context has been onboarded, and it needs to ask whether THIS FEATURE'S
|
|
318
|
+
# context has. The narrower question is already answered next door — require_concept
|
|
319
|
+
# stops when the feature has no OKF concept, and a feature only has one once its
|
|
320
|
+
# context was onboarded — so the sequence is safe today and the gap is in what the
|
|
321
|
+
# message says, not in what it lets through. Tightening it properly means resolving
|
|
322
|
+
# the feature's context from the concept's `bounded_context:` stamp and naming that
|
|
323
|
+
# context in the refusal. Related and unresolved: `fidelity_gate` is per feature and
|
|
324
|
+
# has no context-level equivalent — a context-wide one would fail a whole context on
|
|
325
|
+
# a single mismapped feature.
|
|
294
326
|
- id: require_governance
|
|
295
327
|
action: verify_context
|
|
296
328
|
require_true: rulemap_exists
|
|
@@ -451,7 +483,7 @@ workflow:
|
|
|
451
483
|
output_target: { type: file, path: "{{spec_file}}" }
|
|
452
484
|
task: |
|
|
453
485
|
Reverse-engineer the MASTER specification (the target spec.md in context)
|
|
454
|
-
for the
|
|
486
|
+
for the feature '{{feature_name}}' FROM ITS CODE. The bundle concept (in
|
|
455
487
|
context) is the INDEX — its `source_files` list the code that IS this slice;
|
|
456
488
|
READ those files (under {{module_dir}}, listed below) as the SOURCE OF TRUTH.
|
|
457
489
|
The concept is a summary; the CODE is authoritative — if they disagree, the
|
|
@@ -459,7 +491,13 @@ workflow:
|
|
|
459
491
|
|
|
460
492
|
Capture what the code ACTUALLY DOES, as-built (bugs/quirks included), in the
|
|
461
493
|
template's vocabulary:
|
|
462
|
-
-
|
|
494
|
+
- THIS FEATURE ACTS ON {{aggregates_csv}}. Write exactly that on the Domain Model
|
|
495
|
+
Reference's `**Acts on aggregate(s)**:` line, each name in backticks — every later
|
|
496
|
+
command finds this feature's part of the domain model by reading that line. The
|
|
497
|
+
feature is the capability scope the developer confirmed in the boundary map,
|
|
498
|
+
whatever it is named: its user stories are that capability's operations and no
|
|
499
|
+
others. Sharing a name with an aggregate does not make it that aggregate's only
|
|
500
|
+
feature — other features may act on the same aggregate. Keep US-1.
|
|
463
501
|
- Each command / endpoint / use-case the code exposes is a USER STORY (US-n),
|
|
464
502
|
never a separate feature. Derive its Given/When/Then from the code's paths.
|
|
465
503
|
- System Requirements in EARS (THE SYSTEM SHALL / WHEN / IF…THEN / WHILE /
|
|
@@ -501,7 +539,7 @@ workflow:
|
|
|
501
539
|
rule the code does not exhibit. Behaviour that is clearly present is
|
|
502
540
|
"(certain)".
|
|
503
541
|
- Reference the source-map: note that legacy-artefact ↔ requirement links
|
|
504
|
-
live in `
|
|
542
|
+
live in `{{feature_dir}}/source-map.md` (authored next).
|
|
505
543
|
- This is v1.0: fill the Change History row; keep US-1's "Added: v1.0".
|
|
506
544
|
- Business-facing + testable; NO implementation mechanics (an external interop
|
|
507
545
|
OBLIGATION is an IC- requirement, never the adapter design).
|
|
@@ -538,20 +576,32 @@ workflow:
|
|
|
538
576
|
output_target: { type: file, path: "{{domain_model}}" }
|
|
539
577
|
task: |
|
|
540
578
|
Reverse-engineer the BOUNDED CONTEXT's DOMAIN MODEL (the target domain-model.md in
|
|
541
|
-
context) for the aggregate '{{feature_name}}'
|
|
542
|
-
|
|
543
|
-
|
|
579
|
+
context) for the aggregate(s) the feature '{{feature_name}}' acts on —
|
|
580
|
+
{{aggregates_csv}} — FROM ITS CODE. The reversed spec (in context) names them and
|
|
581
|
+
cites the invariant ids; this document DEFINES them. READ the source files listed
|
|
582
|
+
below — the code is the SOURCE OF TRUTH.
|
|
544
583
|
|
|
545
|
-
-
|
|
584
|
+
- THE SECTION IS NAMED AFTER THE AGGREGATE, NEVER THE FEATURE: {{aggregate_sections}},
|
|
585
|
+
exactly. Every later command finds this feature's part of the model by that
|
|
586
|
+
heading. Where the feature and the aggregate share a name the two are the same
|
|
587
|
+
thing; where they differ, a section headed '{{feature_name}}' is a defect — the
|
|
588
|
+
next feature over the same aggregate would define its root a second time.
|
|
589
|
+
- IF THAT SECTION ALREADY EXISTS, another feature over the same aggregate was adopted
|
|
590
|
+
first. EXTEND it and never write a second one: add `{{feature_name}}` to its
|
|
591
|
+
`Serving specs` line, and add only what THIS feature's code evidences that the
|
|
592
|
+
section lacks — an entity, a lifecycle state, a BR-nnn numbered after the highest
|
|
593
|
+
one already there. Leave what the earlier feature wrote exactly as it is.
|
|
594
|
+
- OTHERWISE ADD ONE `## Aggregate: <Name>` SECTION per aggregate, `{{feature_name}}`
|
|
595
|
+
on its `Serving specs` line: its boundary
|
|
546
596
|
(owns / references by id / out of scope), entities, value objects, domain events,
|
|
547
597
|
lifecycle states, and every always-true invariant the code enforces as a stable
|
|
548
598
|
BR-nnn with its RULE TEXT. The spec cites those ids and never restates them.
|
|
549
599
|
- IF THE MODEL ALREADY HAS CONTENT, this is the context's SECOND (or later) adopted
|
|
550
|
-
slice. ADD this
|
|
551
|
-
NOT rewrite, renumber or "tidy" the aggregates
|
|
552
|
-
their ids, and a renumbered BR breaks every
|
|
553
|
-
`{{feature_name}}` to the `Serving specs` line of any
|
|
554
|
-
slice also acts on.
|
|
600
|
+
slice. ADD any of this feature's aggregates not already there as a new section and
|
|
601
|
+
EXTEND the ubiquitous language. Do NOT rewrite, renumber or "tidy" the aggregates
|
|
602
|
+
already there — other specs cite their ids, and a renumbered BR breaks every
|
|
603
|
+
citation silently. Add `{{feature_name}}` to the `Serving specs` line of any
|
|
604
|
+
existing aggregate this slice also acts on.
|
|
555
605
|
- MODEL WHAT THE CODE EXHIBITS, NOT WHAT GOOD DESIGN WOULD WANT. Classify entities
|
|
556
606
|
vs value objects by whether the code gives the thing an IDENTITY; draw the
|
|
557
607
|
boundary where the code actually draws it (what it loads and saves together).
|
|
@@ -670,7 +720,7 @@ workflow:
|
|
|
670
720
|
task: |
|
|
671
721
|
Reverse-engineer the FRONTEND LANE's master plan (the target frontend-plan.md
|
|
672
722
|
in context — it is this project's code-derived UI plan template) for the
|
|
673
|
-
|
|
723
|
+
feature '{{feature_name}}', FROM ITS UI CODE. Fill the template; preserve its
|
|
674
724
|
section order.
|
|
675
725
|
|
|
676
726
|
THE UI CODE IS THE SOURCE OF TRUTH — describe the surface AS-BUILT, quirks
|
|
@@ -718,13 +768,19 @@ workflow:
|
|
|
718
768
|
output_target: { type: file, path: "{{feature_dir}}/source-map.md" }
|
|
719
769
|
task: |
|
|
720
770
|
Author the source-map sidecar (the target in context) from the template,
|
|
721
|
-
for
|
|
771
|
+
for the feature '{{feature_name}}'. It is the SOURCE ANCHOR of the traceability
|
|
722
772
|
spine — it keeps spec.md / backend-plan.md clean while recording provenance here.
|
|
723
773
|
- One row per legacy artefact in the bundle concept's `source_files` (read
|
|
724
774
|
them under {{module_dir}}): map each to the spec ids (FR/BR/US/CN/DC/IC)
|
|
725
775
|
and the backend-plan element (§/name) that now describe it, with CONFIDENCE
|
|
726
776
|
(certain | inferred). EVERY source file MUST appear — the Fidelity gate
|
|
727
777
|
checks full coverage both ways.
|
|
778
|
+
- THE CONCEPT IS THE AGGREGATE'S ('{{aggregate_primary}}') and lists every feature's
|
|
779
|
+
code on it. Where its `features:` names features besides '{{feature_name}}', a
|
|
780
|
+
file that serves ONLY one of those sibling capabilities is not this feature's:
|
|
781
|
+
record it under Unmapped / open as `sibling: <Feature>`, never as a mapped row.
|
|
782
|
+
The mapped rows ARE this feature's file list — later commands hand exactly those
|
|
783
|
+
files to their fix loops.
|
|
728
784
|
- Where this slice has a UI half, its files map the same way, to the UX-/US-
|
|
729
785
|
they realise and the frontend-plan.md section (§/name) that describes them —
|
|
730
786
|
one spine, both lanes. UI files to map: {{ui_source_files}}
|
|
@@ -772,17 +828,24 @@ workflow:
|
|
|
772
828
|
route it describes that the UI code does not have is a fabricated-plan, and a
|
|
773
829
|
UI file with no home in the source-map is unmapped-code. Classify findings:
|
|
774
830
|
- BLOCKING (structural — set ok=false if any):
|
|
775
|
-
(1) unmapped-code: a source file with NO home in the source-map
|
|
831
|
+
(1) unmapped-code: a source file with NO home in the source-map — one recorded
|
|
832
|
+
there as `sibling: <Feature>` has a home;
|
|
776
833
|
(2) fabricated-spec / fabricated-model / fabricated-plan: a spec id, model
|
|
777
834
|
element or plan element with NO basis in the code (reverse-engineering must
|
|
778
835
|
not invent behaviour or structure). For the MODEL specifically: an entity or
|
|
779
836
|
value object the code does not have, an aggregate boundary it does not draw,
|
|
780
837
|
a lifecycle state it never enters, or a BR-nnn whose rule nothing enforces.
|
|
781
838
|
An anemic slice reversing as an anemic model is CORRECT, not a gap.
|
|
782
|
-
- ONLY
|
|
783
|
-
may already carry sections from earlier adopted slices — judge ONLY
|
|
784
|
-
|
|
785
|
-
|
|
839
|
+
- ONLY THIS FEATURE'S SECTIONS ARE THIS RUN'S. domain-model.md is the CONTEXT's and
|
|
840
|
+
may already carry sections from earlier adopted slices — judge ONLY
|
|
841
|
+
{{aggregate_sections}}. Do not report a sibling aggregate as unmapped because this
|
|
842
|
+
slice's code does not contain it. Where one of those sections' `Serving specs`
|
|
843
|
+
lists an EARLIER feature as well, the aggregate is shared: judge what this run
|
|
844
|
+
ADDED to it, and do not score the earlier feature's entities, states or
|
|
845
|
+
invariants as fabricated because this slice's code does not touch them.
|
|
846
|
+
- ALSO BLOCKING (fabricated-model): a `## Aggregate:` section this run created under
|
|
847
|
+
a name that is not one of {{aggregates_csv}} — most often '{{feature_name}}', the
|
|
848
|
+
feature's own name, used as if it were the aggregate's.
|
|
786
849
|
- REVIEW (does NOT block ok — these are the Adopt review worklist, closed out
|
|
787
850
|
of band): items marked "(inferred)"; as-built quirks the code shows but the
|
|
788
851
|
spec under-states; invariants the code enforces that may be missing/weak;
|
|
@@ -804,7 +867,7 @@ workflow:
|
|
|
804
867
|
context: ["{{spec_file}}", "{{domain_model}}", "{{backend_plan}}", "{{feature_dir}}/source-map.md"]
|
|
805
868
|
output_target: { type: file, path: "{{feature_dir}}/adopt-review-report.md" }
|
|
806
869
|
task: |
|
|
807
|
-
Write an Adopt Review report (the target) for
|
|
870
|
+
Write an Adopt Review report (the target) for the feature '{{feature_name}}' (acting on {{aggregates_csv}}) —
|
|
808
871
|
the out-of-band worklist for this reverse-engineered baseline. Markdown:
|
|
809
872
|
- "# Adopt Review — {{feature_name}}" + today's date + "origin: reverse-engineered".
|
|
810
873
|
- "verdict:" line — "BLOCKING ✗ · <n>" if blocking gaps exist, else "REVIEW ○ · <m> item(s)".
|
|
@@ -829,7 +892,7 @@ workflow:
|
|
|
829
892
|
action: verify_context
|
|
830
893
|
require_true: fidelity_ok
|
|
831
894
|
on_false: stop
|
|
832
|
-
on_false_message: "Fidelity FAILED for {{feature_name}} — the reversed documents do not match the code (blocking gaps: {{blocking_gaps}}). See
|
|
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."
|
|
833
896
|
|
|
834
897
|
# ═══════════ OKF CONCEPT — COMPLETE the shallow scan concept (deep reverse) ═══
|
|
835
898
|
# onboard wrote this aggregate's OKF concept SHALLOW (a discovery stub — enough to pick the
|
|
@@ -854,29 +917,37 @@ workflow:
|
|
|
854
917
|
tools: "Read,Write,Edit"
|
|
855
918
|
permission_mode: acceptEdits
|
|
856
919
|
task: |
|
|
857
|
-
COMPLETE the OKF
|
|
858
|
-
|
|
920
|
+
COMPLETE the OKF concept of the aggregate '{{aggregate_primary}}' at {{concept_path}} —
|
|
921
|
+
the aggregate this run adopted the feature '{{feature_name}}' over. onboard wrote it
|
|
922
|
+
SHALLOW (a discovery stub from the whole-repo scan). The deep reverse has now
|
|
859
923
|
produced a TRUSTWORTHY spec.md + domain-model.md + backend-plan.md + source-map.md (in
|
|
860
924
|
context); deepen the
|
|
861
925
|
concept to match and LINK those artifacts. Follow the OKF concept template in context.
|
|
926
|
+
THE CONCEPT IS THE AGGREGATE'S, NOT THE FEATURE'S. Where its `features:` names more
|
|
927
|
+
than one feature, another feature's adopt may already have deepened it, and nothing
|
|
928
|
+
that run wrote is this run's to remove — reconcile ADDITIVELY.
|
|
862
929
|
RECONCILE, do NOT rewrite:
|
|
863
930
|
- DEEPEN the responsibilities + the owned entities / value objects / domain events /
|
|
864
931
|
commands (user stories) / API endpoints / persistence to match the reversed spec,
|
|
865
932
|
model and plan — the ENTITIES and VALUE OBJECTS come from domain-model.md, which is
|
|
866
933
|
the authority on them; correct any drift the shallow scan got wrong (the deep docs
|
|
867
|
-
win).
|
|
868
|
-
- LINK the deep artifacts: add bundle links to
|
|
934
|
+
win). A sibling capability's commands and endpoints stay where they are.
|
|
935
|
+
- LINK the deep artifacts: add bundle links to {{feature_dir}}/spec.md,
|
|
869
936
|
backend-plan.md, source-map.md and the context's domain-model.md so the concept
|
|
870
|
-
points at the deep truth.
|
|
871
|
-
-
|
|
872
|
-
OKF)
|
|
873
|
-
|
|
874
|
-
|
|
937
|
+
points at the deep truth. Keep every link another feature's adopt added.
|
|
938
|
+
- UNION `source_files` with the source-map's mapped files (grouped by the code's OWN
|
|
939
|
+
folders, per OKF) — never drop a file already listed, which may be a sibling
|
|
940
|
+
capability's — and set `timestamp` to the baseline (inventory HEAD) commit time.
|
|
941
|
+
- Update frontmatter: keep `origin: reverse-engineered`; make sure `features:`
|
|
942
|
+
includes '{{feature_name}}'; stamp `generated_by: aw-spec-adopt`; and record that
|
|
943
|
+
'{{feature_name}}' is ADOPTED / deep-reversed. Where several features act on the
|
|
944
|
+
aggregate, record WHICH of them are adopted rather than marking the whole concept
|
|
945
|
+
done — the others are still shallow.
|
|
875
946
|
- Resolve any `open_questions` the deep reverse answered; keep the rest.
|
|
876
947
|
- Do NOT touch the concept's Depends-On / Cross-References sections (xref_record
|
|
877
948
|
maintains those next), and do NOT edit any OTHER aggregate concept.
|
|
878
|
-
Append a one-line `log.md` entry (bundle root) under today's date noting
|
|
879
|
-
was deep-adopted. Write ONLY {{concept_path}} (+ its dir index.md / bundle log.md if a
|
|
949
|
+
Append a one-line `log.md` entry (bundle root) under today's date noting the feature
|
|
950
|
+
'{{feature_name}}' of '{{aggregate_primary}}' was deep-adopted. Write ONLY {{concept_path}} (+ its dir index.md / bundle log.md if a
|
|
880
951
|
link or description changed). Report what you deepened.
|
|
881
952
|
outputs:
|
|
882
953
|
- { name: concept_completed, from: "$.files" }
|
|
@@ -886,14 +957,17 @@ workflow:
|
|
|
886
957
|
# (its External integrations table, IC-); record them into the shared OKF bundle — a
|
|
887
958
|
# Cross-References row in every
|
|
888
959
|
# SIBLING aggregate this slice uses (their inventory concepts already exist, so this
|
|
889
|
-
# resolves them rather than stubbing), plus
|
|
960
|
+
# resolves them rather than stubbing), plus the Depends-On of the aggregate this
|
|
961
|
+
# feature acts on — the union over every feature on that aggregate, so adopting one
|
|
962
|
+
# capability never erases another's edges. Keyed by --feature-dir so record resolves
|
|
963
|
+
# the aggregate itself rather than being told a feature name. Same
|
|
890
964
|
# reconcile-on-write helper as forward implement, stamped origin reverse-engineered,
|
|
891
965
|
# additive to the inventory bundle. Runs only after fidelity passes.
|
|
892
966
|
- id: xref_record
|
|
893
967
|
action: execute_script
|
|
894
968
|
script: { ref: xref }
|
|
895
969
|
subcommand: record
|
|
896
|
-
args: ["--
|
|
970
|
+
args: ["--feature-dir", "{{feature_dir}}", "--design", "{{backend_plan}}", "--bundle", "{{bundle_dir}}", "--origin", "reverse-engineered"]
|
|
897
971
|
outputs:
|
|
898
972
|
- { name: xref_wrote, from: "$.wrote" }
|
|
899
973
|
- { name: xref_edge_count, from: "$.edge_count" }
|
|
@@ -1115,7 +1189,12 @@ outputs:
|
|
|
1115
1189
|
ui_lane: "{{ui_lane}}"
|
|
1116
1190
|
ui_count: "{{ui_count}}"
|
|
1117
1191
|
source_map: "{{feature_dir}}/source-map.md"
|
|
1118
|
-
|
|
1192
|
+
feature: "{{feature_name}}"
|
|
1193
|
+
# What the feature acts on. Equal to the feature on a 1:1 slice; not on a feature that
|
|
1194
|
+
# is one capability over a shared aggregate. aggregate_source says which of the spec,
|
|
1195
|
+
# the boundary map or the feature name answered.
|
|
1196
|
+
aggregate: "{{aggregates_csv}}"
|
|
1197
|
+
aggregate_source: "{{aggregate_source}}"
|
|
1119
1198
|
source_count: "{{source_count}}"
|
|
1120
1199
|
review_items: "{{review_items}}"
|
|
1121
1200
|
concept_completed: "{{concept_completed}}"
|
|
@@ -1125,4 +1204,4 @@ outputs:
|
|
|
1125
1204
|
composed_file: "{{composed_file}}"
|
|
1126
1205
|
technical_reconcile_ok: "{{technical_ok}}"
|
|
1127
1206
|
domain_reconcile_ok: "{{domain_ok}}"
|
|
1128
|
-
next: "Adopted {{feature_name}} (spec.md + backend-plan.md + source-map.md, v1.0) and
|
|
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."
|