aw-spec-cli 0.1.26 → 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
@@ -9,7 +9,7 @@ makes) and `.appweaver/stack-profiles/*.json` (the candidates it chooses from).
9
9
 
10
10
  ## 1. What a lane is
11
11
 
12
- A **lane** is a technology target that owns disjoint source paths, compiles and tests
12
+ A **lane** is a technology target that owns its own lane root folder, compiles and tests
13
13
  independently, and carries its own technical rules and constitution half — while
14
14
  *sharing* the feature, the spec, the domain, the version, and the knowledge bundle with
15
15
  every other lane.
@@ -33,9 +33,10 @@ no heading to key on. Where the two lanes are folded into one command, as `plan`
33
33
  ```jsonc
34
34
  {
35
35
  "lanes": [
36
- { "id": "service", "kind": "backend", "profile": "dotnet-webapi", "root": "src/",
37
- "build": "dotnet build", "test": "dotnet test", "run": "dotnet run --project src/" },
38
- { "id": "web", "kind": "frontend", "profile": "react-vite", "root": "web/",
36
+ { "id": "service", "kind": "backend", "profile": "dotnet-webapi", "root": "backend/",
37
+ "run_from": "root",
38
+ "build": "dotnet build", "test": "dotnet test", "run": "dotnet run --project {{host}}" },
39
+ { "id": "web", "kind": "frontend", "profile": "react-vite", "root": "frontend/",
39
40
  "run_from": "root",
40
41
  "build": "npm run build", "test": "npm test", "run": "npm run dev" }
41
42
  ],
@@ -50,16 +51,16 @@ no heading to key on. Where the two lanes are folded into one command, as `plan`
50
51
  | `profile` | the stack profile the lane was declared from. Records the technology, so re-stacking a lane never renames the lane. `detected` where the kit ships no profile for what the code is |
51
52
  | `stack` | *(optional)* what the code was found to be, when `profile` is `detected` — e.g. `asp.net-webforms`. The honest record for a legacy lane |
52
53
  | `origin` | *(optional)* `reverse-engineered` on a lane `onboard` derived from an existing codebase. Absent means a person declared it, which is the greenfield shape and the historical one |
53
- | `root` | the lane's **source path** — the subtree it owns (see §3). `--source-path` and `--root` are two spellings of this one field |
54
+ | `root` | the **lane root folder** — the subtree the lane owns (see §3). `--source-path` and `--root` are two spellings of this one field |
54
55
  | `test_root` | *(optional)* the lane's **test path**, written by hand only when the profile's answer does not fit this layout. Absent, `lane-resolve` / `lane-verify` work it out from the profile at read time (§4.3), so no lane has to be re-declared for it |
55
- | `run_from` | *(optional)* **where the commands run**: `repo` (the default, and the shape of every lane written before this key existed) at the repository root, or `root` inside the lane's own source path. See §3.1 |
56
- | `build` / `test` / `run` | project-supplied command strings the engine shells out to. These are the matched **profile's** defaults and may not fit a legacy layout — reviewing them is what `select-stack` is for. `{{root}}` in a command resolves to this lane's `root` (§4.2) |
56
+ | `run_from` | *(optional)* **where the commands run**: `repo` (the default, and the shape of every lane written before this key existed) at the repository root, or `root` inside the lane root folder. `select-stack` decides it from where the stack's build file sits — see §3.1 |
57
+ | `build` / `test` / `run` | project-supplied command strings the engine shells out to. These are the matched **profile's** defaults and may not fit a legacy layout — reviewing them is what `select-stack` is for. `{{root}}` in a command resolves to this lane's `root`, as seen from where the command runs (§4.2) |
57
58
  | `migrate` | *(optional)* the lane's schema-migration command, recorded only when the profile declares one. Absent, `run-migrate` falls back to `dotnet ef` and otherwise reports that nothing ran (§4.2) |
58
59
 
59
60
  ### Three properties matter more than the schema
60
61
 
61
- 1. **`root` is ownership, and it is the source path.** `implement`'s service lane writes its
62
- code only under a backend lane's root and its frontend lane only under a frontend lane's,
62
+ 1. **`root` is ownership, and it is the lane root folder.** `implement`'s service lane writes its
63
+ code only under the backend lane root folder and its frontend lane only under the frontend one,
63
64
  `build`/`test`/`run` act on it, and `onboard` reverse-engineers out of it. The lane's
64
65
  **test path** (§4.3) is owned the same way, and is where `implement` writes the lane's tests.
65
66
  2. **The commands are project-supplied strings.** The engine stays stdlib-only and
@@ -71,33 +72,39 @@ no heading to key on. Where the two lanes are folded into one command, as `plan`
71
72
 
72
73
  ### 3.1 `root` is where the code is; `run_from` is where the commands run
73
74
 
74
- These are two questions and for most stacks they have two answers. `dotnet build` needs the
75
- solution file at the top while the lane owns `src/`; `go build ./...` needs the go.mod there
76
- while the lane owns `internal/`. Those profiles leave `run_from` at its default and run at
77
- the repository root.
78
-
79
- A stack whose **manifest lives inside the subtree** needs the other answer, and cannot work
80
- without it: a Maven wrapper at `backend/orderservice/mvnw` is unreachable from the repository
81
- root, so `java-spring` declares `run_from: "root"`. The five npm frontends and `blazor-wasm`
82
- declare it too, which is what let their commands stop hardcoding `--prefix web` — the lane
83
- already declares that directory, and a lane rooted at `client/` was previously building `web/`
84
- regardless.
75
+ These are two questions, and an existing repository can give them two answers: a classic
76
+ .NET repository keeps its solution file at the top while the lane owns `src/`, and a classic
77
+ Go repository keeps `go.mod` there while the lane owns `internal/`. Their commands have to run
78
+ at the repository root.
79
+
80
+ **A new project is one self-contained folder per lane.** Every backend profile defaults to
81
+ `backend/` and every frontend profile to `frontend/`, with `run_from: "root"`: the lane's
82
+ build files, source and tests all live inside its lane root folder, and its commands run there.
83
+ `foundation` builds exactly that — a pack unpacks into the lane root folder as packed, a spec build
84
+ creates the whole project inside it — and reports a lane that does not run its commands in its
85
+ own folder or keeps its tests outside it, rather than scaffolding a project its own build
86
+ cannot find.
87
+
88
+ **`select-stack` answers `run_from` from the disk.** Each backend profile lists the files its
89
+ commands need (`build_files`: `*.sln`, `pom.xml`, `go.mod`, `package.json`, …). When a lane is
90
+ declared, `lane-declare` looks for them: directly inside the lane root folder ⇒ `root`; only at the
91
+ repository root ⇒ `repo`; nowhere yet — a greenfield lane — ⇒ the profile's own answer, which is
92
+ inside the lane. So a classic repository keeps the shape it has, a Maven module whose `mvnw`
93
+ sits in the lane root folder runs there, and a new project gets the self-contained layout, from
94
+ one profile. `--run-from` overrides it, and so does editing `stacks.json`.
85
95
 
86
96
  Two consequences worth knowing:
87
97
 
88
- - **Declare a `run_from: "root"` lane at the directory holding its manifest**, not at its
89
- source directory. `java-spring`'s profile default root is `src/main/java/`, which is where
90
- the *code* goes; detection proposes the directory `pom.xml` actually sits in, which is the
91
- one to accept. A lane rooted at `src/main/java/` will fail loudly with `./mvnw: not found`
92
- rather than silently.
98
+ - **Declare a lane at the directory holding its build file** when the code already exists and
99
+ should run there — a Maven lane at the folder with `pom.xml`, not at `src/main/java/`.
93
100
  - **A single-module project declares `.`** — stored as `./`, the repository root. It owns
94
101
  everything, so it overlaps every other lane by definition and cannot be combined with one.
95
102
 
96
- ### Where the source path is checked against the disk
103
+ ### Where the lane root folder is checked against the disk
97
104
 
98
105
  `lane-declare` records a path; it does **not** require the path to exist. In a greenfield
99
- project it does not exist yet — the whole point of declaring `src/` on day one is that
100
- `implement` will create it.
106
+ project it does not exist yet — the whole point of declaring `backend/` on day one is that
107
+ `foundation` and `implement` will create it.
101
108
 
102
109
  `lane-verify` is the check, and it runs where the path has to be real: before `implement`,
103
110
  `build`, `run`, and before `onboard` reverse-engineers a codebase out of it. That split is what lets one file serve a project with no code yet and a project that is
@@ -108,7 +115,7 @@ Two failure classes, deliberately distinguished:
108
115
  - **MISSING** — the phase needs a lane of this kind and none is declared. Reported only for
109
116
  the kinds a caller names with `--require`, because "no frontend lane" is a correct and
110
117
  common state that must never fail a backend build.
111
- - **BROKEN** — a lane *is* declared and its source path is empty, is not a directory, or
118
+ - **BROKEN** — a lane *is* declared and its lane root folder is empty, is not a directory, or
112
119
  overlaps another lane's. Checked for **every** declared lane whether or not the caller
113
120
  asked about it. A stack named against a path that is not there is a mistake in every
114
121
  phase, and letting it through means discovering it as an empty build or a generator
@@ -118,11 +125,25 @@ Two failure classes, deliberately distinguished:
118
125
 
119
126
  ### The file ships empty, and may be filled in by hand
120
127
 
121
- The kit installs `.appweaver/stacks.json` holding `{"lanes": []}`, alongside `domain.json` and
128
+ The kit installs `.appweaver/stacks.json` with an empty `lanes` array, alongside `domain.json` and
122
129
  `feature.json`. It is there so a project can open it and fill it in directly — the block at
123
130
  the top of this section is the whole schema, and `select-stack` writes nothing a person
124
131
  could not have typed.
125
132
 
133
+ **The shipped file carries a sample under `_lane_format`**: one backend and one frontend lane
134
+ with every field above filled in (.NET and Next.js), plus a `prototype` block. No command
135
+ reads that key, and `select-stack` leaves it in place, so it is safe to keep. To fill the file
136
+ by hand, copy the lanes you need into `lanes` and edit them. The samples' optional fields hold
137
+ values that change nothing when copied (`stack` is the profile's own title, `origin: "declared"`
138
+ is the default, `test_root` is the profile's own answer), except `migrate`, which is a real
139
+ command: keep it only if the lane has migrations to run. The frontend sample's `migrate` is
140
+ empty because a Next.js lane has none.
141
+
142
+ **Never leave an empty or half-filled entry in `lanes`.** Every entry there is a declared lane,
143
+ whatever its values: `select-stack` replaces a lane only when its `id` matches, so it adds its
144
+ lane beside a blank one instead of replacing it, and `lane-verify` then stops every phase on the
145
+ blank lane's missing lane root folder.
146
+
126
147
  **An empty file and an absent one are the same state.** Shipping the file means its presence
127
148
  no longer says anything about whether a lane was declared, so `declared` reports on the
128
149
  `lanes` array and never on the file: `resolve` reports `exists: true, declared: false` for
@@ -131,7 +152,7 @@ the file is missing, naming which of the two it found.
131
152
 
132
153
  Hand-editing and `select-stack` are the same file and the same schema, so they interleave
133
154
  freely: fill in the service lane by hand and run `select-stack` later to add the UI one, or
134
- run it first and correct a source path in the editor. What hand-editing gives up is the
155
+ run it first and correct a lane root folder in the editor. What hand-editing gives up is the
135
156
  checking `select-stack` does on the way in — an unknown `profile`, a missing `root`, and two
136
157
  lanes whose roots overlap are all refused there and merely written here. `lane-verify` still
137
158
  catches the last two before any phase acts on them; a profile id that matches nothing
@@ -152,7 +173,7 @@ Declaring **`(none)`** for a lane is a real answer and the way a project says it
152
173
 
153
174
  ## 3. The ownership rule
154
175
 
155
- **Lane roots own their subtree and must be disjoint.** Two generators must never be able
176
+ **Lane root folders own their subtree and must be disjoint.** Two generators must never be able
156
177
  to write the same file. A lane owns its test path too, so the rule covers both: no source or
157
178
  test path of one lane may overlap a source or test path of another.
158
179
 
@@ -182,12 +203,19 @@ workflow.**
182
203
  "title": "React + Vite", // shown in the menu
183
204
  "language": "TypeScript", // shown in the menu, parenthesised
184
205
  "ecosystem": "node", // which stack-partitioned corpus this stack draws from
185
- "root": "web/", // the lane root this profile defaults to
206
+ "root": "frontend/", // the folder a NEW lane of this stack gets — see §3.1
186
207
  "test_root": "", // optional — where tests go when not inside root — see §4.3
187
- "run_from": "root", // where the commands run — see §3.1
208
+ "run_from": "root", // where the commands run for a new lane — see §3.1
209
+ "conventional_root": "web/", // optional — where this stack's code usually sits in an
210
+ // EXISTING repository; `detect` is written against it
211
+ "conventional_test_root": "", // optional — the test path that pairs with it — see §4.3
212
+ "build_files": [], // optional — the files its commands need (`*.sln`,
213
+ // `go.mod`); decides `run_from` at declare time — §3.1
188
214
  "build": "npm run build",
189
215
  "test": "npm test",
190
216
  "run": "npm run dev",
217
+ "host_patterns": [], // optional — where the runnable project sits, for
218
+ // `{{host}}` in a command — see §4.2
191
219
  "migrate": "", // optional — schema migration, if the stack has one
192
220
  "prototype_run": "npm run dev --prefix prototype", // frontend only — fills the prototype block
193
221
  "reserved_names": ["Event", "Request", "Response"], // optional — names this stack's AMBIENT
@@ -209,49 +237,96 @@ trees that are physically per-ecosystem:
209
237
 
210
238
  | Tree | Keyed by | Read through |
211
239
  |---|---|---|
212
- | `foundations/<ecosystem>/<Name>/` | `ecosystem` | `foundation-list`, `foundation-locate` |
240
+ | `foundations/<ecosystem>/<Name>/` | `ecosystem`, then each pack/spec's `# kind:` / `# profiles:` header | `foundation-list`, `foundation-locate` |
213
241
  | `foundations/<ecosystem>/components/<C>/` | `ecosystem` | `list-components`, `add-component` |
214
242
 
215
243
  **Why those two are filtered and the rule corpora are not.** A foundation bundle is a whole
216
244
  compiling project and a component runbook edits a host project's build files — both are
217
245
  executable artifacts in one toolchain, so offering a `dotnet` bundle to a Spring repo does not
218
246
  produce a scaffold, it produces a second foreign project beside the real one. `foundation-list`
219
- therefore narrows by ecosystem, and reports `all_foundations` alongside so the gate can say
220
- *what exists and why none of it applies*. `--ecosystem` narrows to the ONE lane being
247
+ therefore narrows by ecosystem, and reports `all_foundations` alongside so the pick list can say
248
+ *what exists and how much of it applies*. `--ecosystem` narrows to the ONE lane being
221
249
  scaffolded, which is what `foundation` passes: a repo declaring a .NET service and a Node UI
222
250
  must not be offered its .NET bundle while the UI lane is the target. Without that argument the
223
251
  filter falls back to every declared lane's ecosystem, and a repo that has declared nothing yet
224
252
  is not filtered — there is no answer to filter on.
225
253
 
226
- **A scaffold lands in a lane.** `foundation` unpacks a bundle into the SOURCE PATH of the lane
227
- it is scaffolding — never the repository root, and there is no output-directory argument. One
228
- bundle fills one lane, so a fullstack repo runs `foundation` once per lane. This is what makes
229
- every phase's "write only into your lane" rule true of the scaffold as well as of generated
230
- code: `implement`, `build`, `test` and `run` all resolve the same path the bundle unpacked into.
231
- A pack carrying its own test tree has it lifted onto the lane's **test path** (§4.3) afterwards,
232
- because for most backend stacks that path is a sibling of the source path rather than a
233
- directory inside it. Lanes must therefore be declared before `foundation` runs, which is why
234
- `select-stack` is the first command of both sequences.
254
+ **Ecosystem alone is not narrow enough for a lane**, because it is shared across kinds: the
255
+ five JavaScript frontends and `node-express` are all `node`, and `blazor-wasm` shares `dotnet`
256
+ with the service stacks. So each pack and spec may open with header lines that `foundation-list`
257
+ filters on as well — `# kind: backend|frontend` (absent = backend, the kind every pack shipped
258
+ before the key existed scaffolds) and `# profiles: <id>, …` (absent = any profile of that kind).
259
+
260
+ **Packs and specs.** A directory under `foundations/<ecosystem>/` may hold a pack
261
+ (`<Name>.pack.txt` — a verified project unpacked byte-for-byte), any number of specs
262
+ (`*.spec.txt` — a description an agent builds the project from), or both; each is its own
263
+ entry in the lane's pick list, after which come `[manual spec]` (a spec given at the prompt)
264
+ and `[skip]`. `foundations/foundation.spec.template.txt` is the shape of a spec. A spec build
265
+ loads the technical rules the active project maps under `## /aw-spec-foundation-backend` or
266
+ `## /aw-spec-foundation-frontend` (§7), and the spec it used is kept at
267
+ `.appweaver/foundation/<lane>.spec.md`.
268
+
269
+ **A scaffold lands in a lane.** `foundation` scaffolds into the FOLDER of each lane — its
270
+ lane root folder, never a directory named on the command line — and the lane is self-contained
271
+ (§3.1): a pack unpacks there exactly as packed, build files, source and tests together, and a
272
+ spec build creates the whole project there and may write nowhere else — checked, not trusted:
273
+ the repo is recorded before the build and compared after it, and a build that created, changed
274
+ or deleted anything outside the lane root folder is "NOT accepted", with what it created there
275
+ removed again and what it changed or deleted listed for the operator. One run covers every
276
+ declared lane, backend then frontend, and each lane gets its own pick. A lane that does not run
277
+ its commands in its lane root folder, or keeps its tests outside it, is reported and passed over. A
278
+ lane that already holds files — any file in its source or test path — is asked "Overwrite?"
279
+ before its pick list; "no" skips the lane, and "yes" replaces same-path files and deletes
280
+ nothing. A pack's reference sample goes on top of the base project the same way. The one
281
+ exception is a pack's **rule file** (`<pack name>.rules.json` beside the pack): the files and
282
+ folders it names with `merge` are combined with the file already there, by an agent following
283
+ the rule's own instruction, instead of being replaced — the .NET reference samples use it to
284
+ merge their DbContext into the base project's. The format is in
285
+ [PACKAGING.md](../guardrails/technology/dotnet-webapi/PACKAGING.md#the-rule-file-pack-namerulesjson).
286
+ This is what makes every phase's "write only into your lane" rule true of the scaffold
287
+ as well as of generated code: `implement`, `build`, `test` and `run` all resolve the same
288
+ paths. Lanes must therefore be declared before `foundation` runs, which is why `select-stack`
289
+ is the first command of both sequences.
235
290
 
236
291
  The technical RULE corpora at `guardrails/technology/<stack>/` are keyed by **profile id**, not
237
292
  ecosystem, because Spring and Quarkus are both `java` and do not share a persistence rule. They
238
293
  are selected through the project's `tech-rule-bindings.md` rather than filtered here — see §7.
239
294
 
240
- ### 4.2 `run_from`, `{{root}}` and `migrate`
295
+ ### 4.2 `run_from`, `{{root}}`, `{{host}}` and `migrate`
241
296
 
242
- A profile command sometimes has to name the source path — `dotnet run --project src/`. Written
297
+ A profile command sometimes has to name the lane root folder — `python -m compileall app`. Written
243
298
  literally that is the profile's *default* root frozen into the command, and it stays frozen when
244
299
  a lane declares a different one. Write **`{{root}}`** instead and it resolves to the lane's own
245
- root, expanded at run time so a hand-edited `root` in `stacks.json` stays consistent with it:
300
+ root as seen from where the command runs, expanded at run time so a hand-edited `root` in
301
+ `stacks.json` stays consistent with it:
246
302
 
247
303
  ```jsonc
248
- "run": "dotnet run --project {{root}}", // Services/Api/ ⇒ dotnet run --project Services/Api
249
- "build": "python -m compileall {{root}}" // repo-root lane ⇒ python -m compileall .
304
+ "build": "python -m compileall {{root}}" // app/, run at the repo root ⇒ python -m compileall app
305
+ // a run_from: root lane ⇒ python -m compileall .
306
+ ```
307
+
308
+ Some commands need a *project* inside the lane root folder rather than the folder itself.
309
+ `dotnet run --project <folder>` needs exactly one project in that folder, and a solution has
310
+ several — a pack's runnable one is `src/<App>.AppHost`, the .NET Aspire host. Write
311
+ **`{{host}}`** and it resolves to that project's folder, from the profile's `host_patterns`:
312
+ paths relative to the lane root folder, best first, `**/` for any depth, case ignored. The
313
+ first pattern that matches exactly one file names the project; build output, dependency and
314
+ test folders are never searched. It is looked up when the command is read, so a project
315
+ scaffolded after the lane was declared is found, and with no single match `{{host}}` falls
316
+ back to `{{root}}`:
317
+
318
+ ```jsonc
319
+ "run": "dotnet run --project {{host}}",
320
+ "host_patterns": ["**/*.AppHost.csproj", "**/*.Api.csproj", "**/*.Service.csproj"]
321
+ // backend/ holding src/Acme.AppHost/ and src/Acme.Service/ ⇒ dotnet run --project src/Acme.AppHost
322
+ // backend/ holding src/Acme.Api/ and src/Acme.Domain/ ⇒ dotnet run --project src/Acme.Api
250
323
  ```
251
324
 
252
325
  A command with no placeholder is used verbatim, so every literal command already on disk is
253
- unaffected. `{{root}}` is the answer when the command needs the *path*; `run_from: "root"` (§3.1)
254
- is the answer when it needs the *working directory*.
326
+ unaffected. A lane keeps the command it was declared with: one declared before `{{host}}` was
327
+ in its profile picks it up when select-stack declares it again. `{{root}}` and `{{host}}` are
328
+ the answer when the command needs a *path*; `run_from: "root"` (§3.1) is the answer when it
329
+ needs the *working directory*.
255
330
 
256
331
  `migrate` is an optional fourth phase command, alongside `build`/`test`/`run`. **No shipped
257
332
  profile declares one**, deliberately: Flyway, Liquibase, Alembic, Prisma and golang-migrate all
@@ -263,17 +338,21 @@ elsewhere rather than implying a migration succeeded.
263
338
  `detect` is an **ANY-of** list, not an all-of: `java-spring` names both `pom.xml` and
264
339
  `build.gradle` because a project has one or the other, and requiring both would make it
265
340
  undetectable. `stack-detect` scores a profile by how many of its patterns hit. Patterns are
266
- matched first as written (repo-root-relative, with `**` spanning separators), then relocated
267
- under any directory prefix — so a profile shipping `web/package.json` still recognises the
268
- same stack in `client/` or `apps/portal/`, which a brownfield repo that never saw our
341
+ written against the layout an EXISTING repository of the stack usually has — the profile's
342
+ `conventional_root` (`web/package.json`, `src/**/*.csproj`) — and matched first as written
343
+ (repo-root-relative, with `**` spanning separators), then relocated under any directory prefix,
344
+ with that root segment substituted — so a profile shipping `web/package.json` still recognises
345
+ the same stack in `client/` or `apps/portal/`, which a brownfield repo that never saw our
269
346
  profiles will need. An anchored hit outranks a relocated one.
270
347
 
271
348
  `detect` and `root` are independent, and deliberately so: `go-chi` detects on `go.mod` at the
272
- repo root and declares a root of `internal/`. Detection reports the shallowest directory its
273
- evidence actually sits in, and that directory is what `select-stack` offers first when it asks
274
- for the lane's source path — ahead of the profile's own default, because a repo that already
275
- exists did not choose its layout from our profiles. The operator picks from the list or types
276
- a path the kit has never seen.
349
+ repo root, keeps its code under `internal/` in an existing repository, and gives a new lane
350
+ `backend/`. Detection reports the shallowest directory its evidence actually sits in; evidence
351
+ only at the repository root (`App.sln`, `go.mod`) defers to the `conventional_root` when that
352
+ directory is there, then to `root`. That directory is what `select-stack` offers first when it
353
+ asks for the lane root folder — ahead of the profile's own default, because a repo that
354
+ already exists did not choose its layout from our profiles. The operator picks from the list or
355
+ types a path the kit has never seen.
277
356
 
278
357
  **Detection leads the menu; it never fills `preselect`.** The engine treats `preselect_from`
279
358
  as the answer, ahead of a headless `--select`, so filling it from a scan would let a repo's
@@ -323,34 +402,37 @@ Absent, nothing is reported and generation is unchanged.
323
402
 
324
403
  ### 4.3 `test_root` — where a lane's tests go
325
404
 
326
- Most backend stacks keep their tests **beside** the source, not inside it: .NET's `tests/` next
327
- to `src/`, Maven's `src/test/java/` next to `src/main/java/`, pytest's `tests/` next to `app/`.
328
- A lane that owned only its source path could not hold its own tests, so each lane also owns a
329
- **test path**, and `implement` writes the lane's paired tests there.
405
+ In an existing repository most backend stacks keep their tests **beside** the source, not
406
+ inside it: .NET's `tests/` next to `src/`, Maven's `src/test/java/` next to `src/main/java/`,
407
+ pytest's `tests/` next to `app/`. A new, self-contained lane keeps them inside its lane root folder
408
+ (`backend/tests/`). Either way each lane owns a **test path**, and `implement` writes the lane's
409
+ paired tests there.
330
410
 
331
- A profile's `test_root` is the test path that pairs with its default `root`. `lane-resolve` and
411
+ A profile's `test_root` is the test path that pairs with its default `root`, and its
412
+ `conventional_test_root` the one that pairs with its `conventional_root`. `lane-resolve` and
332
413
  `lane-verify` report it per kind as `backend_test_path` / `frontend_test_path`, worked out at
333
414
  read time:
334
415
 
335
416
  | The lane | Its test path |
336
417
  |---|---|
337
418
  | declares `test_root` itself | that value |
338
- | sits at the profile's default root (`src/`) | the profile's `test_root` (`tests/`) |
339
- | sits at a path ending in the default root (`backend/src/`) | the profile's `test_root` under the same prefix (`backend/tests/`) |
340
- | anything else, or the profile declares no `test_root` | the source path itself |
341
-
342
- The last row is right for every shipped frontend (tests under `web/tests/`), for Go's
343
- colocated `_test.go` files, and for a Maven lane rooted at the directory holding its
344
- `pom.xml`, where `src/test/java/` is already inside the lane. A layout the table gets wrong is
345
- fixed by writing `test_root` on the lane in `stacks.json`.
346
-
347
- | Profile | `root` | `test_root` |
348
- |---|---|---|
349
- | `dotnet-webapi` | `src/` | `tests/` |
350
- | `java-spring` | `src/main/java/` | `src/test/java/` |
351
- | `python-fastapi` | `app/` | `tests/` |
352
- | `node-express` | `src/` | `tests/` |
353
- | `go-chi` and every frontend | — | *(none — inside the source path)* |
419
+ | sits at the profile's default root (`backend/`) | the profile's `test_root` (`backend/tests/`) |
420
+ | sits at a path ending in the default root (`apps/backend/`) | the profile's `test_root` under the same prefix (`apps/backend/tests/`) |
421
+ | sits at — or at a path ending in — the conventional root (`src/`, `svc/src/main/java/`) | the `conventional_test_root`, the same way (`tests/`, `svc/src/test/java/`) |
422
+ | anything else, or the profile declares neither | the lane root folder itself |
423
+
424
+ The last row is right for every shipped frontend (tests under `frontend/tests/`), for Go's
425
+ colocated `_test.go` files, and for a lane at any other folder name, whose tests are inside it.
426
+ A layout the table gets wrong is fixed by writing `test_root` on the lane in `stacks.json`.
427
+
428
+ | Profile | `root` | `test_root` | `conventional_root` | `conventional_test_root` |
429
+ |---|---|---|---|---|
430
+ | `dotnet-webapi` | `backend/` | `backend/tests/` | `src/` | `tests/` |
431
+ | `java-spring` | `backend/` | `backend/src/test/java/` | `src/main/java/` | `src/test/java/` |
432
+ | `python-fastapi` | `backend/` | `backend/tests/` | `app/` | `tests/` |
433
+ | `node-express` | `backend/` | `backend/tests/` | `src/` | `tests/` |
434
+ | `go-chi` | `backend/` | *(inside the lane root folder)* | `internal/` | — |
435
+ | every frontend | `frontend/` | *(inside the lane root folder)* | `web/` | — |
354
436
 
355
437
  ---
356
438
 
@@ -390,11 +472,12 @@ re-runs the roots-disjoint one on every read (see §2).
390
472
 
391
473
  Consequences worth stating, because they are the reason for the design:
392
474
 
393
- - **`foundation` / `ui-foundation` scaffold *against* a declared lane** and verify what
394
- they produced against it — stopping `STACK-MISMATCH:` rather than rewriting the
395
- declaration to fit the scaffold. A wrong declaration surfaces at the scaffold instead of
396
- being absorbed by it, and the lane a project *chose* stays the authority over the lane a
397
- generator *happened to produce*.
475
+ - **`foundation` scaffolds *into* a declared lane** and never rewrites the declaration to
476
+ fit what it produced. Each lane is scaffolded into its own source and test paths, and a
477
+ spec build must leave a project of the lane's own stack inside the lane — otherwise that
478
+ lane is reported "NOT usable" and the run moves on to the next one. A wrong declaration
479
+ surfaces at the scaffold instead of being absorbed by it, and the lane a project *chose*
480
+ stays the authority over the lane a generator *happened to produce*.
398
481
  - **`build` runs every declared lane's own build command**, in declaration order, and
399
482
  fails if any lane fails. Without a declaration it falls back to the historic
400
483
  first-match-wins sniffer, which is right for one lane and silently skips the second —
@@ -411,7 +494,7 @@ follows from it:
411
494
  | Which rule sets does `onboard` generate? | the same, per lane — `service/` and `frontend/` under the project folder |
412
495
  | Which subtree does `implement` write into? | that lane's `root` |
413
496
  | What does `build` compile, and `run` migrate? | every declared lane's own command, against its own root |
414
- | What does `onboard` scan? | the declared source paths — backend signals from outside the UI subtree, frontend signals from inside it |
497
+ | What does `onboard` scan? | the declared lane root folders — backend signals from outside the UI subtree, frontend signals from inside it |
415
498
 
416
499
  This is a change of authority, and worth being explicit about. The question "does this
417
500
  project have a UI lane" used to be answered by OR-ing three heuristics: a
@@ -441,13 +524,15 @@ constraint where it previously had none. That is a deliberate trade: what it buy
441
524
  every later phase can ask this file a question and get an answer, instead of inferring one.
442
525
 
443
526
  ```
444
- GREENFIELD select-stack → select-domain → constitution → select-feature → specify → …
527
+ GREENFIELD select-stack → constitution → [select-feature] → specify → …
445
528
  BROWNFIELD select-stack → onboard → select-feature → adopt → …
446
529
  ```
447
530
 
448
- `select-domain` is greenfield-only: `onboard` derives the project from the code and
449
- activates it. On an existing codebase `select-stack` is mostly confirmation — it detects the
450
- stack and the source path from the repo's own files and leads the menu with them — but it is
531
+ Neither sequence selects a project. Greenfield uses `guardrails/project-specific/`, which
532
+ `.appweaver/domain.json` names as shipped; `onboard` creates `guardrails/brf-<domain>/` from
533
+ the code and points `domain.json` at it. The optional `select-domain` switches `domain.json` to
534
+ another project folder under `guardrails/` that already exists. On an existing codebase `select-stack` is mostly confirmation — it detects the
535
+ stack and the lane root folder from the repo's own files and leads the menu with them — but it is
451
536
  a person's confirmation, which is what detection on a legacy repo needs.
452
537
 
453
538
  ## 6. Subcommands
@@ -460,7 +545,7 @@ implementation and the twins cannot drift.
460
545
  |---|---|
461
546
  | `stack-list [--kind k] [--preselect text] [--include-none] [--suggest]` | enumerate the installed profiles; returns `select_options[]`, and `preselect` when the text names one of *this kind's* ids. `--suggest` also runs detection, so the menu **leads with** the stack this repo's files point at and `root_options[]` leads with the directory the evidence sits in |
462
547
  | `stack-detect [--kind k] [--exclude-root p]…` | match the repo's OWN files against the installed profiles — the brownfield candidate list, since an existing repo already answered the question. Writes nothing. `--exclude-root` keeps a service profile from relocating onto a subtree another lane owns |
463
- | `lane-declare --profile <id\|label\|none\|detected> [--kind k] [--id lane] [--source-path\|--root p] [--stack label] [--origin declared\|reverse-engineered] [--build/--test/--run/--migrate c] [--run-from repo\|root] [--no-prototype] [--require-existing-root]` | upsert a lane — **the sole writer**. `detected` declares a stack the kit ships no profile for (needs `--stack` and a source path), which is how a legacy WebForms, JSP or ERB lane gets recorded at all |
548
+ | `lane-declare --profile <id\|label\|none\|detected> [--kind k] [--id lane] [--source-path\|--root p] [--stack label] [--origin declared\|reverse-engineered] [--build/--test/--run/--migrate c] [--run-from repo\|root] [--no-prototype] [--require-existing-root]` | upsert a lane — **the sole writer**. `detected` declares a stack the kit ships no profile for (needs `--stack` and a lane root folder), which is how a legacy WebForms, JSP or ERB lane gets recorded at all |
464
549
  | `lane-resolve [--kind k] [--id lane]` | read; exit 0 always. `exists` reports the file, `declared` reports the lanes — the shipped-but-unfilled seed is `exists:true, declared:false`. Returns the matched lane **and** both kinds flattened — `backend_source_path`, `frontend_root_exists`, `backend_build` … — so a workflow binds one variable instead of calling twice |
465
550
  | `lane-verify [--require backend\|frontend]… [--allow-missing-root] [--command phase]` | **the gate**: is the declaration usable for the phase about to run. Never writes, and **always exits 0** — the calling workflow's `verify_context` owns the stop, so the reason arrives as text |
466
551
  | `lane-probe [--kind k] [--hint text]` | read; the **file** signals for "does this lane exist" — a declared lane, and a stack named in free prose. Retained for callers that want a soft hint; the authoritative answer is the declaration, read through `lane-resolve` or `lane-verify` |
@@ -474,10 +559,11 @@ set**, so a caller binds outputs without knowing which branch ran.
474
559
  |---|---|---|
475
560
  | `build-compile` | every declared lane's own `build`, via `run-phase` | the historic first-match-wins ladder |
476
561
  | `test-run` | every declared lane's own `test`, via `run-phase` — **except** `--scope slice` on a repo where a .NET toolchain is present, which keeps the VSTest `--filter` path | the historic ladder |
477
- | `run-migrate` | a lane's declared `migrate` (§4.2) when there is one; otherwise `dotnet ef` searched under the **service lane's root** rather than an assumed `src/` | `dotnet ef` under `src/` |
478
- | `find-host-project` | searches the service lane's root, and reports the lane's `ecosystem` | searches `src/` |
479
- | `foundation-list`, `list-components` | narrowed to the `ecosystem` of the lane being scaffolded, else of every declared lane (§4.1) | unfiltered / `dotnet` |
480
- | `foundation-unpack` | unpacks into the lane's **source path**, and lifts a packed test tree onto its **test path** (§4.1, §4.3) | cannot run — `foundation` stops until a lane is declared |
562
+ | `run-migrate` | a lane's declared `migrate` (§4.2) when there is one; otherwise `dotnet ef` searched under the **service lane root folder** rather than an assumed `src/` | `dotnet ef` under `src/` |
563
+ | `find-host-project` | searches the service lane root folder, and reports the lane's `ecosystem` | searches `src/` |
564
+ | `foundation-list`, `list-components` | narrowed to the `ecosystem` of the lane being scaffolded, else of every declared lane; `foundation-list` also by each entry's `# kind:` / `# profiles:` (§4.1) | unfiltered / `dotnet` |
565
+ | `foundation-unpack` | unpacks into the **lane root folder**, and lifts a packed test tree onto its **test path** (§4.1, §4.3) | cannot run — `foundation` stops until a lane is declared |
566
+ | `build-compile --kind` | that one lane's own `build` — what `foundation` runs after a spec build | the ladder; `--kind` is ignored |
481
567
  | `resolve-tech` | expands stack placeholders and flags a corpus mismatch (§7a) | no placeholders resolve; nothing is flagged |
482
568
 
483
569
  **Why `--scope slice` is the one exception.** VSTest's `--filter FullyQualifiedName~` is the
@@ -498,16 +584,17 @@ No workflow or script changes. If adding a stack ever requires touching a workfl
498
584
  model is wrong and that is the bug to fix.
499
585
 
500
586
  That gives the new stack every phase that is driven by a **command** — build, test, run,
501
- migrate — plus the correct source path everywhere, immediately. Two things are opt-in because
587
+ migrate — plus the correct lane root folder everywhere, immediately. Two things are opt-in because
502
588
  they are content the kit cannot invent:
503
589
 
504
590
  - **Technical rules** — a corpus at `guardrails/technology/<id>/`, mapped from the project's
505
591
  `tech-rule-bindings.md` (§7a). Until one exists the stack runs with the universal
506
592
  `ai-rules.md` and the technical constitution, which is the greenfield-safe default, not an
507
593
  error.
508
- - **A foundation bundle** — `foundations/<ecosystem>/<Name>/`, only if you want `/appweaver
509
- foundation` to scaffold this stack. Scaffolding with the stack's own tooling and then
510
- declaring the result with `select-stack` is fully supported: `implement` generates INTO a
594
+ - **A foundation pack or spec** — `foundations/<ecosystem>/<Name>/`, only if you want
595
+ `/appweaver foundation` to offer one for this stack (give it `# kind:` / `# profiles:`
596
+ header lines). Without one, `foundation` still offers `[manual spec]` for the lane, and
597
+ scaffolding with the stack's own tooling is fully supported: `implement` generates INTO a
511
598
  project tree and does not require that the kit created it.
512
599
 
513
600
  ## 7a. Mapping rules to a stack without pinning the map to one
@@ -1 +1 @@
1
- {"domain":""}
1
+ {"domain":"project-specific"}
@@ -9,11 +9,27 @@ for that agent; the engine that consumes it is the *general* file in `.appweaver
9
9
  ```
10
10
  .appweaver/orchestrator/orchestrator.py ← general engine (reads a profile)
11
11
  .appweaver/orchestrator/adapters/*.py ← shared SDK adapters (one per SDK, not per agent)
12
- .claude/agent.json ← Claude Code profile (claude -p / claude-agent-sdk)
13
- .agents/agent.json ← Antigravity profile (agy -p / google-antigravity)
14
- .kilocode/agent.json ← Kilo Code profile (delegates to claude)
12
+ .claude/agent.json ← Claude Code profile (claude -p / claude-agent-sdk)
13
+ .agents/agent.json ← Antigravity profile (agy -p / google-antigravity)
14
+ .kilo/agent.json ← Kilo Code profile (kilo run / claude-agent-sdk)
15
+ .cursor/agent.json ← Cursor profile (cursor-agent -p)
16
+ .copilot/agent.json ← GitHub Copilot profile (copilot -p)
17
+ .devin/agent.json ← Devin profile (devin -p)
18
+ .cline/agent.json ← Cline profile (cline)
19
+ .roo/agent.json ← Roo Code profile (roo --print)
20
+ .opencode/agent.json ← opencode profile (opencode run)
21
+ .factory/agent.json ← Factory Droid profile (droid exec)
22
+ .augment/agent.json ← Augment profile (auggie --print)
23
+ .continue/agent.json ← Continue profile (cn -p)
24
+ .kiro/agent.json ← Kiro profile (kiro-cli chat)
25
+ .codex/agent.json ← OpenAI Codex profile (codex exec)
26
+ .gemini/agent.json ← Gemini CLI profile (gemini -p)
27
+ .amp/agent.json ← Amp profile (amp -x)
15
28
  ```
16
29
 
30
+ Only the first three have an `sdk` block; the rest support the `agent` and
31
+ `spawn` strategies.
32
+
17
33
  The launcher passes its profile with `--agent-profile <path>` (which implies
18
34
  `--transport real`).
19
35
 
@@ -60,7 +76,7 @@ options). Adapters are keyed by SDK, not by agent:
60
76
  `adapters/google_antigravity.py` maps the neutral permission modes onto
61
77
  Antigravity policies and drives `Agent` / `LocalAgentConfig` (used by
62
78
  `.agents`), while `adapters/claude_agent_sdk.py` drives `query` /
63
- `ClaudeAgentOptions` (shared by `.claude` and `.kilocode`, whose SDK is the
79
+ `ClaudeAgentOptions` (shared by `.claude` and `.kilo`, whose SDK is the
64
80
  same) — none of which the engine knows about; it only follows the profile's
65
81
  `sdk.adapter` pointer.
66
82
 
@@ -87,11 +103,23 @@ When `system` is `"prepend"`, the system block is folded into the prompt
87
103
  ## Adding a new agent
88
104
 
89
105
  1. Drop an `agent.json` at that agent's root (`.<agent>/agent.json`) describing its CLI.
90
- 2. Add a launcher in that agent's native format that calls
91
- `orchestrator.py … --agent-profile .<agent>/agent.json`.
92
- 3. For the `sdk`/`sdk-apply` strategies, add an `sdk` block and point
106
+ 2. Add the agent's row to the profile table in `.appweaver/orchestrator/LAUNCHER.md`.
107
+ The launchers in folders several agents read (`.claude/skills/`, `.agents/skills/`)
108
+ name no agent; an agent loading one of them finds its profile in that table.
109
+ 3. Add a launcher in that agent's native format that points at `LAUNCHER.md` and
110
+ names `.<agent>/agent.json` as the agent's profile — `.cursor/commands/appweaver.md`
111
+ is the shortest example.
112
+ 4. For the `sdk`/`sdk-apply` strategies, add an `sdk` block and point
93
113
  `sdk.adapter` at the matching adapter in `.appweaver/orchestrator/adapters/`
94
114
  — or drop a new adapter there first if that SDK has none yet.
95
-
96
- No engine change required. Verify with `--dry-run` (prints the exact command
97
- without executing).
115
+ 5. Add the agent's folder (and its launcher folder, if different) to `KIT_AGENT_DIRS`
116
+ in `.appweaver/scripts/python/aw-inventory.py`, so a brownfield inventory does not
117
+ read the kit's files as the project's own. When the launcher sits in a folder the
118
+ project also uses (as `.github/` holds its CI), add the launcher's exact path to
119
+ `KIT_AGENT_FILES` instead.
120
+
121
+ No engine change required. Verify with `--dry-run` on `_smoke.orch.yaml`
122
+ (`--non-interactive --strategy spawn --agent-profile .<agent>/agent.json --dry-run`):
123
+ it prints the exact command the profile builds for every model step — the
124
+ read-only `agent_query`, `agent_author` and `agent_review`, and the `agent_generate`
125
+ edit step — without executing any of them.