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
@@ -12,9 +12,26 @@
12
12
  # Interactive confirmations (proceed / overwrite / reference sample) ride the
13
13
  # checkpoint channel under EVERY strategy.
14
14
  #
15
+ # A SCAFFOLD LANDS IN A LANE. The output directory is the SOURCE PATH of the lane being
16
+ # scaffolded, read from .appweaver/stacks.json — never the repository root and never a
17
+ # directory named on the command line. One bundle is one lane's project, so a repo with a
18
+ # service and a UI is scaffolded by running this twice, once per lane, and each run's files
19
+ # land in the subtree that lane owns. That is what makes every later phase's "write only
20
+ # into your lane" rule true of the scaffold as well as of generated code: implement,
21
+ # build, test and run all resolve the same path this unpacked into.
22
+ #
23
+ # The lane's TEST PATH is its second subtree, and for most backend stacks it is a sibling
24
+ # of the source path rather than a directory inside it. A pack carrying its own test
25
+ # project is unpacked like everything else and then has that tree lifted onto the lane's
26
+ # test path — see .appweaver/scripts/python/aw-foundation.py for when that is a no-op.
27
+ #
28
+ # Lanes must therefore be declared FIRST: /appweaver select-stack is the sole writer of
29
+ # stacks.json and the first command of both sequences.
30
+ #
15
31
  # Launch via the parametric skill: /appweaver foundation [strategy]
16
- # Interactive (no args): LISTS the available foundations to pick from, then ASKS
17
- # for the app (project) name — both deterministic, no LLM. Fast path still works:
32
+ # Interactive (no args): ASKS which lane to scaffold, LISTS the foundations available
33
+ # for that lane's ecosystem, then ASKS for the app (project) name — all deterministic,
34
+ # no LLM. Fast path still works:
18
35
  # /appweaver foundation agent VerticalSlice AppWeaver.Health
19
36
  #
20
37
  # Headless:
@@ -25,14 +42,14 @@
25
42
  # =============================================================================
26
43
 
27
44
  name: aw-spec-foundation
28
- description: Scaffold a complete, compiling project — pick from the available foundation bundles and enter the app name, then unpack the verified bundle and rename its sentinel to your app name. Selection is zero-LLM (list + pick + ask).
29
- version: 1.1.0
45
+ description: Scaffold a complete, compiling project INTO A DECLARED LANE — pick the lane, pick from the foundation bundles available for its ecosystem, enter the app name, and the verified bundle is unpacked into that lane's source path with its sentinel renamed. Selection is zero-LLM (list + pick + ask).
46
+ version: 1.2.0
30
47
  agent_agnostic: true
31
48
  dialect: aw-spec.orchestrator/v1
32
49
 
33
50
  inputs:
34
51
  user_input:
35
- description: "Optional fast path: FoundationName [AppName] [OutputDirectory]. Omit to pick a foundation from the list and be asked for the app name."
52
+ description: "Optional fast path: FoundationName [AppName]. Omit to pick a foundation from the list and be asked for the app name. There is no output-directory argument — the scaffold goes into the chosen lane's source path."
36
53
  required: false
37
54
  bind: USER_INPUT
38
55
 
@@ -50,21 +67,83 @@ vars:
50
67
  stash_found: "false" # true only when the reference flow stashed a DbContext
51
68
  app: "" # resolved app name: fast-path 2nd token, else the ask below
52
69
  app_typed: "" # app name entered interactively (empty on the fast path)
70
+ out_ok: "true" # cleared when a third fast-path token names an output directory
53
71
 
54
72
  workflow:
55
73
 
56
- # 1a. LIST the available foundations (deterministic foundations/<ecosystem>/<Name>/<Name>.pack.txt
57
- # scan — no LLM), narrowed to the ecosystems the declared lanes belong to. A
58
- # bundle is a whole compiling project in one ecosystem's build system, so the
59
- # list a Spring repo is shown and the list a .NET repo is shown are different
60
- # lists; `all_foundations` keeps the unfiltered set for the gate message below.
61
- # A direct "Foundation App [out]" argument is parsed here too as an optional
62
- # fast path (preselect_name / preselect_app / out_dir).
74
+ # 0a. THE LANE MODEL, which is where this scaffold is going. Read-only: select-stack is
75
+ # the sole writer, and a scaffold has no business declaring the lane it fills.
76
+ - id: lanes
77
+ action: execute_script
78
+ on_error: continue # never crash raw on an absent stacks.json — the gate explains it
79
+ script: { ref: actions }
80
+ subcommand: lane-resolve
81
+ outputs:
82
+ - { name: lane_options, from: "$.lane_options" }
83
+ - { name: lane_count, from: "$.lane_count" }
84
+
85
+ - id: require_lanes
86
+ action: verify_context
87
+ require_non_empty: lane_options
88
+ on_empty: stop
89
+ on_empty_message: >
90
+ /appweaver foundation cannot run: no lane is declared in .appweaver/stacks.json.
91
+ A scaffold is unpacked into the SOURCE PATH of the lane it belongs to — that is what
92
+ keeps a project's files inside the subtree every later phase compiles, tests and
93
+ generates into — so there has to be a lane to scaffold before there is a scaffold.
94
+ Run /appweaver select-stack first: it declares each lane's stack and source path, and
95
+ is the first command of both the greenfield and the brownfield sequence.
96
+
97
+ # 0b. WHICH lane. One bundle is one lane's project, so a fullstack repo runs this once
98
+ # per lane rather than unpacking a tree that spans both. A single declared lane
99
+ # auto-confirms; the option carries its own explanation and resolves below.
100
+ - id: pick_lane
101
+ action: user_select
102
+ prompt: "Which lane is this scaffold for? The bundle is unpacked into that lane's source path, and its tests into the lane's test path — nothing lands outside them. Scaffold the other lane by running /appweaver foundation again."
103
+ options_from_variable: lane_options
104
+ single_option_confirm: true
105
+ outputs:
106
+ - { name: chosen_lane, from: selection }
107
+
108
+ # 0c. Resolve the chosen lane to the two paths this run writes into, plus the ecosystem
109
+ # that narrows the bundle list. `lane-resolve` strips the option's label itself, so
110
+ # the selection goes back in as the id it already carries.
111
+ - id: lane_target
112
+ action: execute_script
113
+ script: { ref: actions }
114
+ subcommand: lane-resolve
115
+ args: ["--id", "{{chosen_lane}}"]
116
+ outputs:
117
+ - { name: lane_found, from: "$.found" }
118
+ - { name: lane_id, from: "$.lane_id" }
119
+ - { name: lane_root, from: "$.source_path" }
120
+ - { name: lane_test_path, from: "$.test_path" }
121
+ - { name: lane_ecosystem, from: "$.ecosystem" }
122
+ - { name: lane_language, from: "$.language" }
123
+
124
+ - id: lane_resolved
125
+ action: verify_context
126
+ require_true: lane_found
127
+ on_false: stop
128
+ on_false_message: "No lane matches the selection `{{chosen_lane}}` in .appweaver/stacks.json. Re-run /appweaver select-stack to re-declare the lanes, then re-run foundation."
129
+
130
+ - id: lane_has_root
131
+ action: verify_context
132
+ require_non_empty: lane_root
133
+ on_empty: stop
134
+ on_empty_message: "Lane `{{lane_id}}` declares no source path in .appweaver/stacks.json, and the source path is where this scaffold is unpacked. Re-run /appweaver select-stack and give the lane the directory it owns."
135
+
136
+ # 1a. LIST the foundations available for THAT LANE's ecosystem (deterministic
137
+ # foundations/<ecosystem>/<Name>/<Name>.pack.txt scan — no LLM). A bundle is a whole
138
+ # compiling project in one ecosystem's build system, so a Node UI lane is not offered
139
+ # a .NET bundle even in a repo that also declares a .NET service; `all_foundations`
140
+ # keeps the unfiltered set for the gate message below. A direct "Foundation App"
141
+ # argument is parsed here too as an optional fast path.
63
142
  - id: list_foundations
64
143
  action: execute_script
65
144
  script: { ref: actions }
66
145
  subcommand: foundation-list
67
- args: ["--args", "{{USER_INPUT}}"]
146
+ args: ["--args", "{{USER_INPUT}}", "--ecosystem", "{{lane_ecosystem}}"]
68
147
  outputs:
69
148
  - { name: foundations, from: "$.foundations" }
70
149
  - { name: foundation_count, from: "$.count" }
@@ -72,23 +151,41 @@ workflow:
72
151
  - { name: lane_ecosystems, from: "$.lane_ecosystems" }
73
152
  - { name: preselect_name, from: "$.preselect_name" }
74
153
  - { name: preselect_app, from: "$.preselect_app" }
75
- - { name: out_dir, from: "$.out_dir" }
154
+ - { name: rejected_out, from: "$.rejected_out" }
155
+
156
+ # 1b. A third fast-path token used to be an output directory. Refused by name rather
157
+ # than ignored: silently dropping a path argument is how someone believes they
158
+ # scaffolded a sandbox and finds they filled their service lane.
159
+ - id: out_dir_given
160
+ action: set
161
+ condition: "{{rejected_out}} != "
162
+ vars: { out_ok: "" }
163
+
164
+ - id: no_out_dir
165
+ action: verify_context
166
+ require_true: out_ok
167
+ on_false: stop
168
+ on_false_message: >
169
+ `{{rejected_out}}` was given as an output directory, and foundation has none to give.
170
+ A scaffold is unpacked into the source path of the lane it is for — here lane
171
+ `{{lane_id}}` at `{{lane_root}}` — so that everything it writes is inside the subtree
172
+ that lane owns. Re-run as `/appweaver foundation {{preselect_name}} {{preselect_app}}`.
173
+ To scaffold somewhere else, declare a lane there with /appweaver select-stack and
174
+ pick that lane.
76
175
 
77
176
  - id: have_foundations
78
177
  action: verify_context
79
178
  require_non_empty: foundations
80
179
  on_empty: stop
81
180
  on_empty_message: >
82
- No foundation bundle is available for this repository. Bundles present:
83
- {{all_foundations}}; the ecosystem(s) this repository declares in
84
- .appweaver/stacks.json: {{lane_ecosystems}}. A foundation is a complete project in
85
- one ecosystem's languages and build system, so one from another ecosystem is not a
86
- scaffold for this repo — it is a second, foreign project beside it. Either add a
87
- bundle under foundations/<ecosystem>/<Name>/<Name>.pack.txt for your stack (see the
88
- pack scripts in that directory), or scaffold with the stack's own tooling (Spring
89
- Initializr, `npm create`, `cargo new`, …) and declare the result with
90
- /appweaver select-stack — implement generates INTO a project tree and does not
91
- require that the kit be the thing that created it.
181
+ No foundation bundle ships for the `{{lane_id}}` lane's ecosystem ({{lane_ecosystem}}).
182
+ Bundles present: {{all_foundations}}. A foundation is a complete project in one
183
+ ecosystem's languages and build system, so one from another ecosystem is not a
184
+ scaffold for this lane — it is a second, foreign project inside it. Either add a
185
+ bundle under foundations/{{lane_ecosystem}}/<Name>/<Name>.pack.txt (see the pack
186
+ scripts in that directory), or scaffold `{{lane_root}}` with the stack's own tooling
187
+ (Spring Initializr, `npm create`, `cargo new`, …) — implement generates INTO a
188
+ project tree and does not require that the kit be the thing that created it.
92
189
 
93
190
  # 1b. PICK the foundation. A matching fast-path arg auto-selects; a lone
94
191
  # foundation auto-confirms; otherwise the user chooses from the list.
@@ -128,14 +225,13 @@ workflow:
128
225
  action: execute_script
129
226
  script: { ref: actions }
130
227
  subcommand: foundation-locate
131
- args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{out_dir}}"]
228
+ args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}"]
132
229
  outputs:
133
230
  - { name: locate_ok, from: "$.ok" }
134
231
  - { name: locate_msg, from: "$.reason" }
135
232
  - { name: available, from: "$.available" }
136
233
  - { name: fname, from: "$.name" }
137
234
  - { name: app, from: "$.app" }
138
- - { name: out_dir, from: "$.out_dir" }
139
235
  - { name: sentinel, from: "$.sentinel" }
140
236
  - { name: file_count, from: "$.file_count" }
141
237
 
@@ -151,7 +247,9 @@ workflow:
151
247
  prompt: |
152
248
  Foundation: {{fname}} (sentinel {{sentinel}}, {{file_count}} files)
153
249
  App Name: {{app}}
154
- Output: {{out_dir}}
250
+ Lane: {{lane_id}} — {{lane_language}} ({{lane_ecosystem}})
251
+ Source: {{lane_root}} <- the whole bundle unpacks here
252
+ Tests: {{lane_test_path}} <- a test tree the bundle carries is lifted here
155
253
  Rename {{sentinel}} -> {{app}} (dotted/underscored/hyphen/lower forms).
156
254
  Unchanged: 'Projects' sample domain + AppWeaver.* framework packages. Proceed?
157
255
  options: ["yes", "no"]
@@ -169,7 +267,7 @@ workflow:
169
267
  action: execute_script
170
268
  script: { ref: actions }
171
269
  subcommand: foundation-unpack
172
- args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{out_dir}}", "--dry-run"]
270
+ args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--dry-run"]
173
271
  outputs:
174
272
  - { name: has_collisions, from: "$.has_collisions" }
175
273
 
@@ -193,9 +291,11 @@ workflow:
193
291
  action: execute_script
194
292
  script: { ref: actions }
195
293
  subcommand: foundation-unpack
196
- args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{out_dir}}", "--force", "{{overwrite}}"]
294
+ args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--test-path", "{{lane_test_path}}", "--force", "{{overwrite}}"]
197
295
  outputs:
198
- - { name: written, from: "$.written" }
296
+ - { name: written, from: "$.written" }
297
+ - { name: tests_moved, from: "$.tests_moved" }
298
+ - { name: tests_message, from: "$.tests_message" }
199
299
 
200
300
  # 5. Optional reference sample.
201
301
  - id: ask_reference
@@ -209,7 +309,7 @@ workflow:
209
309
  condition: "{{want_reference}} == yes"
210
310
  script: { ref: actions }
211
311
  subcommand: foundation-stash-dbcontext
212
- args: ["--out", "{{out_dir}}"]
312
+ args: ["--out", "{{lane_root}}"]
213
313
  outputs:
214
314
  - { name: stash_found, from: "$.found" }
215
315
  - { name: db_context, from: "$.dbcontext" }
@@ -220,7 +320,7 @@ workflow:
220
320
  condition: "{{want_reference}} == yes"
221
321
  script: { ref: actions }
222
322
  subcommand: foundation-unpack
223
- args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{out_dir}}", "--reference"]
323
+ args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--test-path", "{{lane_test_path}}", "--reference"]
224
324
  outputs:
225
325
  - { name: ref_written, from: "$.written" }
226
326
 
@@ -248,7 +348,7 @@ workflow:
248
348
  action: execute_script
249
349
  script: { ref: actions }
250
350
  subcommand: foundation-verify
251
- args: ["--out", "{{out_dir}}", "--sentinel", "{{sentinel}}"]
351
+ args: ["--out", "{{lane_root}}", "--sentinel", "{{sentinel}}"]
252
352
  outputs:
253
353
  - { name: clean, from: "$.clean" }
254
354
  - { name: residue, from: "$.residue" }
@@ -256,10 +356,14 @@ workflow:
256
356
  outputs:
257
357
  foundation: "{{fname}}"
258
358
  app: "{{app}}"
259
- output_dir: "{{out_dir}}"
359
+ lane: "{{lane_id}}"
360
+ source_path: "{{lane_root}}"
361
+ test_path: "{{lane_test_path}}"
260
362
  files_written: "{{written}}"
363
+ tests_moved: "{{tests_moved}}"
364
+ tests_note: "{{tests_message}}"
261
365
  reference_files: "{{ref_written}}"
262
366
  sentinel_clean: "{{clean}}"
263
367
  sentinel_residue: "{{residue}}"
264
- note: "Foundation scaffolded into {{out_dir}} ({{written}} file(s), sentinel renamed to {{app}}). Configure your AppWeaver.* NuGet feed and run the AppHost to confirm the scaffold starts before building on it."
265
- next: "/appweaver select-feature (pick the feature to build; every stage from specify onward reads that active feature and stops without it. If this workspace has governed projects under guardrails/projects/, run /appweaver select-domain first — it is one-time and optional.)"
368
+ note: "Foundation scaffolded into the {{lane_id}} lane at {{lane_root}} ({{written}} file(s), sentinel renamed to {{app}}); {{tests_message}}. Everything written is inside the subtree that lane owns, which is where implement generates and build/test/run act. Configure your AppWeaver.* NuGet feed and run the AppHost to confirm the scaffold starts before building on it."
369
+ next: "Another lane still to scaffold? Run /appweaver foundation again and pick it — one bundle fills one lane. Otherwise /appweaver select-feature (pick the feature to build; every stage from specify onward reads that active feature and stops without it. If this workspace has governed projects under guardrails/projects/, run /appweaver select-domain first — it is one-time and optional.)"