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
|
@@ -3,14 +3,22 @@
|
|
|
3
3
|
#
|
|
4
4
|
# Included by /appweaver plan on its DELTA branch (a master plan already exists).
|
|
5
5
|
# It authors, inside the change's own directory beside its delta-spec:
|
|
6
|
+
# • changes/<cr-id>/delta-api-plan.md when a backend lane is declared — FIRST
|
|
6
7
|
# • changes/<cr-id>/delta-backend-plan.md when a backend lane is declared
|
|
7
8
|
# • changes/<cr-id>/delta-frontend-plan.md when a frontend lane is declared
|
|
8
|
-
# and touches
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# plan
|
|
12
|
-
# these plans with the change at the end. No version
|
|
13
|
-
# /appweaver implement).
|
|
9
|
+
# and touches NO master. The change stays OPEN; `implement` generates the code from these
|
|
10
|
+
# documents (it asks which change, and takes the delta plans of the answer), finalize-delta
|
|
11
|
+
# merges the change's SPEC and MODEL halves and, by script, its delta API plan into
|
|
12
|
+
# api-plan.md (never a lane plan — a master plan is derived from those and is re-derived
|
|
13
|
+
# from them), and close-delta archives these plans with the change at the end. No version
|
|
14
|
+
# bump here, no code (that is /appweaver implement).
|
|
15
|
+
#
|
|
16
|
+
# THE CHANGE'S API COMES FIRST. Its delta API plan — the endpoints it adds, changes and
|
|
17
|
+
# removes, and the frontend lane's requests it answers — is authored before its delta
|
|
18
|
+
# backend plan, which then realises each endpoint; the frontend delta plan binds to it. A
|
|
19
|
+
# change planned before delta API plans existed keeps its API in its delta backend plan:
|
|
20
|
+
# the scaffold refuses to put an empty delta API plan beside it, and the delta backend plan
|
|
21
|
+
# is re-planned with the API in it, as before.
|
|
14
22
|
#
|
|
15
23
|
# IT DOES NOT CHOOSE THE CHANGE — the caller already did. `plan` asks one question at the
|
|
16
24
|
# top of its run (which is why a re-plan of the MASTER and a delta are the same command),
|
|
@@ -46,17 +54,18 @@
|
|
|
46
54
|
# from the caller already narrowed to the developer's working lanes (.appweaver/lane.json)
|
|
47
55
|
# and to the lanes on the change's **Lanes** line, so a lane that is somebody else's, or
|
|
48
56
|
# that the change does not touch, gets no delta plan here. The other half of the change is
|
|
49
|
-
# READ: the frontend delta plan binds to
|
|
50
|
-
#
|
|
51
|
-
# are to the
|
|
57
|
+
# READ: the frontend delta plan binds to delta_api_read, the change's API as the backend
|
|
58
|
+
# developer left it (its delta API plan, or the delta backend plan of an older change) —
|
|
59
|
+
# absent for a frontend-only change, whose bindings are to the feature's API (api_read).
|
|
52
60
|
#
|
|
53
61
|
# EXPECTS caller vars: feature_dir, spec_file, backend_plan, frontend_plan, domain_model,
|
|
54
62
|
# be_existing, fe_existing, fe_lane_id, source_files, source_count, USER_INPUT,
|
|
55
|
-
#
|
|
63
|
+
# api_read, delta_api_read, req_backend_text,
|
|
56
64
|
# and — resolved by the caller from the change it chose and activated — cr_id, cr_dir,
|
|
57
65
|
# delta_spec, delta_model, wave, predecessor_files, predecessor_count, predecessor_ids,
|
|
58
66
|
# sibling_count, sibling_ids, related_docs.
|
|
59
|
-
# PRODUCES: delta_backend_plan, delta_frontend_plan, downstream_count,
|
|
67
|
+
# PRODUCES: delta_api_plan, delta_backend_plan, delta_frontend_plan, downstream_count,
|
|
68
|
+
# impact_report.
|
|
60
69
|
# =============================================================================
|
|
61
70
|
|
|
62
71
|
name: delta-plan
|
|
@@ -91,13 +100,57 @@ vars:
|
|
|
91
100
|
related_docs: []
|
|
92
101
|
delta_backend_plan: ""
|
|
93
102
|
delta_frontend_plan: ""
|
|
94
|
-
|
|
103
|
+
api_read: ""
|
|
104
|
+
delta_api_read: ""
|
|
105
|
+
req_backend_text: "none"
|
|
106
|
+
# The change's delta API plan. delta_api_go is false for a lane not worked here and for a
|
|
107
|
+
# change whose delta backend plan still carries its API (the scaffold is then `blocked`).
|
|
108
|
+
delta_api_plan: ""
|
|
109
|
+
delta_api_blocked: "false"
|
|
110
|
+
delta_api_go: "false"
|
|
111
|
+
be_delta_api_rule: "" # what the delta backend plan is told about where the change's API is — set below
|
|
95
112
|
|
|
96
113
|
workflow:
|
|
97
114
|
|
|
98
115
|
# 1. Scaffold this change's plan document for each lane whose MASTER already exists.
|
|
99
116
|
# A lane whose master was scaffolded in this same run has just been authored from
|
|
100
117
|
# the whole spec — there is nothing for a delta to add to it — so it gets none.
|
|
118
|
+
#
|
|
119
|
+
# THE CHANGE'S API FIRST, before the delta backend plan, for the reason the master's
|
|
120
|
+
# is: the scaffold refuses (`blocked`) while the change's delta backend plan still
|
|
121
|
+
# carries its API, and a delta backend plan scaffolded first from a template
|
|
122
|
+
# generated before delta API plans existed could carry the old contract table.
|
|
123
|
+
- id: api_delta_scaffold
|
|
124
|
+
condition: "{{be_existing}} == true"
|
|
125
|
+
action: execute_script
|
|
126
|
+
script: { ref: actions }
|
|
127
|
+
subcommand: delta-plan-scaffold
|
|
128
|
+
args: ["--cr-id", "{{cr_id}}", "--lane", "api", "--feature-dir", "{{feature_dir}}"]
|
|
129
|
+
outputs:
|
|
130
|
+
- { name: delta_api_plan, from: "$.delta_plan" }
|
|
131
|
+
- { name: delta_api_blocked, from: "$.blocked" }
|
|
132
|
+
|
|
133
|
+
# Where the change's API is authored, as ordered assignments: the delta API plan on a
|
|
134
|
+
# worked backend lane; its delta backend plan, as before, for a change whose delta
|
|
135
|
+
# backend plan already carries it.
|
|
136
|
+
- id: api_delta_arm
|
|
137
|
+
condition: "{{be_existing}} == true"
|
|
138
|
+
action: set
|
|
139
|
+
vars:
|
|
140
|
+
delta_api_go: "true"
|
|
141
|
+
be_delta_api_rule: "THE CHANGE'S API IS IN delta-api-plan.md (in context), authored just before this document: the endpoints it adds, changes and removes, and the requests it answers. Do NOT restate them here — the API section of this document is the one line pointing to delta-api-plan.md, and where the template has a contract table or an endpoint section (a template generated before delta API plans existed), that line goes in their place. REALISE every endpoint instead: each names under **Maps to** the use-case that serves it, which this document ADDS or MODIFIES, with request and response DTOs that match the endpoint's shapes; a removed endpoint's use-case is REMOVED unless something else still uses it."
|
|
142
|
+
- id: api_delta_in_backend_plan
|
|
143
|
+
condition: "{{delta_api_blocked}} == true"
|
|
144
|
+
action: set
|
|
145
|
+
vars:
|
|
146
|
+
delta_api_go: "false"
|
|
147
|
+
be_delta_api_rule: "THIS CHANGE WAS PLANNED BEFORE DELTA API PLANS EXISTED, so its API stays in this document. PUBLISH WHAT THE FRONTEND LANE NEEDS: fill '## Contract deltas for the frontend lane' with every addition, change or removal to this feature's published surface, one row per endpoint carrying its operation id — a changed endpoint keeps the one the API gives it, a new endpoint gets one unique across the service — and 'None' when the surface is unchanged. THEN WRITE EACH ENDPOINT IT ADDS OR CHANGES IN FULL under '## API — added and changed endpoints', as the API's own endpoint block: the `VERB /route` heading, then **Operation id**, **Request**, **Success** and the **Errors** table, every shape `{ field: type }`. A removed endpoint gets a `removed` row and no block."
|
|
148
|
+
# The frontend delta plan binds to the delta API plan once this run has one.
|
|
149
|
+
- id: delta_api_read_own
|
|
150
|
+
condition: "{{delta_api_go}} == true"
|
|
151
|
+
action: set
|
|
152
|
+
vars: { delta_api_read: "{{delta_api_plan}}" }
|
|
153
|
+
|
|
101
154
|
- id: be_delta_scaffold
|
|
102
155
|
condition: "{{be_existing}} == true"
|
|
103
156
|
action: execute_script
|
|
@@ -116,6 +169,89 @@ workflow:
|
|
|
116
169
|
outputs:
|
|
117
170
|
- { name: delta_frontend_plan, from: "$.delta_plan" }
|
|
118
171
|
|
|
172
|
+
# ═════════════════════ THE CHANGE'S API — the delta API plan ═════════════════
|
|
173
|
+
# The endpoints the change adds, changes and removes, and the requests it answers, in
|
|
174
|
+
# the API plan's own block form — which finalize-delta merges into api-plan.md block by
|
|
175
|
+
# block, so a changed endpoint is written whole. Authored before the delta backend plan,
|
|
176
|
+
# which realises each endpoint.
|
|
177
|
+
- id: ctx_api_delta
|
|
178
|
+
condition: "{{delta_api_go}} == true"
|
|
179
|
+
action: build_context
|
|
180
|
+
context_from: related_docs # the predecessors' delta-specs and every open
|
|
181
|
+
# change's model delta — the same two kinds,
|
|
182
|
+
# and the same opposite licence, as the
|
|
183
|
+
# service lane below.
|
|
184
|
+
context:
|
|
185
|
+
- "{{delta_api_plan}}" # the delta API plan template to fill
|
|
186
|
+
- "{{delta_spec}}" # the spec-side change (from specify)
|
|
187
|
+
- { path: "{{delta_model}}", optional: true }
|
|
188
|
+
- "{{spec_file}}" # the current master spec (READ ONLY)
|
|
189
|
+
- { path: "{{api_read}}", optional: true }
|
|
190
|
+
# THE FEATURE'S API AS IT STANDS (READ ONLY) —
|
|
191
|
+
# api-plan.md, or the backend plan of a feature
|
|
192
|
+
# planned before it. Planned against block by
|
|
193
|
+
# block, never edited here.
|
|
194
|
+
- { path: "{{domain_model}}", optional: true }
|
|
195
|
+
- { path: .appweaver/memory/constitution.technical.md, optional: true }
|
|
196
|
+
- { path: "{{cr_dir}}/delta-slice-analyze-report.md", optional: true }
|
|
197
|
+
- { path: "{{cr_dir}}/delta-slice-analyze-report.backend.md", optional: true }
|
|
198
|
+
# WHAT THE LAST GATE FOUND — a missing or moved
|
|
199
|
+
# endpoint tagged `fix: plan` is closed here.
|
|
200
|
+
- { path: .appweaver/DESIGN-FORMAT.md }
|
|
201
|
+
- id: author_api_delta
|
|
202
|
+
condition: "{{delta_api_go}} == true"
|
|
203
|
+
action: agent_author # plan's technical standards stay ON (default) — correct here
|
|
204
|
+
tools: Read # the feature's EXISTING source arrives as {{source_files}} paths, not inlined
|
|
205
|
+
output_target: { type: file, path: "{{delta_api_plan}}" }
|
|
206
|
+
task: |
|
|
207
|
+
Fill the DELTA API PLAN for change {{cr_id}} (wave {{wave}}) — the target document in
|
|
208
|
+
context: the endpoints this change adds to, changes in and removes from the feature's
|
|
209
|
+
API, and the frontend lane's requests it answers. It is authored before the change's
|
|
210
|
+
delta backend plan, which then realises each endpoint, and finalize-delta merges it
|
|
211
|
+
into api-plan.md by script, every block as it stands. THE DELTA ONLY — do NOT edit the
|
|
212
|
+
feature's api-plan.md or backend-plan.md.
|
|
213
|
+
- THE FEATURE'S API AS IT STANDS is in context: api-plan.md, or the API section of a
|
|
214
|
+
backend plan written before API plans existed. Plan against it, block by block.
|
|
215
|
+
- FILL "## Contract deltas for the frontend lane" with one row per endpoint the change
|
|
216
|
+
adds, changes or removes: Operation id, Contract (`VERB /route`), Change (added /
|
|
217
|
+
changed / removed), Shape, Serves. A changed endpoint keeps the operation id the API
|
|
218
|
+
gives it; a new one takes an id unique across the service. `None` when the change
|
|
219
|
+
leaves the API as it is — what a purely internal change says.
|
|
220
|
+
- THEN WRITE EACH ENDPOINT IT ADDS OR CHANGES IN FULL under "## Endpoints added or
|
|
221
|
+
changed", in the API's own block form: the `### `VERB /route`` heading, then
|
|
222
|
+
**Operation id**, **Auth**, **Request**, **Success**, the **Errors** table and
|
|
223
|
+
**Maps to**, every shape as `{ field: type }`. The merge puts this block in place of
|
|
224
|
+
the API's block, so a changed endpoint is written WHOLE — its unchanged fields too —
|
|
225
|
+
never as the difference. A removed endpoint has its row and no block. A shape in
|
|
226
|
+
prose reaches the frontend untyped.
|
|
227
|
+
- ANSWER THE REQUESTS THIS CHANGE SERVES. Still open in the frontend plan's "Contract
|
|
228
|
+
deltas needed": {{req_backend_text}}. Answer under "## Requests from the frontend
|
|
229
|
+
lane" only those this change's delta-spec gives a basis for — published under an
|
|
230
|
+
operation id, with its endpoint above, or declined with the reason. The others stay
|
|
231
|
+
open for the master. `None` when the change answers none.
|
|
232
|
+
- READ THE CODE FIRST. {{source_count}} source file(s) currently realise this
|
|
233
|
+
feature: {{source_files}}. Where the code already serves an endpoint this change
|
|
234
|
+
touches, its route, verb and operation id are as the code has them.
|
|
235
|
+
- THE LAST GATE'S REPORT, when one is in context (delta-slice-analyze-report.md from
|
|
236
|
+
a run that gated every lane, delta-slice-analyze-report.backend.md from one that
|
|
237
|
+
gated the backend alone; when both are, the one with the later date is the last
|
|
238
|
+
gate). A finding tagged `fix: plan` about an endpoint says THIS document was what was
|
|
239
|
+
wrong — re-derive the rows and blocks it names. EVIDENCE, never a requirement
|
|
240
|
+
source: the delta-spec stays the only authority on what the change must do.
|
|
241
|
+
- THE EFFECTIVE MODEL IS master + this change's delta + the PREDECESSOR deltas in
|
|
242
|
+
context. {{predecessor_count}} predecessor(s) — open changes in a LOWER wave:
|
|
243
|
+
{{predecessor_ids}}. You MAY build on one. {{sibling_count}} SIBLING change(s) at
|
|
244
|
+
the same or a higher wave: {{sibling_ids}} — never assume any of them lands; plan
|
|
245
|
+
against the API as it stands.
|
|
246
|
+
- TWO CASES — decide from the delta-spec in context: SPEC-DRIVEN, where every endpoint
|
|
247
|
+
row serves one of the delta-spec's ids; PLAN-ONLY (an unfilled stub), where an
|
|
248
|
+
endpoint row still traces to an EXISTING spec id — the realisation moves, never the
|
|
249
|
+
requirement.
|
|
250
|
+
- TECHNOLOGY-AGNOSTIC: protocol level only, conceptual types, no code.
|
|
251
|
+
- Do NOT edit the Wave / Base version / Delta spec / Delta backend plan headers —
|
|
252
|
+
scripts stamp those.
|
|
253
|
+
Guidance: {{USER_INPUT}}
|
|
254
|
+
|
|
119
255
|
# ═════════════════════ BACKEND LANE — the service delta plan ═════════════════
|
|
120
256
|
- id: ctx_backend_delta
|
|
121
257
|
condition: "{{be_existing}} == true"
|
|
@@ -136,6 +272,11 @@ workflow:
|
|
|
136
272
|
# unfilled one, and that is a valid state.
|
|
137
273
|
- "{{spec_file}}" # the current master spec (READ ONLY)
|
|
138
274
|
- "{{backend_plan}}" # the current master plan (READ ONLY — never amended here)
|
|
275
|
+
- { path: "{{delta_api_plan}}", optional: true }
|
|
276
|
+
# THE CHANGE'S API, authored just above — the
|
|
277
|
+
# endpoints this plan realises. Absent for a change
|
|
278
|
+
# planned before delta API plans existed, whose API
|
|
279
|
+
# is in this plan.
|
|
139
280
|
- { path: "{{domain_model}}", optional: true }
|
|
140
281
|
# the context's CANONICAL model — the aggregate root,
|
|
141
282
|
# its entities and its invariants live here, not in the
|
|
@@ -205,13 +346,7 @@ workflow:
|
|
|
205
346
|
that either order of merging works. If consuming one is genuinely unavoidable,
|
|
206
347
|
that is a dependency, not an overlap: record it under "## Depends on" and say the
|
|
207
348
|
wave must be raised. Until merged, every one of them is a proposal.
|
|
208
|
-
-
|
|
209
|
-
lane" with every addition or change to this feature's published surface, as field
|
|
210
|
-
shapes. That table is the only part of this document the frontend delta plan may
|
|
211
|
-
bind against; a contract the UI needs and this document never states becomes a
|
|
212
|
-
client-side workaround. "None" when the surface is unchanged. Each row carries the
|
|
213
|
-
endpoint's operation id: a changed endpoint keeps the one the master plan gives
|
|
214
|
-
it, and a new endpoint gets one unique across the service.
|
|
349
|
+
- {{be_delta_api_rule}}
|
|
215
350
|
- Stay technology-agnostic (no code) and keep the master's form: behaviour as
|
|
216
351
|
signatures + pre/post/invariant/raises, contracts as field shapes.
|
|
217
352
|
- TWO CASES — decide from the delta-spec in context:
|
|
@@ -260,13 +395,17 @@ workflow:
|
|
|
260
395
|
- { path: "{{delta_model}}", optional: true }
|
|
261
396
|
- "{{spec_file}}" # the current master spec (READ ONLY)
|
|
262
397
|
- "{{frontend_plan}}" # the current master plan (READ ONLY — never amended here)
|
|
263
|
-
- { path: "{{
|
|
264
|
-
#
|
|
265
|
-
#
|
|
398
|
+
- { path: "{{delta_api_read}}", optional: true }
|
|
399
|
+
# THIS CHANGE'S API — its delta API plan, or the
|
|
400
|
+
# delta backend plan of a change planned before
|
|
401
|
+
# those — is what this lane binds to. READ here, and
|
|
266
402
|
# authored above only when the backend is a working
|
|
267
403
|
# lane: another developer may own it. Optional: an
|
|
268
|
-
# experience-only change has no
|
|
404
|
+
# experience-only change has no API delta, and a
|
|
269
405
|
# frontend-only project has no service lane at all.
|
|
406
|
+
- { path: "{{api_read}}", optional: true }
|
|
407
|
+
# THE FEATURE'S API as it stands — for the endpoints
|
|
408
|
+
# this change leaves alone.
|
|
270
409
|
- { path: "{{domain_model}}", optional: true }
|
|
271
410
|
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
272
411
|
- { path: "{{cr_dir}}/delta-slice-analyze-report.md", optional: true }
|
|
@@ -315,15 +454,17 @@ workflow:
|
|
|
315
454
|
delta-spec does not describe is a SPEC gap: log it as a DA-n that routes back to
|
|
316
455
|
`specify`, never invent the requirement here.
|
|
317
456
|
- THE LANE BOUNDARY IS THE PUBLISHED SURFACE. Bind against the delta-spec's IC- ids
|
|
318
|
-
and, where
|
|
319
|
-
lane" table
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
457
|
+
and, where this change's API is in context, its "Contract deltas for the frontend
|
|
458
|
+
lane" table and the endpoint blocks it writes; for an endpoint the change leaves
|
|
459
|
+
alone, the feature's API in context. CITE those contracts; do not restate,
|
|
460
|
+
re-derive or contradict them, and introduce no server-side behaviour or persistence
|
|
461
|
+
assumption. What the experience needs and the surface does not offer goes under
|
|
462
|
+
"Contract deltas needed", raised back to the service lane — never worked around in
|
|
463
|
+
the client.
|
|
323
464
|
- A BINDING ROW'S ELEMENT LEADS WITH ITS OPERATION ID — `OperationId` · `VERB
|
|
324
|
-
/route` — copied exactly from
|
|
325
|
-
|
|
326
|
-
|
|
465
|
+
/route` — copied exactly from this change's API or, for an endpoint the change does
|
|
466
|
+
not touch, the feature's. Implement looks the endpoint up by it in the API document
|
|
467
|
+
drafted from the API plans, this change's included.
|
|
327
468
|
- UX- IS THIS DOCUMENT'S TO REALISE. The service plan excludes the spec's Experience
|
|
328
469
|
Standards by contract, so a UX- item in the delta-spec that reaches neither plan
|
|
329
470
|
is a dropped requirement.
|
|
@@ -341,7 +482,7 @@ workflow:
|
|
|
341
482
|
- TECHNOLOGY-AGNOSTIC: no framework, router, state library, component library or
|
|
342
483
|
styling approach — responsibilities, field shapes, events and states only. The
|
|
343
484
|
header's Lane field is `{{fe_lane_id}}`.
|
|
344
|
-
- Do NOT edit the Wave / Base version / Delta spec /
|
|
485
|
+
- Do NOT edit the Wave / Base version / Delta spec / API delta headers — scripts
|
|
345
486
|
stamp those.
|
|
346
487
|
Guidance: {{USER_INPUT}}
|
|
347
488
|
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
# `action: include`) by every domain-aware converted skill. Same flow:
|
|
6
6
|
# 1. resolve the active domain + candidate rule files for {{command}}
|
|
7
7
|
# 2. Domain gate: no domain / no domain-rule-bindings → ask Continue (technical only)
|
|
8
|
-
# or EXIT (stops the whole skill, no side effects)
|
|
8
|
+
# or EXIT (stops the whole skill, no side effects). Where the map exists but loads
|
|
9
|
+
# less than it says — no `## /{{command}}` heading (absent or misspelled; a heading
|
|
10
|
+
# present and deliberately empty does not warn), or a mapped file not on disk — WARN
|
|
11
|
+
# and carry on; the engine returns each warning in the run result.
|
|
9
12
|
# 3. Selection box: all / subset / none
|
|
10
13
|
# 4. load the selected rule files — into the STICKY STANDARDS SET (D3: domain
|
|
11
14
|
# rules are SYSTEM standards; every later generation sees them without the
|
|
@@ -43,7 +46,32 @@ workflow:
|
|
|
43
46
|
outputs:
|
|
44
47
|
- { name: domain, from: "$.domain" }
|
|
45
48
|
- { name: dg_rulemap_exists, from: "$.rulemap_exists" }
|
|
49
|
+
- { name: dg_heading_found, from: "$.heading_found" }
|
|
46
50
|
- { name: dg_candidates, from: "$.candidates" }
|
|
51
|
+
- { name: dg_missing, from: "$.missing" }
|
|
52
|
+
|
|
53
|
+
# 1b. What the map says and does not load — warnings, never stops, and only where the
|
|
54
|
+
# map exists (its absence is the gate below). The engine has no "list is not empty"
|
|
55
|
+
# test, so the missing list sets a flag false when it has entries.
|
|
56
|
+
- id: dg_flags
|
|
57
|
+
action: set
|
|
58
|
+
vars: { dg_all_on_disk: "true" }
|
|
59
|
+
- id: dg_flag_missing
|
|
60
|
+
condition: "{{dg_missing}} != []"
|
|
61
|
+
action: set
|
|
62
|
+
vars: { dg_all_on_disk: "false" }
|
|
63
|
+
- id: warn_no_heading
|
|
64
|
+
condition: "{{dg_rulemap_exists}} == true"
|
|
65
|
+
action: verify_context
|
|
66
|
+
require_true: dg_heading_found
|
|
67
|
+
on_false: continue
|
|
68
|
+
on_false_message: "WARNING — guardrails/{{domain}}/domain-rule-bindings.md has no `## /{{command}}` heading, so this step loads no domain rules from the map. If that is not intended, add the heading and its rule files — it must read exactly `## /{{command}}`."
|
|
69
|
+
- id: warn_missing
|
|
70
|
+
condition: "{{dg_rulemap_exists}} == true"
|
|
71
|
+
action: verify_context
|
|
72
|
+
require_true: dg_all_on_disk
|
|
73
|
+
on_false: continue
|
|
74
|
+
on_false_message: "WARNING — guardrails/{{domain}}/domain-rule-bindings.md maps these files under `## /{{command}}`, but they are not on disk, so they were not loaded: {{dg_missing}}. Correct the paths, or add the files."
|
|
47
75
|
|
|
48
76
|
# 2. Domain gate — only when the project or its domain-rule-bindings.md is missing.
|
|
49
77
|
- id: gate
|
|
@@ -68,6 +68,7 @@ workflow:
|
|
|
68
68
|
fp_residue_note: "not checked"
|
|
69
69
|
fp_lane_found: "false"
|
|
70
70
|
fp_lane_file: ""
|
|
71
|
+
fp_lane_contract_write: ""
|
|
71
72
|
fp_lane_contract_run: ""
|
|
72
73
|
fp_lane_contract: ""
|
|
73
74
|
fp_lane_client: ""
|
|
@@ -198,8 +199,9 @@ workflow:
|
|
|
198
199
|
on_false_message: "NOT scaffolded — the {{fl_name}} unpacker failed and wrote {{fp_written}} file(s). Its output ends: {{fp_unpack_out}}"
|
|
199
200
|
|
|
200
201
|
# 5b. The pack's LANE SETTINGS (<pack name>.lane.json beside it): what only the pack
|
|
201
|
-
# knows about the project it scaffolds — chiefly how its service
|
|
202
|
-
#
|
|
202
|
+
# knows about the project it scaffolds — chiefly how its service's API document is
|
|
203
|
+
# written, or how the service is started on its own to fetch it, so analyze-slice
|
|
204
|
+
# can check the API it serves against the plan. Recorded on the lane with lane-set;
|
|
203
205
|
# a key the file leaves empty is not touched, and a pack with no such file leaves
|
|
204
206
|
# the lane on its stack profile's values.
|
|
205
207
|
- id: fp_lane_settings
|
|
@@ -211,6 +213,7 @@ workflow:
|
|
|
211
213
|
outputs:
|
|
212
214
|
- { name: fp_lane_found, from: "$.found" }
|
|
213
215
|
- { name: fp_lane_file, from: "$.file" }
|
|
216
|
+
- { name: fp_lane_contract_write, from: "$.contract_write" }
|
|
214
217
|
- { name: fp_lane_contract_run, from: "$.contract_run" }
|
|
215
218
|
- { name: fp_lane_contract, from: "$.contract" }
|
|
216
219
|
- { name: fp_lane_client, from: "$.client" }
|
|
@@ -221,14 +224,14 @@ workflow:
|
|
|
221
224
|
on_error: continue
|
|
222
225
|
script: { ref: actions }
|
|
223
226
|
subcommand: lane-set
|
|
224
|
-
args: ["--id", "{{fl_lane_id}}", "--set", "contract_run={{fp_lane_contract_run}}", "--set", "contract={{fp_lane_contract}}", "--set", "client={{fp_lane_client}}"]
|
|
227
|
+
args: ["--id", "{{fl_lane_id}}", "--set", "contract_write={{fp_lane_contract_write}}", "--set", "contract_run={{fp_lane_contract_run}}", "--set", "contract={{fp_lane_contract}}", "--set", "client={{fp_lane_client}}"]
|
|
225
228
|
outputs:
|
|
226
229
|
- { name: fp_lane_recorded, from: "$.ok" }
|
|
227
230
|
|
|
228
231
|
- id: fp_lane_noted
|
|
229
232
|
action: set
|
|
230
233
|
condition: "{{fp_lane_recorded}} == true"
|
|
231
|
-
vars: { fp_lane_note: "; the pack's API-
|
|
234
|
+
vars: { fp_lane_note: "; the pack's API-document settings ({{fp_lane_file}}) recorded on the {{fl_lane_id}} lane" }
|
|
232
235
|
|
|
233
236
|
# 6. The merges the rule file asks for: the file that was there (put aside) and the
|
|
234
237
|
# pack's version (now on disk) become one, as the rule's instruction says.
|
|
@@ -69,6 +69,7 @@ workflow:
|
|
|
69
69
|
fs_build_ack: ""
|
|
70
70
|
fs_build_ok: ""
|
|
71
71
|
fs_build_note: "build not run — /appweaver build compiles it and repairs compile errors"
|
|
72
|
+
fs_hdr_contract_write: ""
|
|
72
73
|
fs_hdr_contract_run: ""
|
|
73
74
|
fs_hdr_contract: ""
|
|
74
75
|
fs_hdr_client: ""
|
|
@@ -76,20 +77,17 @@ workflow:
|
|
|
76
77
|
fs_placeholder: ""
|
|
77
78
|
fs_is_backend: "false"
|
|
78
79
|
fs_is_frontend: "false"
|
|
79
|
-
|
|
80
|
-
fs_need_url: "false"
|
|
80
|
+
fs_need_write: "false"
|
|
81
81
|
fs_need_client: "false"
|
|
82
|
-
|
|
83
|
-
fs_ans_contract: ""
|
|
82
|
+
fs_ans_contract_write: ""
|
|
84
83
|
fs_ans_client: ""
|
|
84
|
+
fs_set_write: ""
|
|
85
85
|
fs_set_run: ""
|
|
86
86
|
fs_set_url: ""
|
|
87
87
|
fs_set_client: ""
|
|
88
|
-
|
|
89
|
-
fs_cur_contract: ""
|
|
88
|
+
fs_cur_contract_write: ""
|
|
90
89
|
fs_cur_client: ""
|
|
91
|
-
|
|
92
|
-
fs_cur_url_shown: "none — the export is skipped until one is set"
|
|
90
|
+
fs_cur_write_shown: "none — the API is not checked against the plan until one is set"
|
|
93
91
|
fs_cur_client_shown: "none — implement asks for one before it generates the frontend"
|
|
94
92
|
fs_lane_written: ""
|
|
95
93
|
fs_lane_note: ""
|
|
@@ -152,6 +150,7 @@ workflow:
|
|
|
152
150
|
- { name: fs_source, from: "$.source" }
|
|
153
151
|
- { name: fs_title, from: "$.title" }
|
|
154
152
|
- { name: fs_reason, from: "$.reason" }
|
|
153
|
+
- { name: fs_hdr_contract_write, from: "$.contract_write" }
|
|
155
154
|
- { name: fs_hdr_contract_run, from: "$.contract_run" }
|
|
156
155
|
- { name: fs_hdr_contract, from: "$.contract" }
|
|
157
156
|
- { name: fs_hdr_client, from: "$.client" }
|
|
@@ -170,6 +169,7 @@ workflow:
|
|
|
170
169
|
- { name: fs_source, from: "$.source" }
|
|
171
170
|
- { name: fs_title, from: "$.title" }
|
|
172
171
|
- { name: fs_reason, from: "$.reason" }
|
|
172
|
+
- { name: fs_hdr_contract_write, from: "$.contract_write" }
|
|
173
173
|
- { name: fs_hdr_contract_run, from: "$.contract_run" }
|
|
174
174
|
- { name: fs_hdr_contract, from: "$.contract" }
|
|
175
175
|
- { name: fs_hdr_client, from: "$.client" }
|
|
@@ -421,14 +421,17 @@ workflow:
|
|
|
421
421
|
condition: "{{fs_build_ok}} == false"
|
|
422
422
|
vars: { fs_build_note: "build FAILED — run /appweaver build, which repairs compile errors: {{fs_build_tail}}" }
|
|
423
423
|
|
|
424
|
-
# 8. THE API CONTRACT SETTINGS — what
|
|
425
|
-
#
|
|
426
|
-
#
|
|
427
|
-
#
|
|
428
|
-
#
|
|
429
|
-
#
|
|
430
|
-
#
|
|
431
|
-
#
|
|
424
|
+
# 8. THE API CONTRACT SETTINGS — what the lanes use for the API between them
|
|
425
|
+
# (STACK-PROFILES.md §4.5). A frontend lane needs its client command, which generates
|
|
426
|
+
# its API client from the API documents drawn from the features' API plans. A backend lane may say how its
|
|
427
|
+
# service's as-built API document is written — `contract_write`, a command that writes
|
|
428
|
+
# it with no server started — which analyze-slice runs to check the API the code
|
|
429
|
+
# serves against the plan; or how the service is started to fetch it (`contract_run`
|
|
430
|
+
# + `contract`), which only the header gives. Each comes from the spec's header
|
|
431
|
+
# (`# contract-write:`, `# contract-run:`, `# contract:`, `# client:`, read at capture);
|
|
432
|
+
# the one the header does not give is asked for, and an empty answer keeps the lane's
|
|
433
|
+
# current value. What there is, is recorded on the lane with lane-set, as the pack
|
|
434
|
+
# route records a pack's .lane.json.
|
|
432
435
|
- id: fs_kind_backend
|
|
433
436
|
action: set
|
|
434
437
|
condition: "{{fl_kind}} == backend"
|
|
@@ -437,14 +440,15 @@ workflow:
|
|
|
437
440
|
action: set
|
|
438
441
|
condition: "{{fl_kind}} == frontend"
|
|
439
442
|
vars: { fs_is_frontend: "true" }
|
|
440
|
-
|
|
443
|
+
# Asked only when the header names no way at all: a start command it gives is a way.
|
|
444
|
+
- id: fs_need_write_arm
|
|
441
445
|
action: set
|
|
442
|
-
condition: "{{
|
|
443
|
-
vars: {
|
|
444
|
-
- id:
|
|
446
|
+
condition: "{{fs_hdr_contract_write}} == "
|
|
447
|
+
vars: { fs_need_write: "{{fs_is_backend}}" }
|
|
448
|
+
- id: fs_need_write_run
|
|
445
449
|
action: set
|
|
446
|
-
condition: "{{
|
|
447
|
-
vars: {
|
|
450
|
+
condition: "{{fs_hdr_contract_run}} != "
|
|
451
|
+
vars: { fs_need_write: "false" }
|
|
448
452
|
- id: fs_need_client_arm
|
|
449
453
|
action: set
|
|
450
454
|
condition: "{{fs_hdr_client}} == "
|
|
@@ -458,47 +462,36 @@ workflow:
|
|
|
458
462
|
subcommand: lane-resolve
|
|
459
463
|
args: ["--id", "{{fl_lane_id}}"]
|
|
460
464
|
outputs:
|
|
461
|
-
- { name:
|
|
462
|
-
- { name:
|
|
463
|
-
|
|
464
|
-
- id: fs_cur_run_known
|
|
465
|
+
- { name: fs_cur_contract_write, from: "$.backend_contract_write" }
|
|
466
|
+
- { name: fs_cur_client, from: "$.frontend_client" }
|
|
467
|
+
- id: fs_cur_write_known
|
|
465
468
|
action: set
|
|
466
|
-
condition: "{{
|
|
467
|
-
vars: {
|
|
468
|
-
- id: fs_cur_url_known
|
|
469
|
-
action: set
|
|
470
|
-
condition: "{{fs_cur_contract}} != "
|
|
471
|
-
vars: { fs_cur_url_shown: "{{fs_cur_contract}}" }
|
|
469
|
+
condition: "{{fs_cur_contract_write}} != "
|
|
470
|
+
vars: { fs_cur_write_shown: "{{fs_cur_contract_write}}" }
|
|
472
471
|
- id: fs_cur_client_known
|
|
473
472
|
action: set
|
|
474
473
|
condition: "{{fs_cur_client}} != "
|
|
475
474
|
vars: { fs_cur_client_shown: "{{fs_cur_client}}" }
|
|
476
475
|
|
|
477
|
-
- id:
|
|
478
|
-
action: ask
|
|
479
|
-
condition: "{{fs_need_run}} == true"
|
|
480
|
-
prompt: |
|
|
481
|
-
How does the API in lane `{{fl_lane_id}}` start ON ITS OWN? /appweaver implement starts
|
|
482
|
-
it with this command to fetch its OpenAPI document before it generates the frontend.
|
|
483
|
-
The spec's header gives no `# contract-run:`.
|
|
484
|
-
Type the command — it runs in {{fl_workdir}} — with no orchestration host and nothing
|
|
485
|
-
the API cannot reach on this machine.
|
|
486
|
-
Leave it empty to keep the current one: {{fs_cur_run_shown}}
|
|
487
|
-
answer_var: fs_ans_contract_run
|
|
488
|
-
- id: fs_ask_contract
|
|
476
|
+
- id: fs_ask_contract_write
|
|
489
477
|
action: ask
|
|
490
|
-
condition: "{{
|
|
478
|
+
condition: "{{fs_need_write}} == true"
|
|
491
479
|
prompt: |
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
480
|
+
How is the API document of lane `{{fl_lane_id}}` written, with no server started?
|
|
481
|
+
analyze-slice runs this command once the code is built and checks the API it
|
|
482
|
+
writes against backend-plan.md — the frontend lane is bound to the plan, never to
|
|
483
|
+
the running service. The spec's header gives no `# contract-write:`.
|
|
484
|
+
Type a command that writes the service's OpenAPI JSON to {{fs_placeholder}} — the
|
|
485
|
+
stack's own generator, or a script that loads the application and dumps its
|
|
486
|
+
document. It runs in {{fl_workdir}}.
|
|
487
|
+
Leave it empty to keep the current one: {{fs_cur_write_shown}}
|
|
488
|
+
answer_var: fs_ans_contract_write
|
|
496
489
|
- id: fs_ask_client
|
|
497
490
|
action: ask
|
|
498
491
|
condition: "{{fs_need_client}} == true"
|
|
499
492
|
prompt: |
|
|
500
|
-
Which command generates this frontend's API client from
|
|
501
|
-
|
|
493
|
+
Which command generates this frontend's API client from an OpenAPI document? The
|
|
494
|
+
spec's header gives no `# client:`.
|
|
502
495
|
It runs in {{fl_workdir}}, and {{fs_placeholder}} in it stands for the document's path —
|
|
503
496
|
for example: npx openapi-typescript {{fs_placeholder}} -o lib/api/schema.d.ts
|
|
504
497
|
Leave it empty to keep the current one: {{fs_cur_client_shown}}
|
|
@@ -509,15 +502,11 @@ workflow:
|
|
|
509
502
|
- id: fs_set_backend
|
|
510
503
|
action: set
|
|
511
504
|
condition: "{{fs_is_backend}} == true"
|
|
512
|
-
vars: { fs_set_run: "{{fs_hdr_contract_run}}", fs_set_url: "{{fs_hdr_contract}}" }
|
|
513
|
-
- id:
|
|
514
|
-
action: set
|
|
515
|
-
condition: "{{fs_need_run}} == true"
|
|
516
|
-
vars: { fs_set_run: "{{fs_ans_contract_run}}" }
|
|
517
|
-
- id: fs_set_url_answer
|
|
505
|
+
vars: { fs_set_write: "{{fs_hdr_contract_write}}", fs_set_run: "{{fs_hdr_contract_run}}", fs_set_url: "{{fs_hdr_contract}}" }
|
|
506
|
+
- id: fs_set_write_answer
|
|
518
507
|
action: set
|
|
519
|
-
condition: "{{
|
|
520
|
-
vars: {
|
|
508
|
+
condition: "{{fs_need_write}} == true"
|
|
509
|
+
vars: { fs_set_write: "{{fs_ans_contract_write}}" }
|
|
521
510
|
- id: fs_set_frontend
|
|
522
511
|
action: set
|
|
523
512
|
condition: "{{fs_is_frontend}} == true"
|
|
@@ -532,7 +521,7 @@ workflow:
|
|
|
532
521
|
on_error: continue
|
|
533
522
|
script: { ref: actions }
|
|
534
523
|
subcommand: lane-set
|
|
535
|
-
args: ["--id", "{{fl_lane_id}}", "--set", "contract_run={{fs_set_run}}", "--set", "contract={{fs_set_url}}", "--set", "client={{fs_set_client}}"]
|
|
524
|
+
args: ["--id", "{{fl_lane_id}}", "--set", "contract_write={{fs_set_write}}", "--set", "contract_run={{fs_set_run}}", "--set", "contract={{fs_set_url}}", "--set", "client={{fs_set_client}}"]
|
|
536
525
|
outputs:
|
|
537
526
|
- { name: fs_lane_written, from: "$.written" }
|
|
538
527
|
- id: fs_lane_noted
|