aw-spec-cli 0.1.25 → 0.1.27

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 (98) hide show
  1. package/assets/.agents/skills/appweaver/SKILL.md +7 -47
  2. package/assets/.appweaver/ORCH-DIALECT.md +13 -9
  3. package/assets/.appweaver/RULE-PARTITION.md +3 -3
  4. package/assets/.appweaver/STACK-PROFILES.md +188 -101
  5. package/assets/.appweaver/domain.json +1 -1
  6. package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +38 -10
  7. package/assets/.appweaver/orchestrator/LAUNCHER.md +88 -0
  8. package/assets/.appweaver/orchestrator/README.md +120 -53
  9. package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +1 -1
  10. package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +6 -4
  11. package/assets/.appweaver/orchestrator/modules/foundation-lane.orch.yaml +237 -0
  12. package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +349 -0
  13. package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +381 -0
  14. package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +4 -4
  15. package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +3 -3
  16. package/assets/.appweaver/orchestrator/orchestrator.py +162 -63
  17. package/assets/.appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md +2 -2
  18. package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +2 -2
  19. package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +5 -5
  20. package/assets/.appweaver/orchestrator/rev-templates/frontend-rule-file.skeleton.md +1 -1
  21. package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +14 -14
  22. package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +2 -2
  23. package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +1 -1
  24. package/assets/.appweaver/orchestrator/rev-templates/ui-constitution.skeleton.md +3 -3
  25. package/assets/.appweaver/orchestrator/workflows/_smoke.orch.yaml +41 -3
  26. package/assets/.appweaver/orchestrator/workflows/aw-spec-add-component.orch.yaml +1 -1
  27. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +13 -13
  28. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +3 -3
  29. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +3 -3
  30. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
  31. package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +2 -2
  32. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +61 -12
  33. package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +172 -293
  34. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +29 -28
  35. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +53 -53
  36. package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
  37. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +3 -3
  38. package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +4 -4
  39. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +26 -2
  40. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +21 -15
  41. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +14 -14
  42. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +2 -2
  43. package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +3 -3
  44. package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +6 -6
  45. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +284 -128
  46. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +224 -70
  47. package/assets/.appweaver/scripts/python/aw-foundation.py +809 -20
  48. package/assets/.appweaver/scripts/python/aw-inventory.py +28 -21
  49. package/assets/.appweaver/scripts/python/aw-plan-coverage.py +2 -2
  50. package/assets/.appweaver/scripts/python/aw-rev-constitution.py +6 -6
  51. package/assets/.appweaver/scripts/python/aw-stack.py +195 -89
  52. package/assets/.appweaver/stack-profiles/angular.json +2 -1
  53. package/assets/.appweaver/stack-profiles/blazor-wasm.json +2 -1
  54. package/assets/.appweaver/stack-profiles/dotnet-webapi.json +16 -3
  55. package/assets/.appweaver/stack-profiles/go-chi.json +6 -1
  56. package/assets/.appweaver/stack-profiles/java-spring.json +12 -5
  57. package/assets/.appweaver/stack-profiles/nextjs.json +2 -1
  58. package/assets/.appweaver/stack-profiles/node-express.json +8 -2
  59. package/assets/.appweaver/stack-profiles/python-fastapi.json +9 -2
  60. package/assets/.appweaver/stack-profiles/react-vite.json +2 -1
  61. package/assets/.appweaver/stack-profiles/sveltekit.json +2 -1
  62. package/assets/.appweaver/stack-profiles/vue-vite.json +2 -1
  63. package/assets/.appweaver/stacks.json +37 -1
  64. package/assets/.appweaver/templates/constitution-ui-template.md +2 -2
  65. package/assets/.claude/skills/appweaver/SKILL.md +7 -46
  66. package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.Reference.rules.json +31 -0
  67. package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.Reference.rules.json +16 -0
  68. package/assets/foundations/foundation.spec.template.txt +73 -0
  69. package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +1 -1
  70. package/assets/foundations/java/Hexagonal/unpack-foundation.sh +4 -5
  71. package/assets/foundations/node/NextAppRouter/NextAppRouter.spec.txt +68 -0
  72. package/assets/guardrails/README.md +31 -17
  73. package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain-rule-bindings.md +5 -5
  74. package/assets/guardrails/{projects/dotnet-default-project → project-specific}/tech-rule-bindings.md +151 -114
  75. package/assets/guardrails/technology/dotnet-webapi/PACKAGING.md +58 -4
  76. package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +1 -1
  77. package/assets/guardrails/technology/dotnet-webapi/dependency-policy.md +1 -1
  78. package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
  79. package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +1 -1
  80. package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
  81. package/assets/guardrails/technology/nextjs/api-client.md +1 -1
  82. package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
  83. package/assets/guardrails/technology/nextjs/component-conventions.md +2 -2
  84. package/assets/guardrails/technology/nextjs/design-system.md +1 -1
  85. package/assets/guardrails/technology/nextjs/frontend-testing.md +1 -1
  86. package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
  87. package/assets/guardrails/technology/nextjs/ui-constitution.md +3 -3
  88. package/package.json +1 -1
  89. package/assets/.agents/skills/appweaver-demo/SKILL.md +0 -59
  90. package/assets/.appweaver/orchestrator/.smoke-out/smoke.md +0 -6
  91. package/assets/.appweaver/orchestrator/workflows/aw-spec-demo.orch.yaml +0 -100
  92. package/assets/.claude/skills/appweaver-demo/SKILL.md +0 -59
  93. package/assets/.kilocode/agent.json +0 -28
  94. package/assets/.kilocode/workflows/appweaver-demo.md +0 -59
  95. package/assets/.kilocode/workflows/appweaver.md +0 -55
  96. /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain/domain-requirements.md +0 -0
  97. /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/frontend/ui-requirements.md +0 -0
  98. /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/service/technical-requirements.md +0 -0
@@ -1,38 +1,48 @@
1
1
  # =============================================================================
2
- # aw-spec-foundation (orchestrator) — scaffold a project from a foundation bundle
2
+ # aw-spec-foundation (orchestrator) — scaffold every declared lane, backend then frontend
3
3
  #
4
- # Almost entirely deterministic script orchestration (unpack the verified
5
- # byte-for-byte bundle, rename the sentinel); the model is used ONLY for the
6
- # optional DbContext merge when the reference sample is included. That merge
7
- # is an `agent_author` (decl `llm_generate` port): generation is READ-ONLY
8
- # under every strategy and the ENGINE writes the merged file, so sdk-apply
9
- # degrades to sdk. No standards/domain/hooks — pure scaffolding, so the merge
10
- # runs with include_standards: false and a narrow two-file context.
4
+ # ONE RUN, EVERY LANE. The lanes come from .appweaver/stacks.json (select-stack is its sole
5
+ # writer). The run takes the backend lane first and the frontend lane second. For each, a
6
+ # lane that already holds files is asked about first — "Overwrite?", where "no" skips that
7
+ # lane and nothing else is asked — and then the run lists what the lane can be built from
8
+ # and the operator picks:
11
9
  #
12
- # Interactive confirmations (proceed / overwrite / reference sample) ride the
13
- # checkpoint channel under EVERY strategy.
10
+ # [pack] a verified, compiling project unpacked byte-for-byte into the lane's
11
+ # lane root folder with its sentinel renamed -> modules/foundation-pack
12
+ # [spec] a foundations/<ecosystem>/<Name>/*.spec.txt describing the project; an
13
+ # agent builds it in the lane -> modules/foundation-spec
14
+ # [manual spec] the same, from a file path or text given at the prompt
15
+ # [skip] leave the lane as it is
14
16
  #
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.
17
+ # A lane kind that is not declared is reported and passed over; a lane that is declined,
18
+ # skipped or fails is reported and the run moves on to the next one — the per-lane
19
+ # modules RETURN, they never stop the run. Each lane's outcome is in the run's outputs,
20
+ # which is where the operator reads it.
22
21
  #
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.
22
+ # A SCAFFOLD LANDS IN A LANE. A lane is one self-contained folder: a pack unpacks into it
23
+ # exactly as packed — build files, source and tests together — and a spec build creates
24
+ # the whole project inside it and may write nowhere else. The lane runs its commands in
25
+ # that folder and keeps its tests there; a lane declared the classic way (code in src/,
26
+ # solution and tests/ at the repository root) is reported and passed over, since a
27
+ # project built inside src/ would not be found by a build run at the root. That is what
28
+ # keeps every later phase's "write only into your lane" rule true of the scaffold as well
29
+ # as of generated code: implement, build, test and run all resolve the same paths.
27
30
  #
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.
31
+ # WHAT A LANE IS OFFERED. Bundles are filtered by the lane's ecosystem, then by the
32
+ # `# kind:` and `# profiles:` header lines of each pack/spec — an ecosystem is shared by
33
+ # stacks that cannot use each other's scaffold (Next.js, Angular and the Express backend
34
+ # are all `node`). A pack or spec with no `# kind:` line is a backend one.
35
+ #
36
+ # A SPEC BUILD FOLLOWS THE PROJECT'S TECHNICAL RULES. They are mapped per kind in the
37
+ # active project's tech-rule-bindings.md (the folder under guardrails/ that
38
+ # .appweaver/domain.json names — project-specific by default) under
39
+ # `## /aw-spec-foundation-backend` and `## /aw-spec-foundation-frontend`, and load
40
+ # through tech-select.
30
41
  #
31
42
  # Launch via the parametric skill: /appweaver foundation [strategy]
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:
35
- # /appweaver foundation agent VerticalSlice AppWeaver.Health
43
+ # Fast path: /appweaver foundation agent VerticalSlice AppWeaver.Health
44
+ # — the name preselects its entry in whichever lane lists it; the app name is used for
45
+ # every lane. There is no output-directory argument.
36
46
  #
37
47
  # Headless:
38
48
  # python3 .appweaver/orchestrator/orchestrator.py \
@@ -42,14 +52,14 @@
42
52
  # =============================================================================
43
53
 
44
54
  name: aw-spec-foundation
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
55
+ description: Scaffold every declared lane in one run — backend, then frontend. A lane that already has files is asked whether to overwrite them first (no = skip that lane). For each lane pick a verified pack (unpacked into its lane root folder, sentinel renamed), a spec the agent builds the project from (a bundled *.spec.txt or one you give), or skip it. Spec builds follow the technical rules mapped under /aw-spec-foundation-backend and /aw-spec-foundation-frontend.
56
+ version: 2.0.0
47
57
  agent_agnostic: true
48
58
  dialect: aw-spec.orchestrator/v1
49
59
 
50
60
  inputs:
51
61
  user_input:
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."
62
+ description: "Optional fast path: FoundationName [AppName]. The name preselects that pack or spec in the lane that lists it; the app name is used for every lane. Omit to pick per lane and be asked for the app name once."
53
63
  required: false
54
64
  bind: USER_INPUT
55
65
 
@@ -62,25 +72,52 @@ standards:
62
72
  - { path: .appweaver/ai-rules.md } # kit-resident: the universal operating rules, every flow
63
73
 
64
74
  vars:
65
- overwrite: "no" # flipped by ask_overwrite only when the dry run finds collisions
66
- ref_written: "0" # stays 0 when the reference sample is declined
67
- stash_found: "false" # true only when the reference flow stashed a DbContext
68
- app: "" # resolved app name: fast-path 2nd token, else the ask below
69
- app_typed: "" # app name entered interactively (empty on the fast path)
75
+ app: "" # resolved app name: fast-path 2nd token, else asked by the first lane that needs it
76
+ app_typed: "" # app name entered interactively
70
77
  out_ok: "true" # cleared when a third fast-path token names an output directory
78
+ be_status: "not run"
79
+ be_route: ""
80
+ be_name: ""
81
+ be_spec: ""
82
+ be_files: ""
83
+ fe_status: "not run"
84
+ fe_route: ""
85
+ fe_name: ""
86
+ fe_spec: ""
87
+ fe_files: ""
71
88
 
72
89
  workflow:
73
90
 
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.
91
+ # 0a. THE LANE MODEL. Read-only: select-stack is the sole writer, and a scaffold has no
92
+ # business declaring the lane it fills. Both kinds come back flattened
93
+ # (backend_* / frontend_*), so one call feeds both sections below.
76
94
  - id: lanes
77
95
  action: execute_script
78
96
  on_error: continue # never crash raw on an absent stacks.json — the gate explains it
79
97
  script: { ref: actions }
80
98
  subcommand: lane-resolve
81
99
  outputs:
82
- - { name: lane_options, from: "$.lane_options" }
83
- - { name: lane_count, from: "$.lane_count" }
100
+ - { name: lane_options, from: "$.lane_options" }
101
+ - { name: backend_declared, from: "$.backend_declared" }
102
+ - { name: backend_lane_id, from: "$.backend_lane_id" }
103
+ - { name: backend_profile, from: "$.backend_profile" }
104
+ - { name: backend_title, from: "$.backend_title" }
105
+ - { name: backend_language, from: "$.backend_language" }
106
+ - { name: backend_ecosystem, from: "$.backend_ecosystem" }
107
+ - { name: backend_source_path, from: "$.backend_source_path" }
108
+ - { name: backend_test_path, from: "$.backend_test_path" }
109
+ - { name: backend_workdir, from: "$.backend_workdir" }
110
+ - { name: backend_build, from: "$.backend_build" }
111
+ - { name: frontend_declared, from: "$.frontend_declared" }
112
+ - { name: frontend_lane_id, from: "$.frontend_lane_id" }
113
+ - { name: frontend_profile, from: "$.frontend_profile" }
114
+ - { name: frontend_title, from: "$.frontend_title" }
115
+ - { name: frontend_language, from: "$.frontend_language" }
116
+ - { name: frontend_ecosystem, from: "$.frontend_ecosystem" }
117
+ - { name: frontend_source_path, from: "$.frontend_source_path" }
118
+ - { name: frontend_test_path, from: "$.frontend_test_path" }
119
+ - { name: frontend_workdir, from: "$.frontend_workdir" }
120
+ - { name: frontend_build, from: "$.frontend_build" }
84
121
 
85
122
  - id: require_lanes
86
123
  action: verify_context
@@ -88,74 +125,27 @@ workflow:
88
125
  on_empty: stop
89
126
  on_empty_message: >
90
127
  /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.
142
- - id: list_foundations
128
+ A scaffold goes into the LANE ROOT FOLDER of the lane it belongs to — that is what keeps a
129
+ project's files inside the subtree every later phase compiles, tests and generates
130
+ into — so there has to be a lane before there is a scaffold. Run
131
+ /appweaver select-stack first: it declares each lane's stack and lane root folder, and is
132
+ the first command of both the greenfield and the brownfield sequence.
133
+
134
+ # 0b. The fast path, parsed once for the whole run: the app name it carries applies to
135
+ # every lane. --ecosystem "" filters the listing to nothing — only the parse is wanted.
136
+ - id: parse_args
143
137
  action: execute_script
144
138
  script: { ref: actions }
145
139
  subcommand: foundation-list
146
- args: ["--args", "{{USER_INPUT}}", "--ecosystem", "{{lane_ecosystem}}"]
140
+ args: ["--args", "{{USER_INPUT}}", "--ecosystem", ""]
147
141
  outputs:
148
- - { name: foundations, from: "$.foundations" }
149
- - { name: foundation_count, from: "$.count" }
150
- - { name: all_foundations, from: "$.all_foundations" }
151
- - { name: lane_ecosystems, from: "$.lane_ecosystems" }
152
- - { name: preselect_name, from: "$.preselect_name" }
153
- - { name: preselect_app, from: "$.preselect_app" }
154
- - { name: rejected_out, from: "$.rejected_out" }
142
+ - { name: preselect_name, from: "$.preselect_name" }
143
+ - { name: preselect_app, from: "$.preselect_app" }
144
+ - { name: rejected_out, from: "$.rejected_out" }
155
145
 
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.
146
+ # A third fast-path token used to be an output directory. Refused by name rather than
147
+ # ignored: silently dropping a path argument is how someone believes they scaffolded a
148
+ # sandbox and finds they filled their service lane.
159
149
  - id: out_dir_given
160
150
  action: set
161
151
  condition: "{{rejected_out}} != "
@@ -167,207 +157,96 @@ workflow:
167
157
  on_false: stop
168
158
  on_false_message: >
169
159
  `{{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.
160
+ Each lane is scaffolded into its own lane root folder from .appweaver/stacks.json, so that
161
+ everything written is inside the subtree that lane owns. Re-run without it. To
162
+ scaffold somewhere else, declare a lane there with /appweaver select-stack.
175
163
 
176
- - id: have_foundations
177
- action: verify_context
178
- require_non_empty: foundations
179
- on_empty: stop
180
- on_empty_message: >
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.
189
-
190
- # 1b. PICK the foundation. A matching fast-path arg auto-selects; a lone
191
- # foundation auto-confirms; otherwise the user chooses from the list.
192
- - id: pick_foundation
193
- action: user_select
194
- prompt: "Select a foundation to scaffold from:"
195
- options_from_variable: foundations
196
- preselect_from: preselect_name
197
- single_option_confirm: true
198
- outputs:
199
- - { name: fname, from: selection }
200
-
201
- # 1c. ASK for the app (project) name — deterministic free-text, no LLM. Skipped
202
- # when the fast-path arg already supplied it (preselect_app non-empty).
203
- - id: ask_app
204
- action: ask
205
- condition: "{{preselect_app}} == "
206
- prompt: >
207
- Enter the project (application) name — it replaces the foundation's sentinel
208
- across the generated tree (dotted / underscored / hyphen / lower forms),
209
- e.g. AppWeaver.Health:
210
- answer_var: app_typed
211
-
212
- # 1d. Coalesce the app name (engine has no compound conditions → two ordered
213
- # sets): default to the typed answer, then override with the fast-path token.
214
- - id: app_from_typed
215
- action: set
216
- vars: { app: "{{app_typed}}" }
217
164
  - id: app_from_arg
218
165
  action: set
219
166
  condition: "{{preselect_app}} != "
220
167
  vars: { app: "{{preselect_app}}" }
221
168
 
222
- # 1e. Locate the bundle/unpacker for the CHOSEN foundation + read the (bounded)
223
- # pack header (sentinel + file_count shown in the confirm below).
224
- - id: locate
225
- action: execute_script
226
- script: { ref: actions }
227
- subcommand: foundation-locate
228
- args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}"]
229
- outputs:
230
- - { name: locate_ok, from: "$.ok" }
231
- - { name: locate_msg, from: "$.reason" }
232
- - { name: available, from: "$.available" }
233
- - { name: fname, from: "$.name" }
234
- - { name: app, from: "$.app" }
235
- - { name: sentinel, from: "$.sentinel" }
236
- - { name: file_count, from: "$.file_count" }
237
-
238
- - id: locate_gate
239
- action: verify_context
240
- require_true: locate_ok
241
- on_false: stop
242
- on_false_message: "{{locate_msg}} (available foundations: {{available}})"
243
-
244
- # 2. Confirm the scaffold plan (checkpoint kind 'ask' under every strategy).
245
- - id: confirm
246
- action: ask
247
- prompt: |
248
- Foundation: {{fname}} (sentinel {{sentinel}}, {{file_count}} files)
249
- App Name: {{app}}
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
253
- Rename {{sentinel}} -> {{app}} (dotted/underscored/hyphen/lower forms).
254
- Unchanged: 'Projects' sample domain + AppWeaver.* framework packages. Proceed?
255
- options: ["yes", "no"]
256
- answer_var: proceed
257
-
258
- - id: confirm_gate
259
- action: verify_context
260
- require_value: proceed
261
- equals: "yes"
262
- on_mismatch: stop
263
- on_mismatch_message: "Cancelled by user."
264
-
265
- # 3. Dry-run to surface collisions, then confirm overwrite if needed.
266
- - id: dry_run
267
- action: execute_script
268
- script: { ref: actions }
269
- subcommand: foundation-unpack
270
- args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--dry-run"]
271
- outputs:
272
- - { name: has_collisions, from: "$.has_collisions" }
273
-
274
- - id: ask_overwrite
275
- action: ask
276
- condition: "{{has_collisions}} == true"
277
- prompt: "Output files already exist. Overwrite them?"
278
- options: ["yes", "no"]
279
- answer_var: overwrite
280
-
281
- - id: overwrite_gate
282
- action: verify_context
283
- condition: "{{has_collisions}} == true"
284
- require_value: overwrite
285
- equals: "yes"
286
- on_mismatch: stop
287
- on_mismatch_message: "Aborted — output files exist and overwrite was declined."
288
-
289
- # 4. Unpack for real.
290
- - id: unpack
291
- action: execute_script
292
- script: { ref: actions }
293
- subcommand: foundation-unpack
294
- args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--test-path", "{{lane_test_path}}", "--force", "{{overwrite}}"]
295
- outputs:
296
- - { name: written, from: "$.written" }
297
- - { name: tests_moved, from: "$.tests_moved" }
298
- - { name: tests_message, from: "$.tests_message" }
299
-
300
- # 5. Optional reference sample.
301
- - id: ask_reference
302
- action: ask
303
- prompt: "Include the reference sample project (Projects, Users, Reports slices)?"
304
- options: ["yes", "no"]
305
- answer_var: want_reference
306
-
307
- - id: stash_dbcontext
308
- action: execute_script
309
- condition: "{{want_reference}} == yes"
310
- script: { ref: actions }
311
- subcommand: foundation-stash-dbcontext
312
- args: ["--out", "{{lane_root}}"]
313
- outputs:
314
- - { name: stash_found, from: "$.found" }
315
- - { name: db_context, from: "$.dbcontext" }
316
- - { name: db_backup, from: "$.backup" }
317
-
318
- - id: reference_unpack
319
- action: execute_script
320
- condition: "{{want_reference}} == yes"
321
- script: { ref: actions }
322
- subcommand: foundation-unpack
323
- args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--test-path", "{{lane_test_path}}", "--reference"]
324
- outputs:
325
- - { name: ref_written, from: "$.written" }
326
-
327
- # 6. Merge the foundation DbContext (usings + DbSet<>) back over the reference
328
- # one. agent_author: read-only generation, the ENGINE writes the target.
329
- - id: merge_dbcontext
330
- action: agent_author
331
- condition: "{{stash_found}} == true"
332
- context: ["{{db_backup}}", "{{db_context}}"]
333
- include_standards: false
334
- task: |
335
- The target file ({{db_context}}) currently holds the REFERENCE DbContext
336
- (sample DbSet<> properties, no OnModelCreating). The backup ({{db_backup}})
337
- holds the FOUNDATION DbContext (full OnModelCreating, no DbSet<>). Produce a
338
- single merged ApplicationDbContext:
339
- - Base = the FOUNDATION file (backup).
340
- - Add any `using` directives from the reference that are missing.
341
- - Insert every reference `public DbSet<…>` line as a block right after the
342
- class opening brace (before OnModelCreating), preserving any comment above them.
343
- - Keep everything else in the foundation file unchanged.
344
- output_target: { type: file, path: "{{db_context}}" }
345
-
346
- # 7. Verify (sentinel residue must be zero in the generated tree).
347
- - id: verify
348
- action: execute_script
349
- script: { ref: actions }
350
- subcommand: foundation-verify
351
- args: ["--out", "{{lane_root}}", "--sentinel", "{{sentinel}}"]
352
- outputs:
353
- - { name: clean, from: "$.clean" }
354
- - { name: residue, from: "$.residue" }
169
+ # 1. BACKEND. Not declared -> say so and move on to the frontend.
170
+ - id: backend_absent
171
+ action: set
172
+ condition: "{{backend_declared}} != true"
173
+ vars:
174
+ be_status: "no backend lane is declared in .appweaver/stacks.json — nothing scaffolded for a service; run /appweaver select-stack to add one"
175
+
176
+ - id: backend
177
+ action: include
178
+ condition: "{{backend_declared}} == true"
179
+ module: .appweaver/orchestrator/modules/foundation-lane.orch.yaml
180
+ with:
181
+ fl_kind: "backend"
182
+ fl_lane_id: "{{backend_lane_id}}"
183
+ fl_profile: "{{backend_profile}}"
184
+ fl_title: "{{backend_title}}"
185
+ fl_language: "{{backend_language}}"
186
+ fl_ecosystem: "{{backend_ecosystem}}"
187
+ fl_source_path: "{{backend_source_path}}"
188
+ fl_test_path: "{{backend_test_path}}"
189
+ fl_workdir: "{{backend_workdir}}"
190
+ fl_build: "{{backend_build}}"
191
+
192
+ - id: backend_result
193
+ action: set
194
+ condition: "{{backend_declared}} == true"
195
+ vars:
196
+ be_status: "{{fl_status}}"
197
+ be_route: "{{fl_route}}"
198
+ be_name: "{{fl_name}}"
199
+ be_spec: "{{fl_spec_path}}"
200
+ be_files: "{{fl_files}}"
201
+
202
+ # 2. FRONTEND. Not declared -> say so; the run ends after this section either way.
203
+ - id: frontend_absent
204
+ action: set
205
+ condition: "{{frontend_declared}} != true"
206
+ vars:
207
+ fe_status: "no frontend lane is declared in .appweaver/stacks.json — nothing scaffolded for a UI; run /appweaver select-stack to add one"
208
+
209
+ - id: frontend
210
+ action: include
211
+ condition: "{{frontend_declared}} == true"
212
+ module: .appweaver/orchestrator/modules/foundation-lane.orch.yaml
213
+ with:
214
+ fl_kind: "frontend"
215
+ fl_lane_id: "{{frontend_lane_id}}"
216
+ fl_profile: "{{frontend_profile}}"
217
+ fl_title: "{{frontend_title}}"
218
+ fl_language: "{{frontend_language}}"
219
+ fl_ecosystem: "{{frontend_ecosystem}}"
220
+ fl_source_path: "{{frontend_source_path}}"
221
+ fl_test_path: "{{frontend_test_path}}"
222
+ fl_workdir: "{{frontend_workdir}}"
223
+ fl_build: "{{frontend_build}}"
224
+
225
+ - id: frontend_result
226
+ action: set
227
+ condition: "{{frontend_declared}} == true"
228
+ vars:
229
+ fe_status: "{{fl_status}}"
230
+ fe_route: "{{fl_route}}"
231
+ fe_name: "{{fl_name}}"
232
+ fe_spec: "{{fl_spec_path}}"
233
+ fe_files: "{{fl_files}}"
355
234
 
356
235
  outputs:
357
- foundation: "{{fname}}"
236
+ backend: "{{be_status}}"
237
+ frontend: "{{fe_status}}"
358
238
  app: "{{app}}"
359
- lane: "{{lane_id}}"
360
- source_path: "{{lane_root}}"
361
- test_path: "{{lane_test_path}}"
362
- files_written: "{{written}}"
363
- tests_moved: "{{tests_moved}}"
364
- tests_note: "{{tests_message}}"
365
- reference_files: "{{ref_written}}"
366
- sentinel_clean: "{{clean}}"
367
- sentinel_residue: "{{residue}}"
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."
239
+ backend_route: "{{be_route}}"
240
+ backend_foundation: "{{be_name}}"
241
+ backend_spec: "{{be_spec}}"
242
+ backend_files: "{{be_files}}"
243
+ frontend_route: "{{fe_route}}"
244
+ frontend_foundation: "{{fe_name}}"
245
+ frontend_spec: "{{fe_spec}}"
246
+ frontend_files: "{{fe_files}}"
247
+ note: "Each lane line above says what was scaffolded into it, or why nothing was. Everything written is inside the lane that owns it, which is where implement generates and build/test/run act. A spec build's spec is kept at .appweaver/foundation/<lane>.spec.md. Run /appweaver foundation again to scaffold a lane that was skipped — answer no to the overwrite question for the lane that is already done."
369
248
  # constitution is named here because specify declares constitution.domain.md a
370
249
  # REQUIRED context file. It announces rather than stops when the file is absent,
371
250
  # so a sequence that skips this step produces a spec authored with no domain
372
251
  # frame and only a [context] MISSING line to show for it.
373
- next: "Another lane still to scaffold? Run /appweaver foundation again and pick it — one bundle fills one lane. Otherwise carry on with /appweaver select-domain → constitution → select-feature → specify. select-domain binds this workspace to a governed project under guardrails/projects/ and is one-time; skip it only where no such project exists. /appweaver constitution is NOT optional — it authors the constitution halves, and specify declares constitution.domain.md as REQUIRED context while plan and implement read constitution.technical.md (plus constitution.ui.md on a frontend lane); it also needs the lane declaration select-stack wrote, which is why it runs after this. /appweaver select-feature then picks the feature to build — every stage from specify onward reads that active feature and stops without it."
252
+ next: "/appweaver constitution → specify. /appweaver constitution is NOT optional — it authors the constitution halves, and specify declares constitution.domain.md as REQUIRED context while plan and implement read constitution.technical.md (plus constitution.ui.md on a frontend lane); it reads the domain rule map of the project .appweaver/domain.json names (guardrails/project-specific/ by default). On a project with no specs yet, constitution also creates the default bounded context MainContext and its feature CoreFeature and makes both active, so specify can run straight after it; to use your own names, run /appweaver select-bounded-context <Name> and then /appweaver select-feature <Name> before specify. Every stage from specify onward reads the active feature and stops without it, and once any bounded context exists select-feature refuses to run until one is selected."