aw-spec-cli 0.1.32 → 0.1.34
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/README.md +76 -81
- package/assets/.appweaver/DESIGN-FORMAT.md +56 -17
- package/assets/.appweaver/STACK-PROFILES.md +95 -60
- package/assets/.appweaver/orchestrator/LAUNCHER.md +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +173 -32
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +29 -1
- package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +7 -4
- package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +49 -60
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +58 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +26 -8
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +20 -16
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +30 -6
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +9 -7
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +5 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +341 -36
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +254 -43
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +248 -68
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +251 -20
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +316 -10
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +6 -47
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +62 -16
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +132 -9
- package/assets/.appweaver/orchestrator/workflows/aw-spec-fix-test-coverage.orch.yaml +965 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +222 -169
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +28 -12
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +453 -116
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +7 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +83 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +45 -2
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +324 -74
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +275 -44
- package/assets/.appweaver/scripts/python/aw-foundation.py +16 -13
- package/assets/.appweaver/scripts/python/aw-gaps.py +11 -3
- package/assets/.appweaver/scripts/python/aw-model-guard.py +295 -0
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +412 -3
- package/assets/.appweaver/scripts/python/aw-plangate.py +23 -16
- package/assets/.appweaver/scripts/python/aw-stack.py +1794 -265
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +0 -2
- package/assets/.appweaver/stack-profiles/java-spring.json +0 -2
- package/assets/.appweaver/templates/api-plan-template.md +107 -0
- package/assets/.appweaver/templates/delta-api-plan-template.md +87 -0
- package/assets/.appweaver/templates/delta-design-template.md +25 -23
- package/assets/.appweaver/templates/delta-spec-template.md +3 -3
- package/assets/.appweaver/templates/design-template.md +20 -42
- package/assets/.appweaver/templates/domain-model-template.md +3 -0
- package/assets/.appweaver/templates/ui-delta-design-template.md +11 -8
- package/assets/.appweaver/templates/ui-design-template.md +4 -4
- package/assets/foundations/foundation.spec.template.txt +25 -18
- package/assets/guardrails/README.md +61 -25
- package/assets/guardrails/methodology/ux/accessibility-standards.md +38 -0
- package/assets/guardrails/methodology/ux/content-design.md +34 -0
- package/assets/guardrails/methodology/ux/interaction-states.md +45 -0
- package/assets/guardrails/methodology/ux/journey-mapping.md +41 -0
- package/assets/guardrails/project-specific/domain/domain-requirements.md +25 -38
- package/assets/guardrails/project-specific/domain-rule-bindings.md +33 -69
- package/assets/guardrails/project-specific/frontend/ui-requirements.md +2 -2
- package/assets/guardrails/project-specific/service/technical-requirements.md +2 -2
- package/assets/guardrails/project-specific/tech-rule-bindings.md +16 -13
- package/assets/guardrails/sample-healthcare/domain/domain-requirements.md +58 -0
- package/assets/guardrails/sample-healthcare/domain-rule-bindings.md +138 -0
- package/assets/guardrails/sample-healthcare/frontend/ui-requirements.md +43 -0
- package/assets/guardrails/sample-healthcare/service/technical-requirements.md +40 -0
- package/assets/guardrails/sample-healthcare/tech-rule-bindings.md +303 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +216 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/config.js +1 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/foundation.d.ts +36 -0
- package/dist/utils/foundation.d.ts.map +1 -0
- package/dist/utils/foundation.js +109 -0
- package/dist/utils/foundation.js.map +1 -0
- package/dist/utils/paths.js +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# aw-spec-adopt (orchestrator) — the JUST-IN-TIME per-feature step of Brownfield DEV
|
|
3
3
|
#
|
|
4
4
|
# Develop an EXISTING feature by first reverse-engineering ONE aggregate / vertical
|
|
5
|
-
# slice from its code into a living spec.md, the bounded context's domain-model.md
|
|
6
|
-
# aligned lane plans (+ a source-map.md sidecar),
|
|
5
|
+
# slice from its code into a living spec.md, the bounded context's domain-model.md, the
|
|
6
|
+
# API the slice publishes (api-plan.md) and aligned lane plans (+ a source-map.md sidecar),
|
|
7
|
+
# THEN reconciling this feature's evidence into the project governance that
|
|
7
8
|
# /appweaver onboard established. Adopt is the inverse of implement: implement reads the
|
|
8
9
|
# plans + the model and generates code; adopt reads code and generates the spec, the model
|
|
9
10
|
# and the plans — equally a reasoning step, stamped `origin: reverse-engineered` with
|
|
@@ -28,9 +29,12 @@
|
|
|
28
29
|
# project default). So adopt = reverse an EXISTING feature; the forward pipeline = a
|
|
29
30
|
# new one. Both then evolve through the CR delta loop.
|
|
30
31
|
#
|
|
31
|
-
# The reverse output is a NORMAL feature: spec.md, domain-model.md
|
|
32
|
-
# frontend-plan.md are the very documents the CR delta loop + forward
|
|
33
|
-
# unchanged — under those names, so nothing downstream needs a brownfield
|
|
32
|
+
# The reverse output is a NORMAL feature: spec.md, domain-model.md, api-plan.md and
|
|
33
|
+
# backend-plan.md / frontend-plan.md are the very documents the CR delta loop + forward
|
|
34
|
+
# pipeline consume unchanged — under those names, so nothing downstream needs a brownfield
|
|
35
|
+
# special case. The API plan is written from the kit's api-plan-template, which onboard
|
|
36
|
+
# does not override: its endpoint block form is the one the contract scripts read in every
|
|
37
|
+
# project, so only the plans around it follow the code's own structure.
|
|
34
38
|
# plan-scaffold's backend/frontend lanes also MIGRATE a legacy design.md / design.ui.md left
|
|
35
39
|
# by an adopt that ran before the rename, so an already-adopted repo is carried forward. All brownfield provenance
|
|
36
40
|
# lives in `origin:` frontmatter + the source-map sidecar. Adopt inits
|
|
@@ -71,7 +75,24 @@
|
|
|
71
75
|
# =============================================================================
|
|
72
76
|
|
|
73
77
|
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
|
|
78
|
+
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 — api-plan.md (the endpoints the slice publishes, with the operation ids the code gives them) and 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), record its cross-slice edges and draw its API plan into api-draft.json, the API the frontend lane binds to. 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.
|
|
79
|
+
# 2.5.0 — the endpoints the slice publishes are reversed into the feature's own api-plan.md,
|
|
80
|
+
# before the backend plan, which realises them and points to it; the frontend plan
|
|
81
|
+
# takes its operation ids from the API plan and never reads the backend plan; the
|
|
82
|
+
# source-map and the fidelity check cover the API plan too; once fidelity passes,
|
|
83
|
+
# the API plan is drawn into api-draft.json, so the slice is in the frontend's
|
|
84
|
+
# combined document from the start.
|
|
85
|
+
# 2.3.0 — the SERVICE half is reversed only where the slice has one (svc_lane: a backend lane
|
|
86
|
+
# is declared and the slice has service files), the twin of ui_lane. A frontend-only
|
|
87
|
+
# project gets no backend plan and no cross-slice edges, and its model is read from
|
|
88
|
+
# the UI files; a UI-only slice in a repo with both lanes records lanes=[frontend].
|
|
89
|
+
# A failed fidelity check names the two ways to resolve it — re-adopt, or correct
|
|
90
|
+
# the documents by hand — and what each leaves behind.
|
|
91
|
+
# 2.2.0 — the reversed plans take part in the API ledger between the lanes: the backend plan
|
|
92
|
+
# writes each endpoint the slice publishes as an endpoint block (`#### `VERB /route``
|
|
93
|
+
# + **Operation id**, the id the code publishes — `—` and a DA-n where it publishes
|
|
94
|
+
# none, never an invented one) with a "Requests from the frontend lane" table, and the
|
|
95
|
+
# frontend plan's Contract Binding rows cite those ids.
|
|
75
96
|
# 2.1.0 — every reverse step is scoped to the FEATURE, not its aggregate: the boundary map's
|
|
76
97
|
# operation list (and a sibling feature's operations, which are not this one's)
|
|
77
98
|
# reaches the spec, model, plan, UI plan, source-map and fidelity steps. The spec step
|
|
@@ -87,7 +108,7 @@ description: The JIT per-feature step of brownfield development — reverse-engi
|
|
|
87
108
|
# 1.3.0 — reverse design scaffolds from the DEFAULT `design-template` (onboard's code-derived
|
|
88
109
|
# override) instead of the never-shipped `brownfield-design-template`, unifying the
|
|
89
110
|
# whole brownfield pipeline on one code-derived template.
|
|
90
|
-
version: 2.
|
|
111
|
+
version: 2.5.0
|
|
91
112
|
agent_agnostic: true
|
|
92
113
|
dialect: aw-spec.orchestrator/v1
|
|
93
114
|
|
|
@@ -128,8 +149,12 @@ vars:
|
|
|
128
149
|
be_declared: "false"
|
|
129
150
|
fe_no_surface: "false" # a frontend lane is declared, this slice owns none of its surface, and a backend lane carries it
|
|
130
151
|
feature_lanes: "" # what record_lanes wrote — empty when the feature follows every declared lane
|
|
152
|
+
xref_edge_count: "0" # xref_record runs only where the slice has a service half
|
|
131
153
|
fe_root: ""
|
|
132
154
|
ui_lane: "false" # THIS slice reverses a UI half: a frontend lane is declared AND the slice owns UI files
|
|
155
|
+
svc_lane: "false" # THIS slice reverses a SERVICE half: a backend lane is declared AND the slice has service files
|
|
156
|
+
be_no_surface: "false" # a backend lane is declared, this slice has no service code, and a frontend lane carries it
|
|
157
|
+
model_files: "" # what the model is reversed from: the service files, or the UI files on a slice with none
|
|
133
158
|
ui_count: "0" # how many UI files the concept attributes to this aggregate (aw-inventory ui_source_files)
|
|
134
159
|
ui_source_files: ""
|
|
135
160
|
frontend_plan: ""
|
|
@@ -149,6 +174,21 @@ vars:
|
|
|
149
174
|
# left by an earlier adopt, so a repo that was adopted before the rename is carried
|
|
150
175
|
# forward rather than left with two spellings of one master.
|
|
151
176
|
backend_plan: ""
|
|
177
|
+
# THE API THE SLICE PUBLISHES — its own document, reversed before the backend plan, which
|
|
178
|
+
# realises each endpoint. `api_blocked` is the scaffold refusing because a backend plan
|
|
179
|
+
# already in the folder carries the endpoints: that plan keeps them for this run, as
|
|
180
|
+
# before API plans existed, and the frontend plan reads it in their place (`api_read`).
|
|
181
|
+
api_plan: ""
|
|
182
|
+
api_blocked: "false"
|
|
183
|
+
api_reverse: "false"
|
|
184
|
+
api_read: ""
|
|
185
|
+
draft_path: "" # api-draft.json, drawn after fidelity passes (contract_draft)
|
|
186
|
+
draft_plan: ""
|
|
187
|
+
draft_ops: "0"
|
|
188
|
+
draft_unread: "0"
|
|
189
|
+
draft_unread_text: ""
|
|
190
|
+
draft_clean: "true"
|
|
191
|
+
be_api_rule: "THE ENDPOINTS ARE IN api-plan.md (in context), reversed just before this document from the same code: the API contract the frontend lane binds to. Do NOT restate them here — the §2 axis that publishes them carries the one line pointing to api-plan.md, and where the template has an endpoint block or a requests table there (a template generated before API plans existed), that line goes in their place. REALISE every endpoint instead: each names under **Maps to** the code unit that serves it, and that unit is this document's element, under the same name, with request and response shapes that match the endpoint's. An endpoint with no element here is unrealised. The traceability spine cites an endpoint as `VERB /route`."
|
|
152
192
|
# THE BOUNDED CONTEXT'S DOMAIN MODEL — shared by every feature in the context, and the
|
|
153
193
|
# authority on the aggregate root, its entities and the TEXT of every BR-nnn. Reversed
|
|
154
194
|
# here in the same run as the spec, exactly as greenfield `specify` authors the two
|
|
@@ -157,6 +197,10 @@ vars:
|
|
|
157
197
|
context_dir: ""
|
|
158
198
|
domain_model: ""
|
|
159
199
|
domain_model_exists: "false"
|
|
200
|
+
model_guard_ok: "false" # model-guard-check's verdict on the reverse_model rewrite. DEFAULTS
|
|
201
|
+
# TO REFUSE: only the check's own `ok` lets the run past the gate
|
|
202
|
+
model_guard_losses: "" # what the rejected rewrite lost, one clause per item
|
|
203
|
+
model_guard_rejected: "" # where the rejected rewrite was kept
|
|
160
204
|
spec_exists: "false"
|
|
161
205
|
fidelity_ok: "false"
|
|
162
206
|
rulemap_exists: "false" # governance-present signal (onboard ran) — bound from resolve-domain
|
|
@@ -424,14 +468,6 @@ workflow:
|
|
|
424
468
|
outputs:
|
|
425
469
|
- { name: domain_model, from: "$.domain_model" }
|
|
426
470
|
|
|
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
471
|
- id: version_init
|
|
436
472
|
action: execute_script
|
|
437
473
|
script: { ref: actions }
|
|
@@ -502,6 +538,100 @@ workflow:
|
|
|
502
538
|
outputs:
|
|
503
539
|
- { name: feature_lanes, from: "$.lanes" }
|
|
504
540
|
|
|
541
|
+
# 2d. DOES THIS SLICE HAVE A SERVICE HALF? The twin of 2b: a backend lane must be DECLARED
|
|
542
|
+
# and this aggregate must have service files (the concept's source_files). A
|
|
543
|
+
# frontend-only project declares no backend lane, and its concepts list every file as
|
|
544
|
+
# ui_source_files, so a backend plan there would describe code that does not exist and
|
|
545
|
+
# the fidelity gate would score it as fabricated. Three ordered sets, as in 2b.
|
|
546
|
+
- id: svc_lane_off
|
|
547
|
+
action: set
|
|
548
|
+
vars: { svc_lane: "false" }
|
|
549
|
+
- id: svc_lane_on
|
|
550
|
+
action: set
|
|
551
|
+
condition: "{{be_declared}}"
|
|
552
|
+
vars: { svc_lane: "true" }
|
|
553
|
+
- id: svc_lane_no_code
|
|
554
|
+
action: set
|
|
555
|
+
condition: "{{source_count}} == 0"
|
|
556
|
+
vars: { svc_lane: "false" }
|
|
557
|
+
|
|
558
|
+
# 2e. RECORD A UI-ONLY SLICE in a repo that declares both lanes — the twin of 2c, so plan
|
|
559
|
+
# and the analyze gates do not demand a backend plan for a slice with no service code.
|
|
560
|
+
- id: be_no_surface_off
|
|
561
|
+
action: set
|
|
562
|
+
vars: { be_no_surface: "false" }
|
|
563
|
+
- id: be_no_surface_declared
|
|
564
|
+
action: set
|
|
565
|
+
condition: "{{be_declared}}"
|
|
566
|
+
vars: { be_no_surface: "true" }
|
|
567
|
+
- id: be_no_surface_has_code
|
|
568
|
+
action: set
|
|
569
|
+
condition: "{{source_count}} != 0"
|
|
570
|
+
vars: { be_no_surface: "false" }
|
|
571
|
+
- id: be_no_surface_no_frontend
|
|
572
|
+
action: set
|
|
573
|
+
condition: "{{ui_lane}} != true"
|
|
574
|
+
vars: { be_no_surface: "false" }
|
|
575
|
+
- id: record_lanes_frontend
|
|
576
|
+
condition: "{{be_no_surface}} == true"
|
|
577
|
+
action: execute_script
|
|
578
|
+
script: { ref: actions }
|
|
579
|
+
subcommand: feature-lanes
|
|
580
|
+
args: ["--feature-dir", "{{feature_dir}}", "--set", "frontend"]
|
|
581
|
+
outputs:
|
|
582
|
+
- { name: feature_lanes, from: "$.lanes" }
|
|
583
|
+
|
|
584
|
+
# What the domain model is reversed FROM. The service files where the slice has them; a
|
|
585
|
+
# slice with none keeps its domain in the UI code — the types, states and rules a
|
|
586
|
+
# frontend-only app holds — so the model is read from those.
|
|
587
|
+
- id: model_files_service
|
|
588
|
+
action: set
|
|
589
|
+
vars: { model_files: "{{source_files}}" }
|
|
590
|
+
- id: model_files_ui
|
|
591
|
+
condition: "{{svc_lane}} != true"
|
|
592
|
+
action: set
|
|
593
|
+
vars: { model_files: "{{ui_source_files}}" }
|
|
594
|
+
|
|
595
|
+
# THE API PLAN FIRST, where the slice has a service half — scaffolded from the kit's
|
|
596
|
+
# api-plan-template before the backend plan, as `plan` does it: the scaffold refuses
|
|
597
|
+
# (`blocked`) while a backend plan in the folder carries the API, and a backend plan
|
|
598
|
+
# scaffolded from a template generated before API plans existed carries an empty requests
|
|
599
|
+
# table, so it must not be there yet.
|
|
600
|
+
- id: scaffold_api_plan
|
|
601
|
+
condition: "{{svc_lane}} == true"
|
|
602
|
+
action: execute_script
|
|
603
|
+
script: { ref: actions }
|
|
604
|
+
subcommand: plan-scaffold
|
|
605
|
+
args: ["--feature-dir", "{{feature_dir}}", "--lane", "api"]
|
|
606
|
+
outputs:
|
|
607
|
+
- { name: api_plan, from: "$.plan_doc" }
|
|
608
|
+
- { name: api_blocked, from: "$.blocked" }
|
|
609
|
+
|
|
610
|
+
- id: scaffold_plan
|
|
611
|
+
condition: "{{svc_lane}} == true"
|
|
612
|
+
action: execute_script
|
|
613
|
+
script: { ref: actions }
|
|
614
|
+
subcommand: plan-scaffold
|
|
615
|
+
args: ["--feature-dir", "{{feature_dir}}", "--lane", "backend"] # `design-template` = onboard's code-derived override (greenfield base if onboard has not run)
|
|
616
|
+
outputs:
|
|
617
|
+
- { name: backend_plan, from: "$.plan_doc" }
|
|
618
|
+
|
|
619
|
+
# WHERE THE API IS REVERSED, and what the frontend plan reads of the service: the API plan
|
|
620
|
+
# just scaffolded — or, where the scaffold refused, the backend plan, which then keeps its
|
|
621
|
+
# endpoint blocks for this run. Nothing where the slice has no service half.
|
|
622
|
+
- id: api_reverse_arm
|
|
623
|
+
condition: "{{svc_lane}} == true"
|
|
624
|
+
action: set
|
|
625
|
+
vars: { api_reverse: "true", api_read: "{{api_plan}}" }
|
|
626
|
+
- id: api_reverse_blocked
|
|
627
|
+
condition: "{{api_blocked}} == true"
|
|
628
|
+
action: set
|
|
629
|
+
vars:
|
|
630
|
+
api_plan: ""
|
|
631
|
+
api_reverse: "false"
|
|
632
|
+
api_read: "{{backend_plan}}"
|
|
633
|
+
be_api_rule: "EVERY ENDPOINT THIS FEATURE PUBLISHES IS AN ENDPOINT BLOCK IN THIS DOCUMENT — a backend plan already in the folder carries the API, so it stays here for this run — in the §2 axis that publishes it: the heading `#### `VERB /route` — <purpose> · Serves <US/FR ids>`, then **Operation id**, Auth, Request, Success, the Errors table and Maps to, exactly as the template's endpoint block — never a row in a table of endpoints. The contract scripts read this form: the API draft is written from the headings, and the request ledger between the lanes reads the operation ids. The **Operation id** is the id the code publishes the endpoint under, spelled exactly as the code spells it; the boundary map records each operation's published id where the code declares one ('published as …' above). Where the code publishes none, write `—` and record a §5 DA-n saying the endpoint has no published operation id — never invent one: the service's as-built API document would not carry it, so the frontend would bind to an id the service does not answer, and analyze-slice would report it. After the endpoint blocks, a `#### Requests from the frontend lane` heading with `None` under it: a frontend reversed from the same code binds only to what the service already serves, so no request is open yet."
|
|
634
|
+
|
|
505
635
|
# Scaffold the LANE's plan from ITS template pair (--lane frontend resolves
|
|
506
636
|
# ui-design-template -> frontend-plan.md), the twin of scaffold_plan above. onboard
|
|
507
637
|
# GENERATED that override from this repo's own UI source, so the reversed frontend plan
|
|
@@ -671,6 +801,18 @@ workflow:
|
|
|
671
801
|
- { path: "{{spec_file}}" } # the reversed spec — the BR-nnn ids this model must define
|
|
672
802
|
- { path: "{{concept_path}}" } # source_files + seam
|
|
673
803
|
- { path: .appweaver/memory/constitution.domain.md, optional: true }
|
|
804
|
+
|
|
805
|
+
# THE REWRITE IS CHECKED. The engine writes the agent's reply over the WHOLE model, which
|
|
806
|
+
# on a context's second slice holds the sections, BR-/XBR- rules and ledger rows the
|
|
807
|
+
# features adopted or specified before it rely on. The snapshot copies it aside; the check
|
|
808
|
+
# after the step compares the two (python/aw-model-guard.py) and, on a loss, puts the
|
|
809
|
+
# model back and keeps the rewrite as domain-model.rejected.md.
|
|
810
|
+
- id: reverse_model_snapshot
|
|
811
|
+
action: execute_script
|
|
812
|
+
script: { ref: actions }
|
|
813
|
+
subcommand: model-guard-snapshot
|
|
814
|
+
args: ["--model", "{{domain_model}}"]
|
|
815
|
+
|
|
674
816
|
- id: reverse_model
|
|
675
817
|
action: agent_author
|
|
676
818
|
include_standards: false # the technical/arch sticky set is for the plans, never the model
|
|
@@ -714,6 +856,12 @@ workflow:
|
|
|
714
856
|
already there — other specs cite their ids, and a renumbered BR breaks every
|
|
715
857
|
citation silently. Add `{{feature_name}}` to the `Serving specs` line of any
|
|
716
858
|
existing aggregate this slice also acts on.
|
|
859
|
+
- A SCRIPT COMPARES YOUR FILE WITH THE MODEL AS IT WAS, and puts the old one back if
|
|
860
|
+
any of these is gone: an `## Aggregate:` section under its name; a BR-n or XBR-n,
|
|
861
|
+
in the same section, with the same rule text, defined once; a name on an
|
|
862
|
+
aggregate's `Serving specs` line; a Ubiquitous Language term; a context the
|
|
863
|
+
Context Map names; a Change History row; the Context version. Add to any of them
|
|
864
|
+
freely; never remove, rename, renumber, move or reword one.
|
|
717
865
|
- MODEL WHAT THE CODE EXHIBITS, NOT WHAT GOOD DESIGN WOULD WANT. Classify entities
|
|
718
866
|
vs value objects by whether the code gives the thing an IDENTITY; draw the
|
|
719
867
|
boundary where the code actually draws it (what it loads and saves together).
|
|
@@ -734,6 +882,96 @@ workflow:
|
|
|
734
882
|
- Add `origin: reverse-engineered` near the identity block. Do NOT write the Context
|
|
735
883
|
version or the Change History version column — scripts stamp those.
|
|
736
884
|
|
|
885
|
+
Source files to read (the slice's code — its UI files where it has no service code,
|
|
886
|
+
the one place a frontend-only app keeps its types, states and rules): {{model_files}}
|
|
887
|
+
|
|
888
|
+
- id: reverse_model_check
|
|
889
|
+
action: execute_script
|
|
890
|
+
script: { ref: actions }
|
|
891
|
+
subcommand: model-guard-check
|
|
892
|
+
args: ["--model", "{{domain_model}}"]
|
|
893
|
+
outputs:
|
|
894
|
+
- { name: model_guard_ok, from: "$.ok" }
|
|
895
|
+
- { name: model_guard_losses, from: "$.losses_text" }
|
|
896
|
+
- { name: model_guard_rejected, from: "$.rejected" }
|
|
897
|
+
|
|
898
|
+
# On a stop the model is already restored; this slice's spec.md is written, so the
|
|
899
|
+
# already-adopted guard would refuse a plain re-run — the feature folder goes first.
|
|
900
|
+
- id: reverse_model_guard
|
|
901
|
+
action: verify_context
|
|
902
|
+
require_true: model_guard_ok
|
|
903
|
+
on_false: stop
|
|
904
|
+
on_false_message: "The domain model lost content other features rely on, so {{domain_model}} was put back as it was before this run. Lost: {{model_guard_losses}}. The rejected rewrite is kept at {{model_guard_rejected}} for inspection. This slice's spec.md was already written, so remove {{feature_dir}} and run /appweaver adopt {{feature_name}} again."
|
|
905
|
+
|
|
906
|
+
# ═══════════ REVERSE API PLAN — the endpoints the slice publishes, AS-BUILT ═══
|
|
907
|
+
# The contract between the lanes, reversed before the backend plan exactly as `plan`
|
|
908
|
+
# authors it first: the backend plan then realises each endpoint, and the frontend plan
|
|
909
|
+
# binds to the operation ids written here. One template in every project — the kit's
|
|
910
|
+
# api-plan-template, whose endpoint block form is what the contract scripts read — so the
|
|
911
|
+
# code decides which endpoints there are and what they carry, never the document's shape.
|
|
912
|
+
- id: ctx_reverse_api
|
|
913
|
+
condition: "{{api_reverse}} == true"
|
|
914
|
+
action: build_context
|
|
915
|
+
context:
|
|
916
|
+
- { path: "{{api_plan}}" } # the scaffolded template = the fixed endpoint block form
|
|
917
|
+
- { path: "{{spec_file}}" } # the reversed spec = the US/FR ids each endpoint serves
|
|
918
|
+
- { path: "{{domain_model}}" } # the ubiquitous language, and the BR-nnn an error answers to — READ, never written
|
|
919
|
+
- { path: "{{concept_path}}" } # source_files + seam
|
|
920
|
+
- { path: .appweaver/DESIGN-FORMAT.md } # the HLD contract, and The API plan in it
|
|
921
|
+
- { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — the protocol the service publishes
|
|
922
|
+
- id: reverse_api
|
|
923
|
+
condition: "{{api_reverse}} == true"
|
|
924
|
+
action: agent_author
|
|
925
|
+
include_standards: true # the technical sticky set (reference only — the CODE wins)
|
|
926
|
+
tools: Read # grounded in the CODE, which arrives as {{source_files}} paths
|
|
927
|
+
output_target: { type: file, path: "{{api_plan}}" }
|
|
928
|
+
task: |
|
|
929
|
+
Reverse-engineer the API PLAN of the feature '{{feature_name}}' (the target
|
|
930
|
+
api-plan.md in context — the kit's API plan template) FROM ITS CODE: every endpoint
|
|
931
|
+
the slice publishes for this feature. It is the API contract between the lanes — the
|
|
932
|
+
frontend plan binds to its operation ids, a script drafts it into api-draft.json,
|
|
933
|
+
analyze-slice checks the built service against it — and it is written BEFORE the
|
|
934
|
+
backend plan, which then realises each endpoint. READ the source files listed below;
|
|
935
|
+
the code is the SOURCE OF TRUTH.
|
|
936
|
+
Rules:
|
|
937
|
+
- DESCRIBE THE API AS-BUILT, quirks included: each endpoint's route, verb,
|
|
938
|
+
parameters, request and response fields, status codes and error bodies exactly as
|
|
939
|
+
the code serves them, with the field names as they appear on the wire. Never tidy
|
|
940
|
+
a route, rename a field or add a status the code does not return.
|
|
941
|
+
- THE FEATURE'S SCOPE. {{operations_note}} An endpoint that serves only a sibling
|
|
942
|
+
feature's operation is that feature's and is left out, even though its file is
|
|
943
|
+
listed; a read both features call is this feature's too.
|
|
944
|
+
- KEEP THE TEMPLATE'S FORM EXACTLY: one block per endpoint under "## Endpoints" — the
|
|
945
|
+
heading `### `VERB /route` — purpose · Serves …` citing the reversed spec's US/FR
|
|
946
|
+
ids, then **Operation id**, **Auth**, **Request**, **Success**, the **Errors** table
|
|
947
|
+
and **Maps to**. The contract scripts read exactly this form; an endpoint written
|
|
948
|
+
as a table row or in prose reaches neither api-draft.json nor the frontend.
|
|
949
|
+
- EVERY SHAPE IS `{ field: type }` in conceptual types (string, int, decimal, bool,
|
|
950
|
+
enum(A|B), list<T>, id<T>, money, date, datetime), never the code's own types. A
|
|
951
|
+
shape in prose reaches the frontend's client untyped.
|
|
952
|
+
- THE OPERATION ID is the id the code publishes the endpoint under, spelled exactly as
|
|
953
|
+
the code spells it; the boundary map records each operation's published id where
|
|
954
|
+
the code declares one (the scope line above). Where the code publishes none, write
|
|
955
|
+
`—` and say so in the block — never invent one: the service's as-built API document
|
|
956
|
+
would not carry it, so the frontend would bind to an id the service does not
|
|
957
|
+
answer, and analyze-slice would report it.
|
|
958
|
+
- **Maps to** names the code's own unit that serves the endpoint — its handler,
|
|
959
|
+
use-case or service operation — by the name the code gives it; the backend plan,
|
|
960
|
+
reversed next from the same code, realises it under that name.
|
|
961
|
+
- THE ERROR CONTRACT is what the code returns: one row per failure it actually
|
|
962
|
+
produces, with its status, when it is returned and its body shape. An error the
|
|
963
|
+
code never produces is not written.
|
|
964
|
+
- Under "## Requests from the frontend lane" write `None`: a frontend reversed from
|
|
965
|
+
the same code binds only to what the service already serves, so no request is
|
|
966
|
+
open yet.
|
|
967
|
+
- A SERVICE THAT PUBLISHES ANOTHER PROTOCOL (messages, remote procedures) states each
|
|
968
|
+
operation in the same block form, headed by the code's own name for it.
|
|
969
|
+
- Write `None` under "## Endpoints" when the slice publishes no endpoint.
|
|
970
|
+
- TECHNOLOGY-AGNOSTIC: protocol level only — no controller code, framework
|
|
971
|
+
attributes, serializer settings or language types.
|
|
972
|
+
- Add `origin: reverse-engineered` near the Version field. This is v1.0: write the
|
|
973
|
+
first Change History row.
|
|
974
|
+
|
|
737
975
|
Source files to read (the slice's code): {{source_files}}
|
|
738
976
|
|
|
739
977
|
# ═══════════ REVERSE BACKEND PLAN — from the SPEC + MODEL + CODE, AS-BUILT ═══
|
|
@@ -744,16 +982,19 @@ workflow:
|
|
|
744
982
|
# backend-plan.md IS onboard's code-derived `design-template` override — its §2
|
|
745
983
|
# component sections follow the code's own folders/modules, not a fixed layering.
|
|
746
984
|
- id: ctx_reverse_plan
|
|
985
|
+
condition: "{{svc_lane}} == true"
|
|
747
986
|
action: build_context
|
|
748
987
|
context:
|
|
749
988
|
- { path: "{{backend_plan}}" } # the scaffolded code-derived template = structure + embedded guidance
|
|
750
989
|
- { path: "{{spec_file}}" } # the reversed spec = WHAT the slice must do
|
|
990
|
+
- { path: "{{api_plan}}", optional: true } # the reversed API = the endpoints this plan realises — absent where the scaffold refused
|
|
751
991
|
- { path: "{{domain_model}}" } # the reversed model = what the domain IS — READ, never written here
|
|
752
992
|
- { path: "{{concept_path}}" } # source_files + seam (structure hints)
|
|
753
993
|
- { path: .appweaver/DESIGN-FORMAT.md } # the HLD contract (see its Brownfield variant note)
|
|
754
994
|
- { path: "{{bundle_dir}}/architecture/overview.md", optional: true } # the as-built architecture (inventory) — cite, don't re-derive
|
|
755
995
|
- { path: .appweaver/memory/constitution.technical.md, optional: true } # technology base ONLY — no domain
|
|
756
996
|
- id: reverse_plan
|
|
997
|
+
condition: "{{svc_lane}} == true"
|
|
757
998
|
action: agent_author
|
|
758
999
|
include_standards: true # the technical/architecture sticky set (reference only — the CODE wins)
|
|
759
1000
|
tools: Read # grounded in the CODE, which arrives as {{source_files}} paths
|
|
@@ -798,6 +1039,7 @@ workflow:
|
|
|
798
1039
|
the EXTERNAL INTEGRATIONS table (heading contains "Integration") realising the
|
|
799
1040
|
spec's IC-. Realise CN- (structural → §1.2 invariants/events; retention/audit →
|
|
800
1041
|
§2 persistence / §3 cross-cutting), DC- (§3 + projection surface).
|
|
1042
|
+
- {{be_api_rule}}
|
|
801
1043
|
- AS-BUILT DEBT, not architecture drift. §6 records ONLY debt the CODE ITSELF
|
|
802
1044
|
evidences — stubs/no-ops, dead or duplicate code, torn seams (shared table/
|
|
803
1045
|
transaction), TODOs, hard-coded values. The code being organised differently
|
|
@@ -816,8 +1058,9 @@ workflow:
|
|
|
816
1058
|
# ═══════ REVERSE UI DESIGN — the LANE's half, from the UI code, AS-BUILT ═══
|
|
817
1059
|
# Runs only where this slice HAS a surface (ui_lane). The service design above is
|
|
818
1060
|
# NOT its parent: backend-plan.md realises the spec's FR/BR/CN-, frontend-plan.md
|
|
819
|
-
# realises the spec's UX- and the user stories' interaction, and it CONSUMES the
|
|
820
|
-
# plan's contracts rather than restating them.
|
|
1061
|
+
# realises the spec's UX- and the user stories' interaction, and it CONSUMES the API
|
|
1062
|
+
# plan's contracts rather than restating them. The spec and the API plan are in context
|
|
1063
|
+
# for exactly that reason; the backend plan, the service's internals, is not.
|
|
821
1064
|
# THE FRONTEND'S RULES REPLACE THE BACKEND'S before the lane's plan is authored — the
|
|
822
1065
|
# swap the forward `plan` makes at the same boundary. Sticky standards only accumulate,
|
|
823
1066
|
# so without it the UI plan was authored under the service rule set and none of the
|
|
@@ -835,7 +1078,7 @@ workflow:
|
|
|
835
1078
|
context:
|
|
836
1079
|
- { path: "{{frontend_plan}}" } # the scaffolded code-derived lane template = structure + embedded guidance
|
|
837
1080
|
- { path: "{{spec_file}}" } # the reversed spec — UX- + the user stories this surface serves
|
|
838
|
-
- { path: "{{
|
|
1081
|
+
- { path: "{{api_read}}", optional: true } # the API plan — the contracts the UI consumes, never re-specified here; absent with no service lane
|
|
839
1082
|
- { path: "{{domain_model}}", optional: true } # the ubiquitous language labels and states must use — READ, never written
|
|
840
1083
|
- { path: .appweaver/memory/constitution.ui.md, optional: true } # the lane's UX-* base, reversed by onboard from this same UI
|
|
841
1084
|
- { path: "{{bundle_dir}}/architecture/frontend.md", optional: true } # the as-built UI architecture + the SHARED surface no slice owns
|
|
@@ -861,11 +1104,20 @@ workflow:
|
|
|
861
1104
|
operation — typically that feature's own UI folder — is that feature's. Where this
|
|
862
1105
|
feature's screen hosts it, cite it as the sibling's; never plan it here.
|
|
863
1106
|
- STAY IN THE LANE. Realise the spec's Experience Standards (UX-) and the
|
|
864
|
-
interaction its User Stories imply. The
|
|
865
|
-
|
|
866
|
-
|
|
1107
|
+
interaction its User Stories imply. The API plan (in context — api-plan.md, or the
|
|
1108
|
+
backend plan where this feature's endpoints stayed in it) supplies the CONTRACTS
|
|
1109
|
+
this surface consumes — cite them; do NOT restate, re-derive or contradict them,
|
|
1110
|
+
and introduce no server-side behaviour here. With no API plan in context the
|
|
1111
|
+
service is outside this repo: bind against the routes the UI code calls and the
|
|
1112
|
+
spec's IC-, as an external contract. Where the domain
|
|
867
1113
|
model is in context, use its UBIQUITOUS LANGUAGE exactly in labels, states and
|
|
868
1114
|
messages, and cite BR-nnn rather than restating a rule.
|
|
1115
|
+
- CONTRACT BINDING CITES OPERATION IDS. Each row of the Contract Binding table names,
|
|
1116
|
+
in its Operation id column, the **Operation id** the API plan gives the endpoint
|
|
1117
|
+
the UI code calls — `—` where the API plan records none. The id is how
|
|
1118
|
+
implement finds the endpoint, and how a later change that alters or removes it is
|
|
1119
|
+
reported to this lane. The "Contract deltas needed" table reads `None`: the UI calls
|
|
1120
|
+
only what the service already serves.
|
|
869
1121
|
- STRUCTURE FOLLOWS THE UI CODE. Fill each section from the lane's real shape —
|
|
870
1122
|
its actual routes and screens, its own composition axes (however it groups
|
|
871
1123
|
components), where client state and server data-fetching really live, its
|
|
@@ -890,7 +1142,8 @@ workflow:
|
|
|
890
1142
|
context:
|
|
891
1143
|
- { path: .appweaver/templates/source-map-template.md } # the sidecar structure
|
|
892
1144
|
- { path: "{{spec_file}}" }
|
|
893
|
-
- { path: "{{backend_plan}}" }
|
|
1145
|
+
- { path: "{{backend_plan}}", optional: true } # the service lane's plan, where this slice has one
|
|
1146
|
+
- { path: "{{api_plan}}", optional: true } # the endpoints the slice publishes, where it has a service half
|
|
894
1147
|
- { path: "{{frontend_plan}}", optional: true } # the lane's design, where this slice has one
|
|
895
1148
|
- { path: "{{concept_path}}" } # both file lists to map
|
|
896
1149
|
- id: source_map
|
|
@@ -904,7 +1157,8 @@ workflow:
|
|
|
904
1157
|
spine — it keeps spec.md / backend-plan.md clean while recording provenance here.
|
|
905
1158
|
- One row per legacy artefact in the bundle concept's `source_files` (read
|
|
906
1159
|
them under {{module_dir}}): map each to the spec ids (FR/BR/US/CN/DC/IC)
|
|
907
|
-
and the backend-plan element (§/name) that now describe it,
|
|
1160
|
+
and the backend-plan element (§/name) that now describe it — and, for a file
|
|
1161
|
+
that publishes an endpoint, its `VERB /route` block in api-plan.md — with CONFIDENCE
|
|
908
1162
|
(certain | inferred). EVERY source file MUST appear — the Fidelity gate
|
|
909
1163
|
checks full coverage both ways.
|
|
910
1164
|
- THE CONCEPT IS THE AGGREGATE'S ('{{aggregate_primary}}') and lists every feature's
|
|
@@ -945,7 +1199,8 @@ workflow:
|
|
|
945
1199
|
context:
|
|
946
1200
|
- "{{spec_file}}"
|
|
947
1201
|
- "{{domain_model}}"
|
|
948
|
-
- "{{backend_plan}}"
|
|
1202
|
+
- { path: "{{backend_plan}}", optional: true } # the service lane's plan, where this slice has one
|
|
1203
|
+
- { path: "{{api_plan}}", optional: true } # the endpoints it publishes, where this slice has a service half
|
|
949
1204
|
- { path: "{{frontend_plan}}", optional: true } # the lane's plan, where this slice has one
|
|
950
1205
|
- "{{feature_dir}}/source-map.md"
|
|
951
1206
|
- "{{concept_path}}"
|
|
@@ -960,7 +1215,10 @@ workflow:
|
|
|
960
1215
|
{{module_dir}}). Where frontend-plan.md is in context, it is judged the same way
|
|
961
1216
|
against the slice's UI files ({{ui_source_files}}) — a screen, state store or
|
|
962
1217
|
route it describes that the UI code does not have is a fabricated-plan, and a
|
|
963
|
-
UI file with no home in the source-map is unmapped-code.
|
|
1218
|
+
UI file with no home in the source-map is unmapped-code. Where api-plan.md is in
|
|
1219
|
+
context, it is judged against the service code the same way: an endpoint the code
|
|
1220
|
+
does not serve, a field or status it does not have, or an operation id the code does
|
|
1221
|
+
not publish is a fabricated-plan. Classify findings:
|
|
964
1222
|
- BLOCKING (structural — set ok=false if any):
|
|
965
1223
|
(1) unmapped-code: a source file with NO home in the source-map — one recorded
|
|
966
1224
|
there as `sibling: <Feature>` has a home;
|
|
@@ -1006,7 +1264,13 @@ workflow:
|
|
|
1006
1264
|
condition: "{{report_needed}} == true"
|
|
1007
1265
|
action: agent_author
|
|
1008
1266
|
include_standards: false
|
|
1009
|
-
context:
|
|
1267
|
+
context:
|
|
1268
|
+
- "{{spec_file}}"
|
|
1269
|
+
- "{{domain_model}}"
|
|
1270
|
+
- { path: "{{backend_plan}}", optional: true }
|
|
1271
|
+
- { path: "{{api_plan}}", optional: true }
|
|
1272
|
+
- { path: "{{frontend_plan}}", optional: true }
|
|
1273
|
+
- "{{feature_dir}}/source-map.md"
|
|
1010
1274
|
output_target: { type: file, path: "{{feature_dir}}/adopt-review-report.md" }
|
|
1011
1275
|
task: |
|
|
1012
1276
|
Write an Adopt Review report (the target) for the feature '{{feature_name}}' (acting on {{aggregates_csv}}) —
|
|
@@ -1014,8 +1278,10 @@ workflow:
|
|
|
1014
1278
|
- "# Adopt Review — {{feature_name}}" + today's date + "origin: reverse-engineered".
|
|
1015
1279
|
- "verdict:" line — "BLOCKING ✗ · <n>" if blocking gaps exist, else "REVIEW ○ · <m> item(s)".
|
|
1016
1280
|
- "## Blocking gaps" (if any): table | # | kind | artefact | detail | — MUST be
|
|
1017
|
-
resolved before the slice is trustworthy
|
|
1018
|
-
|
|
1281
|
+
resolved before the slice is trustworthy, one of two ways: re-adopt — remove
|
|
1282
|
+
{{feature_dir}} and run /appweaver adopt {{feature_name}} again, which reverses
|
|
1283
|
+
the documents afresh from the code, so edits made to them by hand are not kept —
|
|
1284
|
+
or correct the documents by hand and keep them.
|
|
1019
1285
|
Blocking gaps: {{blocking_gaps}}
|
|
1020
1286
|
- "## Review items" (close out of band): table | # | kind | where | confirm |.
|
|
1021
1287
|
These are inferred-vs-certain calls, as-built quirks, and missing-invariant
|
|
@@ -1034,7 +1300,7 @@ workflow:
|
|
|
1034
1300
|
action: verify_context
|
|
1035
1301
|
require_true: fidelity_ok
|
|
1036
1302
|
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.
|
|
1303
|
+
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 API plan and lane plans this slice has (API: {{api_plan}}; 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}}, recorded cross-slice edges and drew the API draft, so on this path the concept and the edges stay as onboard left them and the slice has no api-draft.json until /appweaver analyze-plan draws it. 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
1304
|
|
|
1039
1305
|
# ═══════════ OKF CONCEPT — COMPLETE the shallow scan concept (deep reverse) ═══
|
|
1040
1306
|
# onboard wrote this aggregate's OKF concept SHALLOW (a discovery stub — enough to pick the
|
|
@@ -1051,7 +1317,9 @@ workflow:
|
|
|
1051
1317
|
- { path: "{{concept_path}}" } # the shallow onboard concept to complete
|
|
1052
1318
|
- { path: "{{spec_file}}" } # the deep reversed spec (entities, US, BR/CN/DC/IC)
|
|
1053
1319
|
- { path: "{{domain_model}}" } # the deep reversed model (entities, boundary, invariants)
|
|
1054
|
-
- { path: "{{backend_plan}}" }
|
|
1320
|
+
- { path: "{{backend_plan}}", optional: true } # the deep reversed service plan (components, ports, integrations), where the slice has one
|
|
1321
|
+
- { path: "{{api_plan}}", optional: true } # the deep reversed API (the endpoints it publishes), where the slice has a service half
|
|
1322
|
+
- { path: "{{frontend_plan}}", optional: true } # the deep reversed UI plan, where the slice has one
|
|
1055
1323
|
- { path: "{{feature_dir}}/source-map.md" } # the source anchor (files, seam, quirks)
|
|
1056
1324
|
- { path: .appweaver/templates/okf-concept-template.md } # OKF v0.1 format authority
|
|
1057
1325
|
- id: complete_concept
|
|
@@ -1063,8 +1331,8 @@ workflow:
|
|
|
1063
1331
|
COMPLETE the OKF concept of the aggregate '{{aggregate_primary}}' at {{concept_path}} —
|
|
1064
1332
|
the aggregate this run adopted the feature '{{feature_name}}' over. onboard wrote it
|
|
1065
1333
|
SHALLOW (a discovery stub from the whole-repo scan). The deep reverse has now
|
|
1066
|
-
produced a TRUSTWORTHY spec.md + domain-model.md +
|
|
1067
|
-
context); deepen the
|
|
1334
|
+
produced a TRUSTWORTHY spec.md + domain-model.md + api-plan.md + backend-plan.md +
|
|
1335
|
+
source-map.md (in context, each the slice has); deepen the
|
|
1068
1336
|
concept to match and LINK those artifacts. Follow the OKF concept template in context.
|
|
1069
1337
|
THE CONCEPT IS THE AGGREGATE'S, NOT THE FEATURE'S. Where its `features:` names more
|
|
1070
1338
|
than one feature, another feature's adopt may already have deepened it, and nothing
|
|
@@ -1076,8 +1344,9 @@ workflow:
|
|
|
1076
1344
|
the authority on them; correct any drift the shallow scan got wrong (the deep docs
|
|
1077
1345
|
win). A sibling capability's commands and endpoints stay where they are.
|
|
1078
1346
|
- LINK the deep artifacts: add bundle links to {{feature_dir}}/spec.md,
|
|
1079
|
-
backend-plan.md, source-map.md and the context's domain-model.md
|
|
1080
|
-
points at the deep truth. Keep every link another
|
|
1347
|
+
backend-plan.md, api-plan.md, source-map.md and the context's domain-model.md (each
|
|
1348
|
+
the slice has) so the concept points at the deep truth. Keep every link another
|
|
1349
|
+
feature's adopt added.
|
|
1081
1350
|
- UNION `source_files` with the source-map's mapped files (grouped by the code's OWN
|
|
1082
1351
|
folders, per OKF) — never drop a file already listed, which may be a sibling
|
|
1083
1352
|
capability's. The boundary map was drawn at the baseline commit, whose time is
|
|
@@ -1109,6 +1378,7 @@ workflow:
|
|
|
1109
1378
|
# reconcile-on-write helper as forward implement, stamped origin reverse-engineered,
|
|
1110
1379
|
# additive to the inventory bundle. Runs only after fidelity passes.
|
|
1111
1380
|
- id: xref_record
|
|
1381
|
+
condition: "{{svc_lane}} == true" # edges are read off the service plan, as forward implement records them
|
|
1112
1382
|
action: execute_script
|
|
1113
1383
|
script: { ref: xref }
|
|
1114
1384
|
subcommand: record
|
|
@@ -1117,6 +1387,37 @@ workflow:
|
|
|
1117
1387
|
- { name: xref_wrote, from: "$.wrote" }
|
|
1118
1388
|
- { name: xref_edge_count, from: "$.edge_count" }
|
|
1119
1389
|
|
|
1390
|
+
# 3c. THE API DRAFT — api-draft.json beside the plans, drawn by script from the API plan just
|
|
1391
|
+
# reversed (the backend plan, where its endpoints stayed in it): the API contract the
|
|
1392
|
+
# frontend lane's combined document reads and analyze-slice checks the built service
|
|
1393
|
+
# against. Drawn here, after fidelity passed, so the adopted slice is in the combined
|
|
1394
|
+
# document from the start rather than only once plan or analyze-plan next runs for it.
|
|
1395
|
+
# Advisory — a plan the script cannot read leaves the run going; a shape it cannot type
|
|
1396
|
+
# is reported, because the frontend's client gets that data untyped.
|
|
1397
|
+
- id: contract_draft
|
|
1398
|
+
condition: "{{svc_lane}} == true"
|
|
1399
|
+
action: execute_script
|
|
1400
|
+
on_error: continue
|
|
1401
|
+
script: { ref: actions }
|
|
1402
|
+
subcommand: contract-draft
|
|
1403
|
+
args: ["--feature-dir", "{{feature_dir}}"]
|
|
1404
|
+
outputs:
|
|
1405
|
+
- { name: draft_path, from: "$.path" }
|
|
1406
|
+
- { name: draft_plan, from: "$.plan" }
|
|
1407
|
+
- { name: draft_ops, from: "$.operations" }
|
|
1408
|
+
- { name: draft_unread, from: "$.unread_count" }
|
|
1409
|
+
- { name: draft_unread_text, from: "$.unread_text" }
|
|
1410
|
+
- id: draft_unread_flag
|
|
1411
|
+
condition: "{{draft_unread}} != 0"
|
|
1412
|
+
action: set
|
|
1413
|
+
vars: { draft_clean: "false" }
|
|
1414
|
+
- id: warn_draft_unread
|
|
1415
|
+
condition: "{{svc_lane}} == true"
|
|
1416
|
+
action: verify_context
|
|
1417
|
+
require_true: draft_clean
|
|
1418
|
+
on_false: continue
|
|
1419
|
+
on_false_message: "API DRAFT — {{draft_unread}} endpoint shape(s) in {{draft_plan}} are not written as `{ field: type }`, so the frontend's client gets that data untyped: {{draft_unread_text}}. Write each request and response the way the API plan template's endpoint block does, with the fields the code puts on the wire, then run /appweaver analyze-plan, which redraws the draft."
|
|
1420
|
+
|
|
1120
1421
|
# ═══════════ GOVERNANCE RECONCILE — upgrade onboard's PROVISIONAL scaffold ═══
|
|
1121
1422
|
# ⏸ HELD 2026-07-29: every step in this block is gated on `reconcile_governance`
|
|
1122
1423
|
# (default "false"), so adopt currently does NOT touch the constitution halves
|
|
@@ -1327,13 +1628,17 @@ workflow:
|
|
|
1327
1628
|
outputs:
|
|
1328
1629
|
feature_dir: "{{feature_dir}}"
|
|
1329
1630
|
spec_file: "{{spec_file}}"
|
|
1631
|
+
api_plan: "{{api_plan}}" # empty where this slice has no service half, or its backend plan keeps the endpoints
|
|
1632
|
+
api_draft: "{{draft_path}}" # api-draft.json drawn from it — empty where the slice has no service half or publishes no endpoint
|
|
1633
|
+
api_draft_operations: "{{draft_ops}}"
|
|
1330
1634
|
backend_plan: "{{backend_plan}}"
|
|
1331
1635
|
frontend_plan: "{{frontend_plan}}" # empty where this slice has no UI half
|
|
1332
1636
|
domain_model: "{{domain_model}}" # the CONTEXT's, shared — this run ADDED an aggregate section to it
|
|
1333
1637
|
bounded_context: "{{context}}"
|
|
1638
|
+
svc_lane: "{{svc_lane}}"
|
|
1334
1639
|
ui_lane: "{{ui_lane}}"
|
|
1335
1640
|
ui_count: "{{ui_count}}"
|
|
1336
|
-
feature_lanes: "{{feature_lanes}}" # empty = every declared lane; [backend] = a service-only slice in a repo with a frontend
|
|
1641
|
+
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
1642
|
operations: "{{operations_csv}}" # this feature's operations, from the boundary map
|
|
1338
1643
|
baseline_commit: "{{baseline_commit}}"
|
|
1339
1644
|
source_map: "{{feature_dir}}/source-map.md"
|
|
@@ -1352,4 +1657,4 @@ outputs:
|
|
|
1352
1657
|
composed_file: "{{composed_file}}"
|
|
1353
1658
|
technical_reconcile_ok: "{{technical_ok}}"
|
|
1354
1659
|
domain_reconcile_ok: "{{domain_ok}}"
|
|
1355
|
-
next: "Adopted {{feature_name}} (spec.md
|
|
1660
|
+
next: "Adopted {{feature_name}} (spec.md, source-map.md, the API plan and the lane plans this slice has — API: {{api_plan}}; 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). API DRAFT: {{draft_ops}} operation(s) drawn from the API plan into api-draft.json beside it, the API the frontend lane binds to and analyze-slice checks the service against (0 where the slice has no service half). 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."
|