aw-spec-cli 0.1.20 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/assets/.appweaver/ORCH-DIALECT.md +8 -3
  2. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +22 -1
  3. package/assets/.appweaver/orchestrator/orchestrator.py +65 -3
  4. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +6 -6
  5. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +107 -48
  6. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +17 -9
  7. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +265 -20
  8. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +4 -4
  9. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +89 -15
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +140 -36
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +354 -52
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +3 -3
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +47 -23
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +1 -1
  15. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +651 -76
  16. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +600 -59
  17. package/assets/.appweaver/scripts/python/aw-crstate.py +194 -0
  18. package/assets/.appweaver/scripts/python/aw-foundation.py +151 -0
  19. package/assets/.appweaver/scripts/python/aw-payload-parity.py +3 -0
  20. package/assets/.appweaver/scripts/python/aw-plan-coverage.py +423 -0
  21. package/assets/.appweaver/scripts/python/aw-stack.py +251 -28
  22. package/assets/.appweaver/scripts/python/aw-testlog.py +148 -0
  23. package/assets/.appweaver/stack-profiles/dotnet-webapi.json +1 -0
  24. package/assets/.appweaver/stack-profiles/java-spring.json +1 -0
  25. package/assets/.appweaver/stack-profiles/node-express.json +1 -0
  26. package/assets/.appweaver/stack-profiles/python-fastapi.json +1 -0
  27. package/assets/.appweaver/templates/constitution-domain-template.md +17 -2
  28. package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +10 -6
  29. package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +4 -0
  30. package/assets/guardrails/technology/dotnet-webapi/auditing.md +3 -1
  31. package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +5 -3
  32. package/assets/guardrails/technology/dotnet-webapi/cqrs.md +5 -1
  33. package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +13 -5
  34. package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +2 -0
  35. package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +6 -2
  36. package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +70 -0
  37. package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +3 -0
  38. package/assets/guardrails/technology/dotnet-webapi/testing.md +10 -0
  39. package/assets/guardrails/technology/nextjs/ui-constitution.md +8 -1
  40. package/package.json +1 -1
  41. package/assets/.appweaver/orchestrator/.state/smoke.state.json +0 -32
@@ -137,7 +137,7 @@
137
137
 
138
138
  name: aw-spec-onboard
139
139
  description: Onboard an existing codebase for brownfield DEVELOPMENT — a deterministic whole-repo scan produces a high-level OKF feature map (folders/concepts) PLUS the project-level governance scaffold (split constitution + guardrails/projects/brf-<domain>/ with its DOMAIN rule files at domain/ + domain-rule-bindings + code-derived TECHNICAL rule files at service/ + tech-rule-bindings + activated domain), all provisional-from-scan and refined per slice by adopt. Where the scan finds a frontend lane in the CODE, it also authors that lane's half — constitution.ui.md (UX-*) — and its code-derived rule files at brf-<domain>/frontend/, mapped under `## /aw-spec-ui-constitution`; a backend-only repo gets none of it. Every rule tier is reverse-engineered from the CODE alone (never a greenfield rule file), evidenced-only, and WIRED per-workflow so the whole forward pipeline AND adopt govern against THIS codebase — never a greenfield fallback. It also reverse-engineers the repo's own technical structure into a project-specific design + delta-design template pair, installed as the project template overrides, and — where the lane exists — the frontend lane's own pair from the UI source. Every authoring step is shaped by a NEUTRAL rev-templates skeleton; no greenfield constitution, rule file or document template is ever placed in context. Accepts an optional concerns list to look for (onboard decides which are actually present). Reads any declared lane, never writes one. No migration content; never modifies source.
140
- version: 2.0.0
140
+ version: 2.1.0
141
141
  agent_agnostic: true
142
142
  dialect: aw-spec.orchestrator/v1
143
143
 
@@ -1270,8 +1270,8 @@ workflow:
1270
1270
  numeral is whatever the code supported — never a value carried over from another
1271
1271
  constitution. If it mandates no modelling methodology, say so plainly rather than
1272
1272
  supplying one.
1273
- - Author ONLY the domain layer: domain principles, domain folder additions,
1274
- regulatory/security posture, domain governance — from the project + charter. NUMBER
1273
+ - Author ONLY the domain layer: project identity, domain principles, domain folder
1274
+ additions, regulatory/security posture, domain governance — from the project + charter. NUMBER
1275
1275
  them by CONTINUING the technical half's roman numerals: READ
1276
1276
  constitution.technical.md (in context), find its LAST numeral N, start the first
1277
1277
  domain principle at N+1 (do NOT assume it ends at XV). TITLE each by DOMAIN
@@ -14,8 +14,13 @@
14
14
  # evidence sits in, and both are only ever proposals — an operator selects from the
15
15
  # list or types a path the kit has never seen.
16
16
  #
17
- # stack-list --suggest backend → select stack → ask source path → lane-declare
18
- # stack-list --suggest frontend → select stack → ask source path → lane-declare
17
+ # stack-list --suggest backend → select stack → stack-list --profile → ask source path → lane-declare
18
+ # stack-list --suggest frontend → select stack → stack-list --profile → ask source path → lane-declare
19
+ #
20
+ # THE SOURCE-PATH OPTIONS ARE BUILT AFTER THE STACK IS CHOSEN. Most profiles of a kind
21
+ # share a default root (six frontend stacks default to web/), so a list built before the
22
+ # choice cannot say which stack an option belongs to. The second stack-list call answers
23
+ # for the chosen stack alone.
19
24
  #
20
25
  # (none) IS A REAL ANSWER FOR EITHER LANE, and it is how a project says "I do not
21
26
  # have one of these". A backend-only project answers (none) to the UI lane; a
@@ -56,7 +61,7 @@
56
61
 
57
62
  name: aw-spec-select-stack
58
63
  description: Declare the project's lanes — each lane's STACK and its SOURCE PATH — and persist them to .appweaver/stacks.json, the single channel every later workflow resolves and gates on. The first command of both the greenfield and the brownfield sequence, and the sole writer of the lane model.
59
- version: 2.0.0
64
+ version: 2.3.0
60
65
  agent_agnostic: true
61
66
  dialect: aw-spec.orchestrator/v1
62
67
 
@@ -93,9 +98,7 @@ workflow:
93
98
  - { name: api_options, from: "$.select_options" }
94
99
  - { name: api_ids, from: "$.ids" }
95
100
  - { name: api_preselect, from: "$.preselect" }
96
- - { name: api_root_options, from: "$.root_options" }
97
- - { name: api_detected, from: "$.detected_profile" }
98
- - { name: api_detected_root, from: "$.detected_root" }
101
+ - { name: api_detected, from: "$.detected_summary" }
99
102
  - { name: api_none_sentinel, from: "$.none_sentinel" }
100
103
 
101
104
  # 2. No profiles installed is a setup error, not an empty menu. Gated on the
@@ -114,7 +117,7 @@ workflow:
114
117
  # does a detected stack; a project with exactly one candidate auto-confirms.
115
118
  - id: select_api_stack
116
119
  action: user_select
117
- prompt: "Select the API (backend) stack — it owns the service lane. This repo's own files point at: {{api_detected}} (leading the list). (none) declares a project with no service lane at all, and stops the technical constitution half and the service rule files being authored:"
120
+ prompt: "Select the API (backend) stack — it owns the service lane. This repo's own files point at: {{api_detected}}. A detected stack leads the list. (none) declares a project with no service lane at all, and stops the technical constitution half and the service rule files being authored:"
118
121
  options_from_variable: api_options
119
122
  preselect_from: api_preselect
120
123
  single_option_confirm: true
@@ -133,14 +136,24 @@ workflow:
133
136
  condition: "{{chosen_api}} == {{api_none_sentinel}}"
134
137
  vars: { api_selected: "" }
135
138
 
136
- # 5. WHERE the service lane's source lives. Offered best-first — where the
137
- # evidence actually sits, then each profile's own default — and free text is
138
- # accepted, because a repo that matches no profile still has a source path.
139
- # The label after the path says why it is offered and is stripped on the way in.
139
+ # 5. WHERE the service lane's source lives. Offered best-first for the CHOSEN stack —
140
+ # where its evidence sits, then its own default, then where another stack's
141
+ # evidence sits — and free text is accepted, because a repo that matches no
142
+ # profile still has a source path. The label after the path says why it is
143
+ # offered and is stripped on the way in.
144
+ - id: list_api_roots
145
+ action: execute_script
146
+ condition: "{{api_selected}}"
147
+ script: { ref: actions }
148
+ subcommand: stack-list
149
+ args: ["--kind", "backend", "--suggest", "--profile", "{{chosen_api}}"]
150
+ outputs:
151
+ - { name: api_root_options, from: "$.root_options" }
152
+
140
153
  - id: ask_api_source_path
141
154
  action: ask
142
155
  condition: "{{api_selected}}"
143
- prompt: "Service lane SOURCE PATH — the subtree this lane owns, the only place implement may write, and the directory build/test/run act on. Pick one or type your own:"
156
+ prompt: "Service lane SOURCE PATH — the subtree this lane owns, where implement writes its code, and the directory build/test/run act on. Its tests go to the stack's test path beside it (tests/ next to src/ for .NET), shown in the summary. Pick one or type your own:"
144
157
  options_from_variable: api_root_options
145
158
  answer_var: api_source_path
146
159
 
@@ -159,6 +172,7 @@ workflow:
159
172
  - { name: api_lane_id, from: "$.lane_id" }
160
173
  - { name: api_profile, from: "$.profile" }
161
174
  - { name: api_root, from: "$.source_path" }
175
+ - { name: api_test_path, from: "$.test_path" }
162
176
  - { name: api_root_exists, from: "$.root_exists" }
163
177
 
164
178
  # ═══════════════ UI LANE ════════════════════════════════════════════════════
@@ -176,15 +190,13 @@ workflow:
176
190
  - { name: ui_options, from: "$.select_options" }
177
191
  - { name: ui_ids, from: "$.ids" }
178
192
  - { name: ui_preselect, from: "$.preselect" }
179
- - { name: ui_root_options, from: "$.root_options" }
180
- - { name: ui_detected, from: "$.detected_profile" }
181
- - { name: ui_detected_root, from: "$.detected_root" }
193
+ - { name: ui_detected, from: "$.detected_summary" }
182
194
  - { name: ui_none_sentinel, from: "$.none_sentinel" }
183
195
 
184
196
  # 8. Choose the UI stack.
185
197
  - id: select_ui_stack
186
198
  action: user_select
187
- prompt: "Select the UI (frontend) stack — it owns the web lane, and its root must not overlap the service lane's. This repo's own files point at: {{ui_detected}} (leading the list). (none) declares a backend-only project, and stops the UI constitution half and the frontend rule files being authored:"
199
+ prompt: "Select the UI (frontend) stack — it owns the web lane, and its root must not overlap the service lane's. This repo's own files point at: {{ui_detected}}. A detected stack leads the list. (none) declares a backend-only project, and stops the UI constitution half and the frontend rule files being authored:"
188
200
  options_from_variable: ui_options
189
201
  preselect_from: ui_preselect
190
202
  single_option_confirm: true
@@ -199,7 +211,16 @@ workflow:
199
211
  condition: "{{chosen_ui}} == {{ui_none_sentinel}}"
200
212
  vars: { ui_selected: "" }
201
213
 
202
- # 9. WHERE the UI lane's source lives.
214
+ # 9. WHERE the UI lane's source lives, offered for the chosen UI stack.
215
+ - id: list_ui_roots
216
+ action: execute_script
217
+ condition: "{{ui_selected}}"
218
+ script: { ref: actions }
219
+ subcommand: stack-list
220
+ args: ["--kind", "frontend", "--suggest", "--profile", "{{chosen_ui}}"]
221
+ outputs:
222
+ - { name: ui_root_options, from: "$.root_options" }
223
+
203
224
  - id: ask_ui_source_path
204
225
  action: ask
205
226
  condition: "{{ui_selected}}"
@@ -217,6 +238,7 @@ workflow:
217
238
  - { name: ui_lane_id, from: "$.lane_id" }
218
239
  - { name: ui_profile, from: "$.profile" }
219
240
  - { name: ui_root, from: "$.source_path" }
241
+ - { name: ui_test_path, from: "$.test_path" }
220
242
  - { name: ui_root_exists, from: "$.root_exists" }
221
243
 
222
244
  # ═══════════════ CHECK + REPORT ═════════════════════════════════════════════
@@ -257,7 +279,7 @@ workflow:
257
279
  - id: api_summary_lane
258
280
  action: set
259
281
  condition: "{{api_declared}}"
260
- vars: { api_summary: "{{api_lane_id}} ({{api_profile}}) at {{api_root}} — source path exists: {{api_root_exists}}" }
282
+ vars: { api_summary: "{{api_lane_id}} ({{api_profile}}) at {{api_root}}, tests at {{api_test_path}} — source path exists: {{api_root_exists}}" }
261
283
 
262
284
  - id: ui_summary_none
263
285
  action: set
@@ -265,7 +287,7 @@ workflow:
265
287
  - id: ui_summary_lane
266
288
  action: set
267
289
  condition: "{{ui_declared}}"
268
- vars: { ui_summary: "{{ui_lane_id}} ({{ui_profile}}) at {{ui_root}} — source path exists: {{ui_root_exists}}" }
290
+ vars: { ui_summary: "{{ui_lane_id}} ({{ui_profile}}) at {{ui_root}}, tests at {{ui_test_path}} — source path exists: {{ui_root_exists}}" }
269
291
 
270
292
  # 12. Read the finished declaration back through the reader every later workflow
271
293
  # uses, so what this run reports is what they will resolve.
@@ -287,10 +309,12 @@ outputs:
287
309
  ui_lane: "{{ui_summary}}"
288
310
  api_source_path: "{{api_root}}"
289
311
  ui_source_path: "{{ui_root}}"
312
+ api_test_path: "{{api_test_path}}"
313
+ ui_test_path: "{{ui_test_path}}"
290
314
  api_source_path_exists: "{{be_root_exists}}"
291
315
  ui_source_path_exists: "{{fe_root_exists}}"
292
- api_detected: "{{api_detected}} at {{api_detected_root}}"
293
- ui_detected: "{{ui_detected}} at {{ui_detected_root}}"
316
+ api_detected: "{{api_detected}}"
317
+ ui_detected: "{{ui_detected}}"
294
318
  prototype: "{{prototype}}"
295
- note: "Lanes persisted to .appweaver/stacks.json. Each lane carries a STACK and a SOURCE PATH, and both are load-bearing. The SOURCE PATH is ownership: implement's service lane writes only under the service lane's and its frontend lane only under the UI lane's, build/test/run act on it, and onboard reverse-engineers out of it — overlapping paths are refused here rather than discovered later as two generators writing one file. The build/test/run commands are project-supplied strings the engine shells out to, which is how it stays stdlib-only and stack-ignorant. api_detected / ui_detected report what this repo's own files pointed at, whether or not it was chosen — empty on a greenfield repo with nothing to detect. A source path that does not exist yet is NORMAL here and reported rather than refused: greenfield declares src/ before implement creates it. The on-disk check runs in the phases that read the subtree — implement, build, run and onboard all call lane-verify and STOP with the fix named. Declining a lane with (none) is a real declaration and removes any lane of that kind a previous run left behind; it is also what stops that lane's constitution half and rule files being authored. `prototype` is not a lane — it has a root and a run command so an operator can open it, and nothing else. Re-run this workflow any time to re-stack a lane, move a source path, or drop a lane with (none); it is the only workflow that may write this file."
296
- next: "GREENFIELD: /appweaver select-domain → constitution → select-feature → specify. BROWNFIELD: /appweaver onboard — it reverse-engineers out of the source paths declared here and generates the project (no select-domain; onboard derives and activates it from the code). Run this workflow FIRST in either sequence: constitution reads the declaration to decide which halves to author, and onboard reads it to decide what to scan."
319
+ note: "Lanes persisted to .appweaver/stacks.json. Each lane carries a STACK and a SOURCE PATH, and both are load-bearing. The SOURCE PATH is ownership: implement's service lane writes its code only under the service lane's and its frontend lane only under the UI lane's, build/test/run act on it, and onboard reverse-engineers out of it. Each lane also owns a TEST PATH, taken from its stack profile — tests/ beside a .NET src/, the source path itself for a stack that keeps its tests inside — and implement writes the lane's tests there. Overlapping paths of either kind are refused here rather than discovered later as two generators writing one file. The build/test/run commands are project-supplied strings the engine shells out to, which is how it stays stdlib-only and stack-ignorant. api_detected / ui_detected report what this repo's own files pointed at, whether or not it was chosen, as "<stack> at <path>" — or "nothing detected" on a greenfield repo with nothing to detect. A source path that does not exist yet is NORMAL here and reported rather than refused: greenfield declares src/ before implement creates it. The on-disk check runs in the phases that read the subtree — implement, build, run and onboard all call lane-verify and STOP with the fix named. Declining a lane with (none) is a real declaration and removes any lane of that kind a previous run left behind; it is also what stops that lane's constitution half and rule files being authored. `prototype` is not a lane — it has a root and a run command so an operator can open it, and nothing else. Re-run this workflow any time to re-stack a lane, move a source path, or drop a lane with (none); it is the only workflow that may write this file."
320
+ next: "GREENFIELD: /appweaver foundation — once per lane, to scaffold a compiling project into each source path declared here (skip it for a lane whose code already exists, or scaffold with the stack's own tooling) — then /appweaver select-domain → constitution → select-feature → specify. BROWNFIELD: /appweaver onboard — it reverse-engineers out of the source paths declared here and generates the project (no select-domain; onboard derives and activates it from the code). Run this workflow FIRST in either sequence: foundation unpacks into the source paths declared here, constitution reads the declaration to decide which halves to author, and onboard reads it to decide what to scan."
@@ -670,5 +670,5 @@ outputs:
670
670
  authored_document: "{{quality_target}}" # spec.md (bootstrap) | delta-spec.md (delta)
671
671
  checklist_file: "{{checklist_file}}"
672
672
  checklist_quality: "{{checklist_passed}}/{{checklist_total}} items passing"
673
- note: "Behaviour and structure were authored together: the spec says what the system must do, {{domain_model}} says what the domain IS. The aggregate root, its entities and its invariants (BR-n) live in the model and are cited from the spec by id — never restated there, because several specs act on one aggregate and a per-spec copy drifts. On the DELTA path neither master was touched: this run wrote changes/{{cr_id}}/delta-spec.md and {{delta_model}}, and that model delta sits BESIDE the master where every other open delta can see it while being authored. WAVE {{wave}} is its parallel-development level — deltas sharing a wave are independent, a higher wave is implemented only after the lower ones land; `cr-wave --cr-id {{cr_id}} --wave <n>` moves it. {{open_count}} change(s) are open on this feature."
673
+ note: "Behaviour and structure were authored together: the spec says what the system must do, {{domain_model}} says what the domain IS. The aggregate root, its entities and its invariants (BR-n) live in the model and are cited from the spec by id — never restated there, because several specs act on one aggregate and a per-spec copy drifts. On the DELTA path neither master was touched: this run wrote changes/{{cr_id}}/delta-spec.md and {{delta_model}}, and that model delta sits BESIDE the master where every other open delta can see it while being authored. WAVE {{wave}} is its parallel-development level — deltas sharing a wave are independent, a higher wave is implemented only after the lower ones land. The wave is DERIVED from the model delta's `## Depends on`: one above the highest delta named there, stamped into both halves as soon as it was authored ({{wave_message}}). To move it, correct that section and re-run `.appweaver/scripts/bash/aw-spec-actions.sh cr-wave-derive --cr-id {{cr_id}} --stamp`; finalize-delta refuses a change whose wave contradicts it. {{open_count}} change(s) are open on this feature."
674
674
  next: "/appweaver plan — BOOTSTRAP authors backend-plan.md from the spec + the domain model; a DELTA (this run opened change {{cr_id}}, wave {{wave}}) authors the delta plans for it. A change then flows: analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta → analyze-slice → close-delta (finalize-delta merges the delta into spec.md + domain-model.md — not the lane plans, which are derived from those two — but leaves the change open; analyze-slice verifies the slice against the merged master; close-delta archives the change and bumps the version). ⚠️ With more than one change open, plan, analyze-plan-delta and implement ASK which one they are for (pass the id as the argument to skip the prompt) and activate the answer; finalize-delta and close-delta still act on whichever change is active — so finish or close the others deliberately."