aw-spec-cli 0.1.31 → 0.1.33
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 +10 -3
- package/assets/.appweaver/STACK-PROFILES.md +9 -1
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +10 -6
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +53 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +128 -22
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +77 -26
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +104 -33
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +6 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +7 -4
- package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +20 -5
- package/assets/.appweaver/scripts/python/aw-inventory.py +2064 -259
- package/assets/.appweaver/scripts/python/aw-stack.py +13 -4
- package/assets/.appweaver/scripts/python/aw-xref.py +63 -43
- package/assets/.appweaver/templates/okf-concept-template.md +3 -1
- package/assets/.appweaver/templates/proposed-inventory-template.md +52 -13
- package/assets/foundations/java/Hexagonal/unpack-foundation.ps1 +168 -0
- package/package.json +1 -1
|
@@ -82,11 +82,18 @@ same traceability spine and the two deferral ledgers — with **one difference t
|
|
|
82
82
|
are **derived from the code's own organisation** (its folders / projects / modules)
|
|
83
83
|
rather than the fixed Domain/Application/API/Infrastructure four. A component the
|
|
84
84
|
code has that fits no textbook layer (a UI tier, a worker, a host project) gets its
|
|
85
|
-
own section; a layer the code lacks does not appear.
|
|
85
|
+
own section; a layer the code lacks does not appear. Three neutral anchors are kept
|
|
86
86
|
because every system has them and the tooling keys on them: **§1 is the domain/data
|
|
87
|
-
model** (the cross-reference tool reads it for `<Other>Id` reference edges)
|
|
87
|
+
model** (the cross-reference tool reads it for `<Other>Id` reference edges), one
|
|
88
88
|
**§2 subsection is the External Integrations table** (matched by heading keyword, so
|
|
89
|
-
`aw-xref` extracts contract edges from either layout)
|
|
89
|
+
`aw-xref` extracts contract edges from either layout), and **each published endpoint
|
|
90
|
+
is an endpoint block** — the greenfield §3 form, `#### `VERB /route`` with its
|
|
91
|
+
**Operation id**, followed by the "Requests from the frontend lane" table — inside
|
|
92
|
+
whichever §2 axis publishes it. The contract scripts read exactly that form: the API
|
|
93
|
+
draft is written from the headings, and the request ledger between the two lanes reads
|
|
94
|
+
the operation ids and the table. An endpoint the code already serves keeps the id it
|
|
95
|
+
publishes; where it publishes none the plan writes `—` and records a §5 assumption,
|
|
96
|
+
never an invented id. The `debt` ledger records
|
|
90
97
|
only debt the **code itself** evidences (stubs, dead code, torn seams) — being
|
|
91
98
|
organised unlike a reference architecture is **not** debt, it is the as-built shape;
|
|
92
99
|
migrating toward a target is a separate modernization decision.
|
|
@@ -113,6 +113,14 @@ project it does not exist yet — the whole point of declaring `backend/` on day
|
|
|
113
113
|
`build`, `run`, and before `onboard` reverse-engineers a codebase out of it. That split is what lets one file serve a project with no code yet and a project that is
|
|
114
114
|
nothing but code.
|
|
115
115
|
|
|
116
|
+
A lane can also be declared before it is built on a project that has code — a new front end
|
|
117
|
+
planned beside an existing service, or the reverse. `inventory` and `onboard` read code, so
|
|
118
|
+
they pass `--allow-missing-root` and read the lanes whose folders exist (`kinds_on_disk`): a
|
|
119
|
+
declared lane whose lane root folder is not there yet is read and governed as absent, and
|
|
120
|
+
named in a warning and in the proposal's Scanned line. Its governance is the developer's to
|
|
121
|
+
provide before the forward pipeline builds it. Both stop, with each missing folder named
|
|
122
|
+
(`missing_roots`), only when no declared lane's folder exists.
|
|
123
|
+
|
|
116
124
|
Two failure classes, deliberately distinguished:
|
|
117
125
|
|
|
118
126
|
- **MISSING** — the phase needs a lane of this kind and none is declared. Reported only for
|
|
@@ -691,7 +699,7 @@ implementation and the twins cannot drift.
|
|
|
691
699
|
| `contract-check --plan p [--doc d]` | the operation ids a frontend plan cites (its §5 `Operation id` column, or a delta plan's `Contract Binding` rows; never the request table) against the document's `operationId`s — by script, no model. `drafted[]` names the cited ids that are only draft operations |
|
|
692
700
|
| `contract-impact --delta-backend p --frontend-plan p [--delta-frontend p]` | the operations a change's service delta marks `changed` or `removed` in its "Contract deltas for the frontend lane" table that the frontend master plan binds to, and which of them the change's frontend delta plan does not name (`unaddressed[]`) — `analyze-plan-delta`'s warning. By script, no model |
|
|
693
701
|
| `lane-resolve [--kind k] [--id lane] [--feature-dir p]` | read; exit 0 always. `exists` reports the file, `declared` reports the lanes — the shipped-but-unfilled seed is `exists:true, declared:false`. Returns the matched lane **and** both kinds flattened — `backend_source_path`, `frontend_root_exists`, `backend_build` … — so a workflow binds one variable instead of calling twice — and the working-lane fields of §2a |
|
|
694
|
-
| `lane-verify [--require backend\|frontend]… [--allow-missing-root] [--working-only] [--command phase]` | **the gate**: is the declaration usable for the phase about to run. Never writes, and **always exits 0** — the calling workflow's `verify_context` owns the stop, so the reason arrives as text. `--working-only` checks on disk only the working lanes' root folders and the required kinds (§2a) |
|
|
702
|
+
| `lane-verify [--require backend\|frontend]… [--allow-missing-root] [--working-only] [--command phase]` | **the gate**: is the declaration usable for the phase about to run. Never writes, and **always exits 0** — the calling workflow's `verify_context` owns the stop, so the reason arrives as text. `--working-only` checks on disk only the working lanes' root folders and the required kinds (§2a). Whatever the flags, `kinds_on_disk[]` lists the kinds whose lane root folder exists and `missing_roots` the issue each declared lane without one raises when `--allow-missing-root` is not passed |
|
|
695
703
|
| `lane-select [--lane backend\|frontend\|both] [--default] [--preselect text]` | read or write `.appweaver/lane.json` (§2a). No argument reads it and returns the `select_options[]` the declared stacks offer; `--lane` writes one of them; `--default` writes the default — `select-stack`'s call |
|
|
696
704
|
| `cr-lanes [--cr-id id [--feature-dir p] [--stamp] [--set both\|backend\|frontend]]` | the lanes a change touches (§2a). No `--cr-id` returns the `select_options[]` the declared stacks offer, both first; with it, reads that change's `**Lanes**:` line — `lanes[]` is what it touches among the lanes declared now, every declared lane when nothing is recorded. `--set` writes an answer (a whole option line is accepted); `--stamp` alone writes every declared lane |
|
|
697
705
|
| `slice-gate-stamp --cr-id id --lane backend\|frontend [--feature-dir p] [--tests-green b] [--tests-ran b] [--requirements-ok b] [--findings json]` | write one lane's `analyze-slice-delta` result to `changes/<id>/.aw-slice-gate.<lane>.json` (§2a): `verdict` `pass` when no test failed and the requirements are met, else `fail`, with `tests_ran` (false when the lane declares no test command, so nothing ran), the findings counts and the sha256 of the delta-spec and the lane's delta plan it was gated against. Findings that cannot be read are recorded as `null`, never as none |
|
|
@@ -126,15 +126,19 @@ migration is needed (yes/no); any contract change other systems couple to.]
|
|
|
126
126
|
|
|
127
127
|
## Contract deltas for the frontend lane
|
|
128
128
|
|
|
129
|
-
<!-- What this change adds to
|
|
130
|
-
only part of this document the frontend lane may bind against.
|
|
131
|
-
the surface is unchanged. Omit the section entirely where the code
|
|
132
|
-
frontend lane. The operation id is the endpoint's name in the published
|
|
133
|
-
document; an endpoint that already has one keeps it.
|
|
129
|
+
<!-- What this change adds to, alters in or removes from the service's PUBLISHED
|
|
130
|
+
surface — the only part of this document the frontend lane may bind against.
|
|
131
|
+
`None` when the surface is unchanged. Omit the section entirely where the code
|
|
132
|
+
has no frontend lane. The operation id is the endpoint's name in the published
|
|
133
|
+
API document; an endpoint that already has one keeps it. A `changed` or
|
|
134
|
+
`removed` row whose operation the frontend plan binds to is reported by
|
|
135
|
+
analyze-plan-delta (contract-impact reads the Operation id and Change
|
|
136
|
+
columns) until the frontend delta of this change — or a follow-up change —
|
|
137
|
+
carries the matching change. -->
|
|
134
138
|
|
|
135
139
|
| Operation id | Contract | Change | Shape | Serves |
|
|
136
140
|
|---|---|---|---|---|
|
|
137
|
-
| `[OperationId]` | `[contract]` | [added / changed] | [request → response field shapes] | IC-[nnn] |
|
|
141
|
+
| `[OperationId]` | `[contract]` | [added / changed / removed] | [request → response field shapes] | IC-[nnn] |
|
|
138
142
|
|
|
139
143
|
## Other open deltas
|
|
140
144
|
|
|
@@ -30,6 +30,13 @@
|
|
|
30
30
|
• Exactly one §2 subsection's heading contains the word "Integration".
|
|
31
31
|
`aw-xref` matches it by keyword to extract contract edges, which is what
|
|
32
32
|
lets a derived layout and the greenfield layout be read by one tool.
|
|
33
|
+
• Each published endpoint is a `####` ENDPOINT BLOCK — the heading
|
|
34
|
+
`#### `[VERB] [/route]`` and its **Operation id** field — with the
|
|
35
|
+
"Requests from the frontend lane" table after the blocks, inside whichever
|
|
36
|
+
§2 axis publishes them. The contract scripts read exactly this form:
|
|
37
|
+
`contract-draft` drafts api-draft.json from the headings, and
|
|
38
|
+
`contract-requests` reads the operation ids and the table. The axis keeps
|
|
39
|
+
the project's own name; only the block form is fixed.
|
|
33
40
|
• The traceability spine (§4) and both ledgers (§5 DA-n, §6 WA-n) survive
|
|
34
41
|
every layout — the analyze gates read them.
|
|
35
42
|
• The §0–§6 numbering itself: `plan`'s bootstrap_bf branch names those
|
|
@@ -169,9 +176,8 @@
|
|
|
169
176
|
the titles are the code's answer — the slots below are illustrative
|
|
170
177
|
positions, not a required set. In each: the components that live there,
|
|
171
178
|
their behaviour in signature form, and the contracts they expose or
|
|
172
|
-
require.
|
|
173
|
-
|
|
174
|
-
the frontend plan cites and implement looks the endpoint up by. -->
|
|
179
|
+
require. The endpoints the service PUBLISHES take the endpoint block form
|
|
180
|
+
shown in the axis below. -->
|
|
175
181
|
|
|
176
182
|
### 2.1 [PROJECT_STRUCTURAL_AXIS_NAME]
|
|
177
183
|
|
|
@@ -186,6 +192,50 @@
|
|
|
186
192
|
|
|
187
193
|
<!-- … one per axis the code actually shows, then stop … -->
|
|
188
194
|
|
|
195
|
+
### 2.[M] [AXIS_THAT_PUBLISHES_THE_ENDPOINTS]
|
|
196
|
+
|
|
197
|
+
<!-- PINNED FORM, in whichever axis this project publishes its endpoints from. The
|
|
198
|
+
axis keeps the project's own name and its component table; what is fixed is
|
|
199
|
+
each endpoint's block below it and the requests table after the blocks,
|
|
200
|
+
because the contract scripts read exactly these: the `#### `VERB /route``
|
|
201
|
+
heading (contract-draft writes api-draft.json from it), the **Operation id**
|
|
202
|
+
field (the id the service serves, which the frontend plan binds to) and the
|
|
203
|
+
`| Request | Outcome |` table (contract-requests). A project that publishes no
|
|
204
|
+
endpoint omits both. -->
|
|
205
|
+
|
|
206
|
+
| Component | Behaviour (signature) | Pre / Post / Raises | Serves |
|
|
207
|
+
|---|---|---|---|
|
|
208
|
+
| `[Component]` | `[operation](args) -> result` | [pre / post / raises] | FR-00X · US-Y |
|
|
209
|
+
|
|
210
|
+
#### `[VERB] [/route]` — [purpose] · Serves US-Y · FR-00X
|
|
211
|
+
|
|
212
|
+
- **Operation id**: `[OperationId]` <!-- the endpoint's name in the published API document. An endpoint the code already serves keeps the id it publishes, spelled exactly as the code spells it; where it publishes none, write `—` and record a §5 DA-n saying so — never an invented id, which the service's exported document would not carry. A NEW endpoint takes an id unique across the service, and implement gives it exactly that. -->
|
|
213
|
+
- **Auth**: [required role/scope, as enforced]
|
|
214
|
+
- **Request**: `[path/query params]` + body `{ [field]: [type], … }`
|
|
215
|
+
- **Success**: `2xx` → `{ [response shape] }`
|
|
216
|
+
- **Errors** *(the error contract)*:
|
|
217
|
+
|
|
218
|
+
| Status | When | Body shape |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| [status] | [when it is returned] | [body shape] |
|
|
221
|
+
|
|
222
|
+
- **Maps to**: `[Component]` (§2.n)
|
|
223
|
+
|
|
224
|
+
#### Requests from the frontend lane
|
|
225
|
+
|
|
226
|
+
<!-- THE ANSWERS to the frontend plan's "Contract deltas needed" table, one row
|
|
227
|
+
per RQ-n it raises. PUBLISH: add the endpoint above — under the proposed
|
|
228
|
+
operation id unless it clashes with one this service already uses — and name
|
|
229
|
+
the id here. DECLINE: say why in one line; a request the spec does not
|
|
230
|
+
support is declined as a spec gap for `specify`, never published on a guess.
|
|
231
|
+
Keep every answered row; write `None` instead of the table when the frontend
|
|
232
|
+
plan raises no request — always so for a plan reversed from code, whose
|
|
233
|
+
frontend binds only to what the service already serves. -->
|
|
234
|
+
|
|
235
|
+
| Request | Outcome | Operation id | Why |
|
|
236
|
+
|---|---|---|---|
|
|
237
|
+
| RQ-[n] | published / declined | `[OperationId]` / — | [one line — required when declined] |
|
|
238
|
+
|
|
189
239
|
### 2.[N] External Integrations
|
|
190
240
|
|
|
191
241
|
<!-- PINNED ANCHOR — the heading MUST contain the word "Integration"; the
|
|
@@ -71,7 +71,18 @@
|
|
|
71
71
|
# =============================================================================
|
|
72
72
|
|
|
73
73
|
name: aw-spec-adopt
|
|
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
|
|
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 its lane plans — backend-plan.md where a backend lane is declared and the slice has service code, 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.3.0 — the SERVICE half is reversed only where the slice has one (svc_lane: a backend lane
|
|
76
|
+
# is declared and the slice has service files), the twin of ui_lane. A frontend-only
|
|
77
|
+
# project gets no backend plan and no cross-slice edges, and its model is read from
|
|
78
|
+
# the UI files; a UI-only slice in a repo with both lanes records lanes=[frontend].
|
|
79
|
+
# A failed fidelity check names the two ways to resolve it — re-adopt, or correct
|
|
80
|
+
# the documents by hand — and what each leaves behind.
|
|
81
|
+
# 2.2.0 — the reversed plans take part in the API ledger between the lanes: the backend plan
|
|
82
|
+
# writes each endpoint the slice publishes as an endpoint block (`#### `VERB /route``
|
|
83
|
+
# + **Operation id**, the id the code publishes — `—` and a DA-n where it publishes
|
|
84
|
+
# none, never an invented one) with a "Requests from the frontend lane" table, and the
|
|
85
|
+
# frontend plan's Contract Binding rows cite those ids.
|
|
75
86
|
# 2.1.0 — every reverse step is scoped to the FEATURE, not its aggregate: the boundary map's
|
|
76
87
|
# operation list (and a sibling feature's operations, which are not this one's)
|
|
77
88
|
# reaches the spec, model, plan, UI plan, source-map and fidelity steps. The spec step
|
|
@@ -87,7 +98,7 @@ description: The JIT per-feature step of brownfield development — reverse-engi
|
|
|
87
98
|
# 1.3.0 — reverse design scaffolds from the DEFAULT `design-template` (onboard's code-derived
|
|
88
99
|
# override) instead of the never-shipped `brownfield-design-template`, unifying the
|
|
89
100
|
# whole brownfield pipeline on one code-derived template.
|
|
90
|
-
version: 2.
|
|
101
|
+
version: 2.3.0
|
|
91
102
|
agent_agnostic: true
|
|
92
103
|
dialect: aw-spec.orchestrator/v1
|
|
93
104
|
|
|
@@ -128,8 +139,12 @@ vars:
|
|
|
128
139
|
be_declared: "false"
|
|
129
140
|
fe_no_surface: "false" # a frontend lane is declared, this slice owns none of its surface, and a backend lane carries it
|
|
130
141
|
feature_lanes: "" # what record_lanes wrote — empty when the feature follows every declared lane
|
|
142
|
+
xref_edge_count: "0" # xref_record runs only where the slice has a service half
|
|
131
143
|
fe_root: ""
|
|
132
144
|
ui_lane: "false" # THIS slice reverses a UI half: a frontend lane is declared AND the slice owns UI files
|
|
145
|
+
svc_lane: "false" # THIS slice reverses a SERVICE half: a backend lane is declared AND the slice has service files
|
|
146
|
+
be_no_surface: "false" # a backend lane is declared, this slice has no service code, and a frontend lane carries it
|
|
147
|
+
model_files: "" # what the model is reversed from: the service files, or the UI files on a slice with none
|
|
133
148
|
ui_count: "0" # how many UI files the concept attributes to this aggregate (aw-inventory ui_source_files)
|
|
134
149
|
ui_source_files: ""
|
|
135
150
|
frontend_plan: ""
|
|
@@ -424,14 +439,6 @@ workflow:
|
|
|
424
439
|
outputs:
|
|
425
440
|
- { name: domain_model, from: "$.domain_model" }
|
|
426
441
|
|
|
427
|
-
- id: scaffold_plan
|
|
428
|
-
action: execute_script
|
|
429
|
-
script: { ref: actions }
|
|
430
|
-
subcommand: plan-scaffold
|
|
431
|
-
args: ["--feature-dir", "{{feature_dir}}", "--lane", "backend"] # `design-template` = onboard's code-derived override (greenfield base if onboard has not run)
|
|
432
|
-
outputs:
|
|
433
|
-
- { name: backend_plan, from: "$.plan_doc" }
|
|
434
|
-
|
|
435
442
|
- id: version_init
|
|
436
443
|
action: execute_script
|
|
437
444
|
script: { ref: actions }
|
|
@@ -502,6 +509,69 @@ workflow:
|
|
|
502
509
|
outputs:
|
|
503
510
|
- { name: feature_lanes, from: "$.lanes" }
|
|
504
511
|
|
|
512
|
+
# 2d. DOES THIS SLICE HAVE A SERVICE HALF? The twin of 2b: a backend lane must be DECLARED
|
|
513
|
+
# and this aggregate must have service files (the concept's source_files). A
|
|
514
|
+
# frontend-only project declares no backend lane, and its concepts list every file as
|
|
515
|
+
# ui_source_files, so a backend plan there would describe code that does not exist and
|
|
516
|
+
# the fidelity gate would score it as fabricated. Three ordered sets, as in 2b.
|
|
517
|
+
- id: svc_lane_off
|
|
518
|
+
action: set
|
|
519
|
+
vars: { svc_lane: "false" }
|
|
520
|
+
- id: svc_lane_on
|
|
521
|
+
action: set
|
|
522
|
+
condition: "{{be_declared}}"
|
|
523
|
+
vars: { svc_lane: "true" }
|
|
524
|
+
- id: svc_lane_no_code
|
|
525
|
+
action: set
|
|
526
|
+
condition: "{{source_count}} == 0"
|
|
527
|
+
vars: { svc_lane: "false" }
|
|
528
|
+
|
|
529
|
+
# 2e. RECORD A UI-ONLY SLICE in a repo that declares both lanes — the twin of 2c, so plan
|
|
530
|
+
# and the analyze gates do not demand a backend plan for a slice with no service code.
|
|
531
|
+
- id: be_no_surface_off
|
|
532
|
+
action: set
|
|
533
|
+
vars: { be_no_surface: "false" }
|
|
534
|
+
- id: be_no_surface_declared
|
|
535
|
+
action: set
|
|
536
|
+
condition: "{{be_declared}}"
|
|
537
|
+
vars: { be_no_surface: "true" }
|
|
538
|
+
- id: be_no_surface_has_code
|
|
539
|
+
action: set
|
|
540
|
+
condition: "{{source_count}} != 0"
|
|
541
|
+
vars: { be_no_surface: "false" }
|
|
542
|
+
- id: be_no_surface_no_frontend
|
|
543
|
+
action: set
|
|
544
|
+
condition: "{{ui_lane}} != true"
|
|
545
|
+
vars: { be_no_surface: "false" }
|
|
546
|
+
- id: record_lanes_frontend
|
|
547
|
+
condition: "{{be_no_surface}} == true"
|
|
548
|
+
action: execute_script
|
|
549
|
+
script: { ref: actions }
|
|
550
|
+
subcommand: feature-lanes
|
|
551
|
+
args: ["--feature-dir", "{{feature_dir}}", "--set", "frontend"]
|
|
552
|
+
outputs:
|
|
553
|
+
- { name: feature_lanes, from: "$.lanes" }
|
|
554
|
+
|
|
555
|
+
# What the domain model is reversed FROM. The service files where the slice has them; a
|
|
556
|
+
# slice with none keeps its domain in the UI code — the types, states and rules a
|
|
557
|
+
# frontend-only app holds — so the model is read from those.
|
|
558
|
+
- id: model_files_service
|
|
559
|
+
action: set
|
|
560
|
+
vars: { model_files: "{{source_files}}" }
|
|
561
|
+
- id: model_files_ui
|
|
562
|
+
condition: "{{svc_lane}} != true"
|
|
563
|
+
action: set
|
|
564
|
+
vars: { model_files: "{{ui_source_files}}" }
|
|
565
|
+
|
|
566
|
+
- id: scaffold_plan
|
|
567
|
+
condition: "{{svc_lane}} == true"
|
|
568
|
+
action: execute_script
|
|
569
|
+
script: { ref: actions }
|
|
570
|
+
subcommand: plan-scaffold
|
|
571
|
+
args: ["--feature-dir", "{{feature_dir}}", "--lane", "backend"] # `design-template` = onboard's code-derived override (greenfield base if onboard has not run)
|
|
572
|
+
outputs:
|
|
573
|
+
- { name: backend_plan, from: "$.plan_doc" }
|
|
574
|
+
|
|
505
575
|
# Scaffold the LANE's plan from ITS template pair (--lane frontend resolves
|
|
506
576
|
# ui-design-template -> frontend-plan.md), the twin of scaffold_plan above. onboard
|
|
507
577
|
# GENERATED that override from this repo's own UI source, so the reversed frontend plan
|
|
@@ -734,7 +804,8 @@ workflow:
|
|
|
734
804
|
- Add `origin: reverse-engineered` near the identity block. Do NOT write the Context
|
|
735
805
|
version or the Change History version column — scripts stamp those.
|
|
736
806
|
|
|
737
|
-
Source files to read (the slice's code
|
|
807
|
+
Source files to read (the slice's code — its UI files where it has no service code,
|
|
808
|
+
the one place a frontend-only app keeps its types, states and rules): {{model_files}}
|
|
738
809
|
|
|
739
810
|
# ═══════════ REVERSE BACKEND PLAN — from the SPEC + MODEL + CODE, AS-BUILT ═══
|
|
740
811
|
# Anchored to the reversed spec's ids (§4 Traceability → forward-compatible),
|
|
@@ -744,6 +815,7 @@ workflow:
|
|
|
744
815
|
# backend-plan.md IS onboard's code-derived `design-template` override — its §2
|
|
745
816
|
# component sections follow the code's own folders/modules, not a fixed layering.
|
|
746
817
|
- id: ctx_reverse_plan
|
|
818
|
+
condition: "{{svc_lane}} == true"
|
|
747
819
|
action: build_context
|
|
748
820
|
context:
|
|
749
821
|
- { path: "{{backend_plan}}" } # the scaffolded code-derived template = structure + embedded guidance
|
|
@@ -754,6 +826,7 @@ workflow:
|
|
|
754
826
|
- { path: "{{bundle_dir}}/architecture/overview.md", optional: true } # the as-built architecture (inventory) — cite, don't re-derive
|
|
755
827
|
- { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain
|
|
756
828
|
- id: reverse_plan
|
|
829
|
+
condition: "{{svc_lane}} == true"
|
|
757
830
|
action: agent_author
|
|
758
831
|
include_standards: true # the technical/architecture sticky set (reference only — the CODE wins)
|
|
759
832
|
tools: Read # grounded in the CODE, which arrives as {{source_files}} paths
|
|
@@ -798,6 +871,21 @@ workflow:
|
|
|
798
871
|
the EXTERNAL INTEGRATIONS table (heading contains "Integration") realising the
|
|
799
872
|
spec's IC-. Realise CN- (structural → §1.2 invariants/events; retention/audit →
|
|
800
873
|
§2 persistence / §3 cross-cutting), DC- (§3 + projection surface).
|
|
874
|
+
- EVERY ENDPOINT THIS FEATURE PUBLISHES IS AN ENDPOINT BLOCK, in the §2 axis that
|
|
875
|
+
publishes it: the heading `#### `VERB /route` — <purpose> · Serves <US/FR ids>`, then
|
|
876
|
+
**Operation id**, Auth, Request, Success, the Errors table and Maps to, exactly as
|
|
877
|
+
the template's endpoint block — never a row in a table of endpoints. The contract
|
|
878
|
+
scripts read this form: the API draft is written from the headings, and the request
|
|
879
|
+
ledger between the lanes reads the operation ids. The **Operation id** is the id the
|
|
880
|
+
code publishes the endpoint under, spelled exactly as the code spells it; the
|
|
881
|
+
boundary map records each operation's published id where the code declares one
|
|
882
|
+
("published as …" above). Where the code publishes none, write `—` and record a §5
|
|
883
|
+
DA-n saying the endpoint has no published operation id — never invent one: the
|
|
884
|
+
service's exported API document would not carry it, so nothing could bind to it.
|
|
885
|
+
- After the endpoint blocks, a `#### Requests from the frontend lane` heading with
|
|
886
|
+
`None` under it: a frontend reversed from the same code binds only to what the
|
|
887
|
+
service already serves, so no request is open yet. Later plans answer requests
|
|
888
|
+
there.
|
|
801
889
|
- AS-BUILT DEBT, not architecture drift. §6 records ONLY debt the CODE ITSELF
|
|
802
890
|
evidences — stubs/no-ops, dead or duplicate code, torn seams (shared table/
|
|
803
891
|
transaction), TODOs, hard-coded values. The code being organised differently
|
|
@@ -835,7 +923,7 @@ workflow:
|
|
|
835
923
|
context:
|
|
836
924
|
- { path: "{{frontend_plan}}" } # the scaffolded code-derived lane template = structure + embedded guidance
|
|
837
925
|
- { path: "{{spec_file}}" } # the reversed spec — UX- + the user stories this surface serves
|
|
838
|
-
- { path: "{{backend_plan}}" }
|
|
926
|
+
- { path: "{{backend_plan}}", optional: true } # the backend plan — the contracts the UI consumes, never re-specified here; absent with no service lane
|
|
839
927
|
- { path: "{{domain_model}}", optional: true } # the ubiquitous language labels and states must use — READ, never written
|
|
840
928
|
- { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's UX-* base, reversed by onboard from this same UI
|
|
841
929
|
- { path: "{{bundle_dir}}/architecture/frontend.md", optional: true } # the as-built UI architecture + the SHARED surface no slice owns
|
|
@@ -863,9 +951,17 @@ workflow:
|
|
|
863
951
|
- STAY IN THE LANE. Realise the spec's Experience Standards (UX-) and the
|
|
864
952
|
interaction its User Stories imply. The backend plan (in context) supplies
|
|
865
953
|
the CONTRACTS this surface consumes — cite them; do NOT restate, re-derive or
|
|
866
|
-
contradict them, and introduce no server-side behaviour here.
|
|
954
|
+
contradict them, and introduce no server-side behaviour here. With no backend plan
|
|
955
|
+
in context the service is outside this repo: bind against the routes the UI code
|
|
956
|
+
calls and the spec's IC-, as an external contract. Where the domain
|
|
867
957
|
model is in context, use its UBIQUITOUS LANGUAGE exactly in labels, states and
|
|
868
958
|
messages, and cite BR-nnn rather than restating a rule.
|
|
959
|
+
- CONTRACT BINDING CITES OPERATION IDS. Each row of the Contract Binding table names,
|
|
960
|
+
in its Operation id column, the **Operation id** backend-plan.md gives the endpoint
|
|
961
|
+
the UI code calls — `—` where the backend plan records none. The id is how
|
|
962
|
+
implement finds the endpoint, and how a later change that alters or removes it is
|
|
963
|
+
reported to this lane. The "Contract deltas needed" table reads `None`: the UI calls
|
|
964
|
+
only what the service already serves.
|
|
869
965
|
- STRUCTURE FOLLOWS THE UI CODE. Fill each section from the lane's real shape —
|
|
870
966
|
its actual routes and screens, its own composition axes (however it groups
|
|
871
967
|
components), where client state and server data-fetching really live, its
|
|
@@ -890,7 +986,7 @@ workflow:
|
|
|
890
986
|
context:
|
|
891
987
|
- { path: .appweaver/templates/source-map-template.md } # the sidecar structure
|
|
892
988
|
- { path: "{{spec_file}}" }
|
|
893
|
-
- { path: "{{backend_plan}}" }
|
|
989
|
+
- { path: "{{backend_plan}}", optional: true } # the service lane's plan, where this slice has one
|
|
894
990
|
- { path: "{{frontend_plan}}", optional: true } # the lane's design, where this slice has one
|
|
895
991
|
- { path: "{{concept_path}}" } # both file lists to map
|
|
896
992
|
- id: source_map
|
|
@@ -945,7 +1041,7 @@ workflow:
|
|
|
945
1041
|
context:
|
|
946
1042
|
- "{{spec_file}}"
|
|
947
1043
|
- "{{domain_model}}"
|
|
948
|
-
- "{{backend_plan}}"
|
|
1044
|
+
- { path: "{{backend_plan}}", optional: true } # the service lane's plan, where this slice has one
|
|
949
1045
|
- { path: "{{frontend_plan}}", optional: true } # the lane's plan, where this slice has one
|
|
950
1046
|
- "{{feature_dir}}/source-map.md"
|
|
951
1047
|
- "{{concept_path}}"
|
|
@@ -1006,7 +1102,12 @@ workflow:
|
|
|
1006
1102
|
condition: "{{report_needed}} == true"
|
|
1007
1103
|
action: agent_author
|
|
1008
1104
|
include_standards: false
|
|
1009
|
-
context:
|
|
1105
|
+
context:
|
|
1106
|
+
- "{{spec_file}}"
|
|
1107
|
+
- "{{domain_model}}"
|
|
1108
|
+
- { path: "{{backend_plan}}", optional: true }
|
|
1109
|
+
- { path: "{{frontend_plan}}", optional: true }
|
|
1110
|
+
- "{{feature_dir}}/source-map.md"
|
|
1010
1111
|
output_target: { type: file, path: "{{feature_dir}}/adopt-review-report.md" }
|
|
1011
1112
|
task: |
|
|
1012
1113
|
Write an Adopt Review report (the target) for the feature '{{feature_name}}' (acting on {{aggregates_csv}}) —
|
|
@@ -1014,8 +1115,10 @@ workflow:
|
|
|
1014
1115
|
- "# Adopt Review — {{feature_name}}" + today's date + "origin: reverse-engineered".
|
|
1015
1116
|
- "verdict:" line — "BLOCKING ✗ · <n>" if blocking gaps exist, else "REVIEW ○ · <m> item(s)".
|
|
1016
1117
|
- "## Blocking gaps" (if any): table | # | kind | artefact | detail | — MUST be
|
|
1017
|
-
resolved before the slice is trustworthy
|
|
1018
|
-
|
|
1118
|
+
resolved before the slice is trustworthy, one of two ways: re-adopt — remove
|
|
1119
|
+
{{feature_dir}} and run /appweaver adopt {{feature_name}} again, which reverses
|
|
1120
|
+
the documents afresh from the code, so edits made to them by hand are not kept —
|
|
1121
|
+
or correct the documents by hand and keep them.
|
|
1019
1122
|
Blocking gaps: {{blocking_gaps}}
|
|
1020
1123
|
- "## Review items" (close out of band): table | # | kind | where | confirm |.
|
|
1021
1124
|
These are inferred-vs-certain calls, as-built quirks, and missing-invariant
|
|
@@ -1034,7 +1137,7 @@ workflow:
|
|
|
1034
1137
|
action: verify_context
|
|
1035
1138
|
require_true: fidelity_ok
|
|
1036
1139
|
on_false: stop
|
|
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.
|
|
1140
|
+
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. Resolve it one of two ways. RE-ADOPT: remove {{feature_dir}} and run /appweaver adopt {{feature_name}} again; the documents are reversed afresh from the code, so edits made to them first are not kept. Removing the folder leaves what this run added to {{domain_model}} (git diff shows it), and a re-adopt extends it, so remove that first. CORRECT BY HAND: re-read the source and refine spec.md, {{domain_model}}, the lane plans this slice has (backend: {{backend_plan}}; frontend: {{frontend_plan}}) and source-map.md — map the unmapped code, remove what is fabricated. This run stopped before it completed the OKF concept {{aggregate_primary}} and recorded cross-slice edges, so on this path both stay as onboard left them. 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."
|
|
1038
1141
|
|
|
1039
1142
|
# ═══════════ OKF CONCEPT — COMPLETE the shallow scan concept (deep reverse) ═══
|
|
1040
1143
|
# onboard wrote this aggregate's OKF concept SHALLOW (a discovery stub — enough to pick the
|
|
@@ -1051,7 +1154,8 @@ workflow:
|
|
|
1051
1154
|
- { path: "{{concept_path}}" } # the shallow onboard concept to complete
|
|
1052
1155
|
- { path: "{{spec_file}}" } # the deep reversed spec (entities, US, BR/CN/DC/IC)
|
|
1053
1156
|
- { path: "{{domain_model}}" } # the deep reversed model (entities, boundary, invariants)
|
|
1054
|
-
- { path: "{{backend_plan}}" }
|
|
1157
|
+
- { path: "{{backend_plan}}", optional: true } # the deep reversed service plan (components, ports, integrations), where the slice has one
|
|
1158
|
+
- { path: "{{frontend_plan}}", optional: true } # the deep reversed UI plan, where the slice has one
|
|
1055
1159
|
- { path: "{{feature_dir}}/source-map.md" } # the source anchor (files, seam, quirks)
|
|
1056
1160
|
- { path: .appweaver/templates/okf-concept-template.md } # OKF v0.1 format authority
|
|
1057
1161
|
- id: complete_concept
|
|
@@ -1109,6 +1213,7 @@ workflow:
|
|
|
1109
1213
|
# reconcile-on-write helper as forward implement, stamped origin reverse-engineered,
|
|
1110
1214
|
# additive to the inventory bundle. Runs only after fidelity passes.
|
|
1111
1215
|
- id: xref_record
|
|
1216
|
+
condition: "{{svc_lane}} == true" # edges are read off the service plan, as forward implement records them
|
|
1112
1217
|
action: execute_script
|
|
1113
1218
|
script: { ref: xref }
|
|
1114
1219
|
subcommand: record
|
|
@@ -1331,9 +1436,10 @@ outputs:
|
|
|
1331
1436
|
frontend_plan: "{{frontend_plan}}" # empty where this slice has no UI half
|
|
1332
1437
|
domain_model: "{{domain_model}}" # the CONTEXT's, shared — this run ADDED an aggregate section to it
|
|
1333
1438
|
bounded_context: "{{context}}"
|
|
1439
|
+
svc_lane: "{{svc_lane}}"
|
|
1334
1440
|
ui_lane: "{{ui_lane}}"
|
|
1335
1441
|
ui_count: "{{ui_count}}"
|
|
1336
|
-
feature_lanes: "{{feature_lanes}}" # empty = every declared lane; [backend] = a service-only slice in a repo with a frontend
|
|
1442
|
+
feature_lanes: "{{feature_lanes}}" # empty = every declared lane; [backend] = a service-only slice in a repo with a frontend; [frontend] = a UI-only slice in a repo with a service
|
|
1337
1443
|
operations: "{{operations_csv}}" # this feature's operations, from the boundary map
|
|
1338
1444
|
baseline_commit: "{{baseline_commit}}"
|
|
1339
1445
|
source_map: "{{feature_dir}}/source-map.md"
|
|
@@ -1352,4 +1458,4 @@ outputs:
|
|
|
1352
1458
|
composed_file: "{{composed_file}}"
|
|
1353
1459
|
technical_reconcile_ok: "{{technical_ok}}"
|
|
1354
1460
|
domain_reconcile_ok: "{{domain_ok}}"
|
|
1355
|
-
next: "Adopted {{feature_name}} (spec.md
|
|
1461
|
+
next: "Adopted {{feature_name}} (spec.md, source-map.md and the lane plans this slice has — backend: {{backend_plan}}; frontend: {{frontend_plan}} — 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. SERVICE HALF: svc_lane={{svc_lane}} — false where the repo declares no backend lane (a frontend-only project: no backend plan, no cross-slice edges, and the model was read from the UI code) or this slice has no service code; in the second case, where a frontend lane carries it, the feature records lanes=[frontend] the same way. 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/{{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."
|