aw-spec-cli 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/.agents/skills/appweaver/SKILL.md +7 -47
- package/assets/.appweaver/ORCH-DIALECT.md +13 -9
- package/assets/.appweaver/RULE-PARTITION.md +11 -12
- package/assets/.appweaver/STACK-PROFILES.md +260 -108
- package/assets/.appweaver/domain.json +1 -1
- package/assets/.appweaver/interpreter/run-skill.py +28 -7
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +38 -10
- package/assets/.appweaver/orchestrator/LAUNCHER.md +88 -0
- package/assets/.appweaver/orchestrator/README.md +120 -53
- package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +17 -7
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +6 -4
- package/assets/.appweaver/orchestrator/modules/foundation-lane.orch.yaml +237 -0
- package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +392 -0
- package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +548 -0
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/orchestrator.py +162 -63
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +5 -4
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +3 -1
- package/assets/.appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +21 -13
- package/assets/.appweaver/orchestrator/rev-templates/frontend-rule-file.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +28 -31
- package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-constitution.skeleton.md +3 -3
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +4 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +6 -4
- package/assets/.appweaver/orchestrator/workflows/_smoke.orch.yaml +41 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-add-component.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +17 -16
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +7 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +7 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +61 -12
- package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +172 -293
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +253 -44
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +72 -71
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +38 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +26 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +21 -15
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +14 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +6 -6
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +371 -131
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +293 -76
- package/assets/.appweaver/scripts/python/aw-foundation.py +885 -20
- package/assets/.appweaver/scripts/python/aw-inventory.py +28 -21
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +2 -2
- package/assets/.appweaver/scripts/python/aw-rev-constitution.py +6 -6
- package/assets/.appweaver/scripts/python/aw-stack.py +676 -99
- package/assets/.appweaver/stack-profiles/angular.json +2 -1
- package/assets/.appweaver/stack-profiles/blazor-wasm.json +2 -1
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +18 -3
- package/assets/.appweaver/stack-profiles/go-chi.json +6 -1
- package/assets/.appweaver/stack-profiles/java-spring.json +14 -5
- package/assets/.appweaver/stack-profiles/nextjs.json +2 -1
- package/assets/.appweaver/stack-profiles/node-express.json +8 -2
- package/assets/.appweaver/stack-profiles/python-fastapi.json +9 -2
- package/assets/.appweaver/stack-profiles/react-vite.json +2 -1
- package/assets/.appweaver/stack-profiles/sveltekit.json +2 -1
- package/assets/.appweaver/stack-profiles/vue-vite.json +2 -1
- package/assets/.appweaver/stacks.json +40 -1
- package/assets/.appweaver/templates/constitution-ui-template.md +2 -2
- package/assets/.appweaver/templates/delta-design-template.md +7 -5
- package/assets/.appweaver/templates/design-template.md +3 -0
- package/assets/.appweaver/templates/ui-delta-design-template.md +1 -1
- package/assets/.appweaver/templates/ui-design-template.md +6 -4
- package/assets/.claude/skills/appweaver/SKILL.md +7 -46
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.Reference.rules.json +31 -0
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.lane.json +6 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.Reference.rules.json +16 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.lane.json +6 -0
- package/assets/foundations/foundation.spec.template.txt +98 -0
- package/assets/foundations/java/Hexagonal/Hexagonal.lane.json +6 -0
- package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +8 -1
- package/assets/foundations/java/Hexagonal/unpack-foundation.sh +4 -5
- package/assets/foundations/node/NextAppRouter/NextAppRouter.spec.txt +68 -0
- package/assets/guardrails/README.md +31 -17
- package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain-rule-bindings.md +25 -13
- package/assets/guardrails/project-specific/tech-rule-bindings.md +296 -0
- package/assets/guardrails/technology/dotnet-webapi/PACKAGING.md +58 -4
- package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/authorization.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/data-classification.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/dependency-policy.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/eventing.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/persistence.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +1 -1
- package/assets/guardrails/technology/nextjs/accessibility-implementation.md +1 -1
- package/assets/guardrails/technology/nextjs/api-client.md +1 -1
- package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +1 -1
- package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +1 -1
- package/assets/guardrails/technology/nextjs/auth-session.md +1 -1
- package/assets/guardrails/technology/nextjs/client-security.md +1 -1
- package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
- package/assets/guardrails/technology/nextjs/component-conventions.md +2 -2
- package/assets/guardrails/technology/nextjs/design-system.md +1 -1
- package/assets/guardrails/technology/nextjs/error-handling.md +1 -1
- package/assets/guardrails/technology/nextjs/forms-and-validation.md +1 -1
- package/assets/guardrails/technology/nextjs/frontend-testing.md +1 -1
- package/assets/guardrails/technology/nextjs/i18n.md +1 -1
- package/assets/guardrails/technology/nextjs/observability.md +1 -1
- package/assets/guardrails/technology/nextjs/performance-budgets.md +1 -1
- package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
- package/assets/guardrails/technology/nextjs/state-management.md +1 -1
- package/assets/guardrails/technology/nextjs/ui-constitution.md +3 -3
- package/package.json +1 -1
- package/assets/.agents/skills/appweaver-demo/SKILL.md +0 -59
- package/assets/.appweaver/orchestrator/.smoke-out/smoke.md +0 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-demo.orch.yaml +0 -100
- package/assets/.claude/skills/appweaver-demo/SKILL.md +0 -59
- package/assets/.kilocode/agent.json +0 -28
- package/assets/.kilocode/workflows/appweaver-demo.md +0 -59
- package/assets/.kilocode/workflows/appweaver.md +0 -55
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +0 -271
- /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain/domain-requirements.md +0 -0
- /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/frontend/ui-requirements.md +0 -0
- /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
|
|
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.
|
|
@@ -22,7 +22,7 @@ and why nothing in the format assumes there are only two lanes.
|
|
|
22
22
|
|
|
23
23
|
**Lanes get map headings, never a `--lane` flag.** Rule maps are keyed on
|
|
24
24
|
`## /aw-spec-<name>`, so a lane's rule set is reached under its own heading —
|
|
25
|
-
`## /aw-spec-implement-
|
|
25
|
+
`## /aw-spec-implement-frontend` for the frontend lane of `implement`, and so on. A flag would have
|
|
26
26
|
no heading to key on. Where the two lanes are folded into one command, as `plan` and
|
|
27
27
|
`implement` are, the heading names the lane and the command dispatches on the declaration.
|
|
28
28
|
|
|
@@ -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": "
|
|
37
|
-
"
|
|
38
|
-
|
|
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,19 @@ 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
|
|
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
|
|
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
|
|
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) |
|
|
59
|
+
| `contract_run` | *(optional, backend)* the command that starts the service on its own so `implement` can export its API document (§4.5). Absent, the profile's is used. `foundation` sets it from a pack's `<pack>.lane.json` |
|
|
60
|
+
| `contract` | *(optional, backend)* the URL the running service serves its API document at (§4.5). Absent, the profile's is used |
|
|
61
|
+
| `client` | *(optional, frontend)* the command that generates the frontend's API client from that document; `{{contract}}` in it is the document's path. No shipped profile has one: `implement` asks the developer the first time and records the answer here (§4.5) |
|
|
58
62
|
|
|
59
63
|
### Three properties matter more than the schema
|
|
60
64
|
|
|
61
|
-
1. **`root` is ownership, and it is the
|
|
62
|
-
code only under
|
|
65
|
+
1. **`root` is ownership, and it is the lane root folder.** `implement`'s service lane writes its
|
|
66
|
+
code only under the backend lane root folder and its frontend lane only under the frontend one,
|
|
63
67
|
`build`/`test`/`run` act on it, and `onboard` reverse-engineers out of it. The lane's
|
|
64
68
|
**test path** (§4.3) is owned the same way, and is where `implement` writes the lane's tests.
|
|
65
69
|
2. **The commands are project-supplied strings.** The engine stays stdlib-only and
|
|
@@ -71,33 +75,39 @@ no heading to key on. Where the two lanes are folded into one command, as `plan`
|
|
|
71
75
|
|
|
72
76
|
### 3.1 `root` is where the code is; `run_from` is where the commands run
|
|
73
77
|
|
|
74
|
-
These are two questions and
|
|
75
|
-
solution file at the top while the lane owns `src
|
|
76
|
-
while the lane owns `internal/`.
|
|
77
|
-
the repository root.
|
|
78
|
-
|
|
79
|
-
A
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
These are two questions, and an existing repository can give them two answers: a classic
|
|
79
|
+
.NET repository keeps its solution file at the top while the lane owns `src/`, and a classic
|
|
80
|
+
Go repository keeps `go.mod` there while the lane owns `internal/`. Their commands have to run
|
|
81
|
+
at the repository root.
|
|
82
|
+
|
|
83
|
+
**A new project is one self-contained folder per lane.** Every backend profile defaults to
|
|
84
|
+
`backend/` and every frontend profile to `frontend/`, with `run_from: "root"`: the lane's
|
|
85
|
+
build files, source and tests all live inside its lane root folder, and its commands run there.
|
|
86
|
+
`foundation` builds exactly that — a pack unpacks into the lane root folder as packed, a spec build
|
|
87
|
+
creates the whole project inside it — and reports a lane that does not run its commands in its
|
|
88
|
+
own folder or keeps its tests outside it, rather than scaffolding a project its own build
|
|
89
|
+
cannot find.
|
|
90
|
+
|
|
91
|
+
**`select-stack` answers `run_from` from the disk.** Each backend profile lists the files its
|
|
92
|
+
commands need (`build_files`: `*.sln`, `pom.xml`, `go.mod`, `package.json`, …). When a lane is
|
|
93
|
+
declared, `lane-declare` looks for them: directly inside the lane root folder ⇒ `root`; only at the
|
|
94
|
+
repository root ⇒ `repo`; nowhere yet — a greenfield lane — ⇒ the profile's own answer, which is
|
|
95
|
+
inside the lane. So a classic repository keeps the shape it has, a Maven module whose `mvnw`
|
|
96
|
+
sits in the lane root folder runs there, and a new project gets the self-contained layout, from
|
|
97
|
+
one profile. `--run-from` overrides it, and so does editing `stacks.json`.
|
|
85
98
|
|
|
86
99
|
Two consequences worth knowing:
|
|
87
100
|
|
|
88
|
-
- **Declare a
|
|
89
|
-
|
|
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.
|
|
101
|
+
- **Declare a lane at the directory holding its build file** when the code already exists and
|
|
102
|
+
should run there — a Maven lane at the folder with `pom.xml`, not at `src/main/java/`.
|
|
93
103
|
- **A single-module project declares `.`** — stored as `./`, the repository root. It owns
|
|
94
104
|
everything, so it overlaps every other lane by definition and cannot be combined with one.
|
|
95
105
|
|
|
96
|
-
### Where the
|
|
106
|
+
### Where the lane root folder is checked against the disk
|
|
97
107
|
|
|
98
108
|
`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 `
|
|
100
|
-
`implement` will create it.
|
|
109
|
+
project it does not exist yet — the whole point of declaring `backend/` on day one is that
|
|
110
|
+
`foundation` and `implement` will create it.
|
|
101
111
|
|
|
102
112
|
`lane-verify` is the check, and it runs where the path has to be real: before `implement`,
|
|
103
113
|
`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 +118,7 @@ Two failure classes, deliberately distinguished:
|
|
|
108
118
|
- **MISSING** — the phase needs a lane of this kind and none is declared. Reported only for
|
|
109
119
|
the kinds a caller names with `--require`, because "no frontend lane" is a correct and
|
|
110
120
|
common state that must never fail a backend build.
|
|
111
|
-
- **BROKEN** — a lane *is* declared and its
|
|
121
|
+
- **BROKEN** — a lane *is* declared and its lane root folder is empty, is not a directory, or
|
|
112
122
|
overlaps another lane's. Checked for **every** declared lane whether or not the caller
|
|
113
123
|
asked about it. A stack named against a path that is not there is a mistake in every
|
|
114
124
|
phase, and letting it through means discovering it as an empty build or a generator
|
|
@@ -118,11 +128,25 @@ Two failure classes, deliberately distinguished:
|
|
|
118
128
|
|
|
119
129
|
### The file ships empty, and may be filled in by hand
|
|
120
130
|
|
|
121
|
-
The kit installs `.appweaver/stacks.json`
|
|
131
|
+
The kit installs `.appweaver/stacks.json` with an empty `lanes` array, alongside `domain.json` and
|
|
122
132
|
`feature.json`. It is there so a project can open it and fill it in directly — the block at
|
|
123
133
|
the top of this section is the whole schema, and `select-stack` writes nothing a person
|
|
124
134
|
could not have typed.
|
|
125
135
|
|
|
136
|
+
**The shipped file carries a sample under `_lane_format`**: one backend and one frontend lane
|
|
137
|
+
with every field above filled in (.NET and Next.js), plus a `prototype` block. No command
|
|
138
|
+
reads that key, and `select-stack` leaves it in place, so it is safe to keep. To fill the file
|
|
139
|
+
by hand, copy the lanes you need into `lanes` and edit them. The samples' optional fields hold
|
|
140
|
+
values that change nothing when copied (`stack` is the profile's own title, `origin: "declared"`
|
|
141
|
+
is the default, `test_root` is the profile's own answer), except `migrate`, which is a real
|
|
142
|
+
command: keep it only if the lane has migrations to run. The frontend sample's `migrate` is
|
|
143
|
+
empty because a Next.js lane has none.
|
|
144
|
+
|
|
145
|
+
**Never leave an empty or half-filled entry in `lanes`.** Every entry there is a declared lane,
|
|
146
|
+
whatever its values: `select-stack` replaces a lane only when its `id` matches, so it adds its
|
|
147
|
+
lane beside a blank one instead of replacing it, and `lane-verify` then stops every phase on the
|
|
148
|
+
blank lane's missing lane root folder.
|
|
149
|
+
|
|
126
150
|
**An empty file and an absent one are the same state.** Shipping the file means its presence
|
|
127
151
|
no longer says anything about whether a lane was declared, so `declared` reports on the
|
|
128
152
|
`lanes` array and never on the file: `resolve` reports `exists: true, declared: false` for
|
|
@@ -131,7 +155,7 @@ the file is missing, naming which of the two it found.
|
|
|
131
155
|
|
|
132
156
|
Hand-editing and `select-stack` are the same file and the same schema, so they interleave
|
|
133
157
|
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
|
|
158
|
+
run it first and correct a lane root folder in the editor. What hand-editing gives up is the
|
|
135
159
|
checking `select-stack` does on the way in — an unknown `profile`, a missing `root`, and two
|
|
136
160
|
lanes whose roots overlap are all refused there and merely written here. `lane-verify` still
|
|
137
161
|
catches the last two before any phase acts on them; a profile id that matches nothing
|
|
@@ -152,7 +176,7 @@ Declaring **`(none)`** for a lane is a real answer and the way a project says it
|
|
|
152
176
|
|
|
153
177
|
## 3. The ownership rule
|
|
154
178
|
|
|
155
|
-
**Lane
|
|
179
|
+
**Lane root folders own their subtree and must be disjoint.** Two generators must never be able
|
|
156
180
|
to write the same file. A lane owns its test path too, so the rule covers both: no source or
|
|
157
181
|
test path of one lane may overlap a source or test path of another.
|
|
158
182
|
|
|
@@ -182,13 +206,25 @@ workflow.**
|
|
|
182
206
|
"title": "React + Vite", // shown in the menu
|
|
183
207
|
"language": "TypeScript", // shown in the menu, parenthesised
|
|
184
208
|
"ecosystem": "node", // which stack-partitioned corpus this stack draws from
|
|
185
|
-
"root": "
|
|
209
|
+
"root": "frontend/", // the folder a NEW lane of this stack gets — see §3.1
|
|
186
210
|
"test_root": "", // optional — where tests go when not inside root — see §4.3
|
|
187
|
-
"run_from": "root", // where the commands run — see §3.1
|
|
211
|
+
"run_from": "root", // where the commands run for a new lane — see §3.1
|
|
212
|
+
"conventional_root": "web/", // optional — where this stack's code usually sits in an
|
|
213
|
+
// EXISTING repository; `detect` is written against it
|
|
214
|
+
"conventional_test_root": "", // optional — the test path that pairs with it — see §4.3
|
|
215
|
+
"build_files": [], // optional — the files its commands need (`*.sln`,
|
|
216
|
+
// `go.mod`); decides `run_from` at declare time — §3.1
|
|
188
217
|
"build": "npm run build",
|
|
189
218
|
"test": "npm test",
|
|
190
219
|
"run": "npm run dev",
|
|
220
|
+
"host_patterns": [], // optional — where the runnable project sits, for
|
|
221
|
+
// `{{host}}` in a command — see §4.2
|
|
191
222
|
"migrate": "", // optional — schema migration, if the stack has one
|
|
223
|
+
"contract_run": "", // optional, backend — start the service to export
|
|
224
|
+
// its API document — see §4.5
|
|
225
|
+
"contract": "", // optional, backend — the URL it serves it at
|
|
226
|
+
"client": "", // optional, frontend — generate the API client
|
|
227
|
+
// from `{{contract}}` — see §4.5
|
|
192
228
|
"prototype_run": "npm run dev --prefix prototype", // frontend only — fills the prototype block
|
|
193
229
|
"reserved_names": ["Event", "Request", "Response"], // optional — names this stack's AMBIENT
|
|
194
230
|
// scope already defines; see §4.4
|
|
@@ -209,49 +245,96 @@ trees that are physically per-ecosystem:
|
|
|
209
245
|
|
|
210
246
|
| Tree | Keyed by | Read through |
|
|
211
247
|
|---|---|---|
|
|
212
|
-
| `foundations/<ecosystem>/<Name>/` | `ecosystem
|
|
248
|
+
| `foundations/<ecosystem>/<Name>/` | `ecosystem`, then each pack/spec's `# kind:` / `# profiles:` header | `foundation-list`, `foundation-locate` |
|
|
213
249
|
| `foundations/<ecosystem>/components/<C>/` | `ecosystem` | `list-components`, `add-component` |
|
|
214
250
|
|
|
215
251
|
**Why those two are filtered and the rule corpora are not.** A foundation bundle is a whole
|
|
216
252
|
compiling project and a component runbook edits a host project's build files — both are
|
|
217
253
|
executable artifacts in one toolchain, so offering a `dotnet` bundle to a Spring repo does not
|
|
218
254
|
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
|
|
220
|
-
*what exists and
|
|
255
|
+
therefore narrows by ecosystem, and reports `all_foundations` alongside so the pick list can say
|
|
256
|
+
*what exists and how much of it applies*. `--ecosystem` narrows to the ONE lane being
|
|
221
257
|
scaffolded, which is what `foundation` passes: a repo declaring a .NET service and a Node UI
|
|
222
258
|
must not be offered its .NET bundle while the UI lane is the target. Without that argument the
|
|
223
259
|
filter falls back to every declared lane's ecosystem, and a repo that has declared nothing yet
|
|
224
260
|
is not filtered — there is no answer to filter on.
|
|
225
261
|
|
|
226
|
-
**
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
`
|
|
262
|
+
**Ecosystem alone is not narrow enough for a lane**, because it is shared across kinds: the
|
|
263
|
+
five JavaScript frontends and `node-express` are all `node`, and `blazor-wasm` shares `dotnet`
|
|
264
|
+
with the service stacks. So each pack and spec may open with header lines that `foundation-list`
|
|
265
|
+
filters on as well — `# kind: backend|frontend` (absent = backend, the kind every pack shipped
|
|
266
|
+
before the key existed scaffolds) and `# profiles: <id>, …` (absent = any profile of that kind).
|
|
267
|
+
|
|
268
|
+
**Packs and specs.** A directory under `foundations/<ecosystem>/` may hold a pack
|
|
269
|
+
(`<Name>.pack.txt` — a verified project unpacked byte-for-byte), any number of specs
|
|
270
|
+
(`*.spec.txt` — a description an agent builds the project from), or both; each is its own
|
|
271
|
+
entry in the lane's pick list, after which come `[manual spec]` (a spec given at the prompt)
|
|
272
|
+
and `[skip]`. `foundations/foundation.spec.template.txt` is the shape of a spec. A spec build
|
|
273
|
+
loads the technical rules the active project maps under `## /aw-spec-foundation-backend` or
|
|
274
|
+
`## /aw-spec-foundation-frontend` (§7), and the spec it used is kept at
|
|
275
|
+
`.appweaver/foundation/<lane>.spec.md`.
|
|
276
|
+
|
|
277
|
+
**A scaffold lands in a lane.** `foundation` scaffolds into the FOLDER of each lane — its
|
|
278
|
+
lane root folder, never a directory named on the command line — and the lane is self-contained
|
|
279
|
+
(§3.1): a pack unpacks there exactly as packed, build files, source and tests together, and a
|
|
280
|
+
spec build creates the whole project there and may write nowhere else — checked, not trusted:
|
|
281
|
+
the repo is recorded before the build and compared after it, and a build that created, changed
|
|
282
|
+
or deleted anything outside the lane root folder is "NOT accepted", with what it created there
|
|
283
|
+
removed again and what it changed or deleted listed for the operator. One run covers every
|
|
284
|
+
declared lane, backend then frontend, and each lane gets its own pick. A lane that does not run
|
|
285
|
+
its commands in its lane root folder, or keeps its tests outside it, is reported and passed over. A
|
|
286
|
+
lane that already holds files — any file in its source or test path — is asked "Overwrite?"
|
|
287
|
+
before its pick list; "no" skips the lane, and "yes" replaces same-path files and deletes
|
|
288
|
+
nothing. A pack's reference sample goes on top of the base project the same way. The one
|
|
289
|
+
exception is a pack's **rule file** (`<pack name>.rules.json` beside the pack): the files and
|
|
290
|
+
folders it names with `merge` are combined with the file already there, by an agent following
|
|
291
|
+
the rule's own instruction, instead of being replaced — the .NET reference samples use it to
|
|
292
|
+
merge their DbContext into the base project's. The format is in
|
|
293
|
+
[PACKAGING.md](../guardrails/technology/dotnet-webapi/PACKAGING.md#the-rule-file-pack-namerulesjson).
|
|
294
|
+
This is what makes every phase's "write only into your lane" rule true of the scaffold
|
|
295
|
+
as well as of generated code: `implement`, `build`, `test` and `run` all resolve the same
|
|
296
|
+
paths. Lanes must therefore be declared before `foundation` runs, which is why `select-stack`
|
|
297
|
+
is the first command of both sequences.
|
|
235
298
|
|
|
236
299
|
The technical RULE corpora at `guardrails/technology/<stack>/` are keyed by **profile id**, not
|
|
237
300
|
ecosystem, because Spring and Quarkus are both `java` and do not share a persistence rule. They
|
|
238
301
|
are selected through the project's `tech-rule-bindings.md` rather than filtered here — see §7.
|
|
239
302
|
|
|
240
|
-
### 4.2 `run_from`, `{{root}}` and `migrate`
|
|
303
|
+
### 4.2 `run_from`, `{{root}}`, `{{host}}` and `migrate`
|
|
241
304
|
|
|
242
|
-
A profile command sometimes has to name the
|
|
305
|
+
A profile command sometimes has to name the lane root folder — `python -m compileall app`. Written
|
|
243
306
|
literally that is the profile's *default* root frozen into the command, and it stays frozen when
|
|
244
307
|
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
|
|
308
|
+
root as seen from where the command runs, expanded at run time so a hand-edited `root` in
|
|
309
|
+
`stacks.json` stays consistent with it:
|
|
246
310
|
|
|
247
311
|
```jsonc
|
|
248
|
-
"
|
|
249
|
-
|
|
312
|
+
"build": "python -m compileall {{root}}" // app/, run at the repo root ⇒ python -m compileall app
|
|
313
|
+
// a run_from: root lane ⇒ python -m compileall .
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Some commands need a *project* inside the lane root folder rather than the folder itself.
|
|
317
|
+
`dotnet run --project <folder>` needs exactly one project in that folder, and a solution has
|
|
318
|
+
several — a pack's runnable one is `src/<App>.AppHost`, the .NET Aspire host. Write
|
|
319
|
+
**`{{host}}`** and it resolves to that project's folder, from the profile's `host_patterns`:
|
|
320
|
+
paths relative to the lane root folder, best first, `**/` for any depth, case ignored. The
|
|
321
|
+
first pattern that matches exactly one file names the project; build output, dependency and
|
|
322
|
+
test folders are never searched. It is looked up when the command is read, so a project
|
|
323
|
+
scaffolded after the lane was declared is found, and with no single match `{{host}}` falls
|
|
324
|
+
back to `{{root}}`:
|
|
325
|
+
|
|
326
|
+
```jsonc
|
|
327
|
+
"run": "dotnet run --project {{host}}",
|
|
328
|
+
"host_patterns": ["**/*.AppHost.csproj", "**/*.Api.csproj", "**/*.Service.csproj"]
|
|
329
|
+
// backend/ holding src/Acme.AppHost/ and src/Acme.Service/ ⇒ dotnet run --project src/Acme.AppHost
|
|
330
|
+
// backend/ holding src/Acme.Api/ and src/Acme.Domain/ ⇒ dotnet run --project src/Acme.Api
|
|
250
331
|
```
|
|
251
332
|
|
|
252
333
|
A command with no placeholder is used verbatim, so every literal command already on disk is
|
|
253
|
-
unaffected.
|
|
254
|
-
|
|
334
|
+
unaffected. A lane keeps the command it was declared with: one declared before `{{host}}` was
|
|
335
|
+
in its profile picks it up when select-stack declares it again. `{{root}}` and `{{host}}` are
|
|
336
|
+
the answer when the command needs a *path*; `run_from: "root"` (§3.1) is the answer when it
|
|
337
|
+
needs the *working directory*.
|
|
255
338
|
|
|
256
339
|
`migrate` is an optional fourth phase command, alongside `build`/`test`/`run`. **No shipped
|
|
257
340
|
profile declares one**, deliberately: Flyway, Liquibase, Alembic, Prisma and golang-migrate all
|
|
@@ -263,17 +346,21 @@ elsewhere rather than implying a migration succeeded.
|
|
|
263
346
|
`detect` is an **ANY-of** list, not an all-of: `java-spring` names both `pom.xml` and
|
|
264
347
|
`build.gradle` because a project has one or the other, and requiring both would make it
|
|
265
348
|
undetectable. `stack-detect` scores a profile by how many of its patterns hit. Patterns are
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
349
|
+
written against the layout an EXISTING repository of the stack usually has — the profile's
|
|
350
|
+
`conventional_root` (`web/package.json`, `src/**/*.csproj`) — and matched first as written
|
|
351
|
+
(repo-root-relative, with `**` spanning separators), then relocated under any directory prefix,
|
|
352
|
+
with that root segment substituted — so a profile shipping `web/package.json` still recognises
|
|
353
|
+
the same stack in `client/` or `apps/portal/`, which a brownfield repo that never saw our
|
|
269
354
|
profiles will need. An anchored hit outranks a relocated one.
|
|
270
355
|
|
|
271
356
|
`detect` and `root` are independent, and deliberately so: `go-chi` detects on `go.mod` at the
|
|
272
|
-
repo root
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
357
|
+
repo root, keeps its code under `internal/` in an existing repository, and gives a new lane
|
|
358
|
+
`backend/`. Detection reports the shallowest directory its evidence actually sits in; evidence
|
|
359
|
+
only at the repository root (`App.sln`, `go.mod`) defers to the `conventional_root` when that
|
|
360
|
+
directory is there, then to `root`. That directory is what `select-stack` offers first when it
|
|
361
|
+
asks for the lane root folder — ahead of the profile's own default, because a repo that
|
|
362
|
+
already exists did not choose its layout from our profiles. The operator picks from the list or
|
|
363
|
+
types a path the kit has never seen.
|
|
277
364
|
|
|
278
365
|
**Detection leads the menu; it never fills `preselect`.** The engine treats `preselect_from`
|
|
279
366
|
as the answer, ahead of a headless `--select`, so filling it from a scan would let a repo's
|
|
@@ -323,47 +410,98 @@ Absent, nothing is reported and generation is unchanged.
|
|
|
323
410
|
|
|
324
411
|
### 4.3 `test_root` — where a lane's tests go
|
|
325
412
|
|
|
326
|
-
|
|
327
|
-
to `src/`, Maven's `src/test/java/` next to `src/main/java/`,
|
|
328
|
-
|
|
329
|
-
**test path**, and `implement` writes the lane's
|
|
413
|
+
In an existing repository most backend stacks keep their tests **beside** the source, not
|
|
414
|
+
inside it: .NET's `tests/` next to `src/`, Maven's `src/test/java/` next to `src/main/java/`,
|
|
415
|
+
pytest's `tests/` next to `app/`. A new, self-contained lane keeps them inside its lane root folder
|
|
416
|
+
(`backend/tests/`). Either way each lane owns a **test path**, and `implement` writes the lane's
|
|
417
|
+
paired tests there.
|
|
330
418
|
|
|
331
|
-
A profile's `test_root` is the test path that pairs with its default `root
|
|
419
|
+
A profile's `test_root` is the test path that pairs with its default `root`, and its
|
|
420
|
+
`conventional_test_root` the one that pairs with its `conventional_root`. `lane-resolve` and
|
|
332
421
|
`lane-verify` report it per kind as `backend_test_path` / `frontend_test_path`, worked out at
|
|
333
422
|
read time:
|
|
334
423
|
|
|
335
424
|
| The lane | Its test path |
|
|
336
425
|
|---|---|
|
|
337
426
|
| declares `test_root` itself | that value |
|
|
338
|
-
| sits at the profile's default root (`
|
|
339
|
-
| sits at a path ending in the default root (`backend
|
|
340
|
-
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
`
|
|
345
|
-
fixed by writing `test_root` on the lane in `stacks.json`.
|
|
346
|
-
|
|
347
|
-
| Profile | `root` | `test_root` |
|
|
427
|
+
| sits at the profile's default root (`backend/`) | the profile's `test_root` (`backend/tests/`) |
|
|
428
|
+
| sits at a path ending in the default root (`apps/backend/`) | the profile's `test_root` under the same prefix (`apps/backend/tests/`) |
|
|
429
|
+
| 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/`) |
|
|
430
|
+
| anything else, or the profile declares neither | the lane root folder itself |
|
|
431
|
+
|
|
432
|
+
The last row is right for every shipped frontend (tests under `frontend/tests/`), for Go's
|
|
433
|
+
colocated `_test.go` files, and for a lane at any other folder name, whose tests are inside it.
|
|
434
|
+
A layout the table gets wrong is fixed by writing `test_root` on the lane in `stacks.json`.
|
|
435
|
+
|
|
436
|
+
| Profile | `root` | `test_root` | `conventional_root` | `conventional_test_root` |
|
|
437
|
+
|---|---|---|---|---|
|
|
438
|
+
| `dotnet-webapi` | `backend/` | `backend/tests/` | `src/` | `tests/` |
|
|
439
|
+
| `java-spring` | `backend/` | `backend/src/test/java/` | `src/main/java/` | `src/test/java/` |
|
|
440
|
+
| `python-fastapi` | `backend/` | `backend/tests/` | `app/` | `tests/` |
|
|
441
|
+
| `node-express` | `backend/` | `backend/tests/` | `src/` | `tests/` |
|
|
442
|
+
| `go-chi` | `backend/` | *(inside the lane root folder)* | `internal/` | — |
|
|
443
|
+
| every frontend | `frontend/` | *(inside the lane root folder)* | `web/` | — |
|
|
444
|
+
|
|
445
|
+
### 4.5 `contract_run`, `contract` and `client` — the API contract between the lanes
|
|
446
|
+
|
|
447
|
+
The frontend lane binds to the service through the service's published API document, and
|
|
448
|
+
the document that counts is the one the **built code** serves. `implement` therefore runs
|
|
449
|
+
three steps between the two lanes, each of which warns rather than stops:
|
|
450
|
+
|
|
451
|
+
1. **Export** (`contract-export`) — start the service with the backend lane's
|
|
452
|
+
`contract_run`, fetch its `contract` URL, write the document to
|
|
453
|
+
`<service lane root folder>/contracts/openapi.json`, and stop the service with
|
|
454
|
+
everything it started. A service already answering at the URL is used as it is, and
|
|
455
|
+
the run says it may be older than the code.
|
|
456
|
+
2. **Client** (`contract-client`) — run the frontend lane's `client` command, with
|
|
457
|
+
`{{contract}}` standing for that document as seen from where the command runs.
|
|
458
|
+
3. **Check** (`contract-check`) — every operation id the frontend plan cites must be an
|
|
459
|
+
`operationId` in the document.
|
|
460
|
+
|
|
461
|
+
Each key is read from the lane first and from its profile second, so a lane declared
|
|
462
|
+
before the keys existed still answers them. Where the values come from:
|
|
463
|
+
|
|
464
|
+
| Key | Profile default | Set on the lane by |
|
|
348
465
|
|---|---|---|
|
|
349
|
-
| `dotnet-webapi`
|
|
350
|
-
| `
|
|
351
|
-
| `
|
|
352
|
-
|
|
353
|
-
|
|
466
|
+
| `contract_run` | `dotnet-webapi`: `dotnet run --project {{host}} -- --environment Development --urls http://localhost:5000`<br>`java-spring`: `./mvnw spring-boot:run -Dspring-boot.run.arguments=--server.port=8080` | `foundation`: on the pack route from the pack's `<pack>.lane.json` — the shipped .NET packs start their API project alone (the AppHost needs Docker), the Java `Hexagonal` pack its one Maven module; on the spec route from the spec's `# contract-run:` header line, or the operator's answer when it has none |
|
|
467
|
+
| `contract` | `dotnet-webapi`: `http://localhost:5000/swagger/v1/swagger.json`<br>`java-spring`: `http://localhost:8080/v3/api-docs` (springdoc's default path) | `foundation`, the same way — the `# contract:` header line on the spec route |
|
|
468
|
+
| `client` | none — no generator is assumed | `foundation` on the spec route, from the `# client:` header line or the operator's answer; else `implement`, which asks the first time and records the answer |
|
|
469
|
+
|
|
470
|
+
On the spec route an empty answer keeps the lane's current value, so a lane nobody set
|
|
471
|
+
anything on stays on the profile's default. `foundations/foundation.spec.template.txt`
|
|
472
|
+
shows the three header lines; `<App>` in them is the app name given at `foundation`.
|
|
473
|
+
|
|
474
|
+
`--environment Development` matters for the .NET packs: outside Development they redirect
|
|
475
|
+
to HTTPS, and the export fetches over http. Their API projects also read
|
|
476
|
+
`ConnectionStrings:SqliteConnection` when started without the AppHost, and the packs'
|
|
477
|
+
configuration carries none, so their commands pass one. A pack's `<pack>.lane.json` sits
|
|
478
|
+
beside its `.pack.txt`, names the pack's sentinel the way its files do, and is renamed with
|
|
479
|
+
them:
|
|
480
|
+
|
|
481
|
+
```json
|
|
482
|
+
{"format": "aw-spec-pack-lane/v1",
|
|
483
|
+
"contract_run": "dotnet run --project src/AppWeaver.VerticalSlice.Service -- --environment Development --urls http://localhost:5000 --ConnectionStrings:SqliteConnection \"Data Source=App_Data/app.db\"",
|
|
484
|
+
"contract": "http://localhost:5000/swagger/v1/swagger.json"}
|
|
485
|
+
```
|
|
354
486
|
|
|
355
487
|
---
|
|
356
488
|
|
|
357
489
|
## 5. Who may write this file
|
|
358
490
|
|
|
359
|
-
**`lane-declare` writes
|
|
360
|
-
|
|
491
|
+
**`lane-declare` writes whole lanes, and it has exactly ONE caller** — so the rule is a
|
|
492
|
+
grep, not a convention:
|
|
361
493
|
|
|
362
494
|
```sh
|
|
363
495
|
grep -rl "lane-declare" .appweaver/orchestrator/workflows/ # must print exactly one file
|
|
364
496
|
# aw-spec-select-stack.orch.yaml — a person picks
|
|
365
497
|
```
|
|
366
498
|
|
|
499
|
+
**`lane-set` changes three keys on a lane that is already there, and nothing else** —
|
|
500
|
+
`contract_run`, `contract` and `client` (§4.5). Its callers are `foundation`, recording
|
|
501
|
+
what a pack knows about starting its service, and `implement`, recording the client
|
|
502
|
+
command a developer types. They cannot use `lane-declare`, which rebuilds the whole lane
|
|
503
|
+
and would reset every command on it to the profile's default.
|
|
504
|
+
|
|
367
505
|
`onboard` used to be a second caller. Before `select-stack` was the first command of the
|
|
368
506
|
brownfield sequence a repo could reach `onboard` undeclared, so `onboard` ran its own
|
|
369
507
|
detection pass, recorded what it found marked `origin: reverse-engineered`, and was gated
|
|
@@ -390,11 +528,12 @@ re-runs the roots-disjoint one on every read (see §2).
|
|
|
390
528
|
|
|
391
529
|
Consequences worth stating, because they are the reason for the design:
|
|
392
530
|
|
|
393
|
-
- **`foundation`
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
531
|
+
- **`foundation` scaffolds *into* a declared lane** and never rewrites the declaration to
|
|
532
|
+
fit what it produced. Each lane is scaffolded into its own source and test paths, and a
|
|
533
|
+
spec build must leave a project of the lane's own stack inside the lane — otherwise that
|
|
534
|
+
lane is reported "NOT usable" and the run moves on to the next one. A wrong declaration
|
|
535
|
+
surfaces at the scaffold instead of being absorbed by it, and the lane a project *chose*
|
|
536
|
+
stays the authority over the lane a generator *happened to produce*.
|
|
398
537
|
- **`build` runs every declared lane's own build command**, in declaration order, and
|
|
399
538
|
fails if any lane fails. Without a declaration it falls back to the historic
|
|
400
539
|
first-match-wins sniffer, which is right for one lane and silently skips the second —
|
|
@@ -411,7 +550,7 @@ follows from it:
|
|
|
411
550
|
| Which rule sets does `onboard` generate? | the same, per lane — `service/` and `frontend/` under the project folder |
|
|
412
551
|
| Which subtree does `implement` write into? | that lane's `root` |
|
|
413
552
|
| What does `build` compile, and `run` migrate? | every declared lane's own command, against its own root |
|
|
414
|
-
| What does `onboard` scan? | the declared
|
|
553
|
+
| What does `onboard` scan? | the declared lane root folders — backend signals from outside the UI subtree, frontend signals from inside it |
|
|
415
554
|
|
|
416
555
|
This is a change of authority, and worth being explicit about. The question "does this
|
|
417
556
|
project have a UI lane" used to be answered by OR-ing three heuristics: a
|
|
@@ -441,13 +580,15 @@ constraint where it previously had none. That is a deliberate trade: what it buy
|
|
|
441
580
|
every later phase can ask this file a question and get an answer, instead of inferring one.
|
|
442
581
|
|
|
443
582
|
```
|
|
444
|
-
GREENFIELD select-stack →
|
|
583
|
+
GREENFIELD select-stack → constitution → [select-feature] → specify → …
|
|
445
584
|
BROWNFIELD select-stack → onboard → select-feature → adopt → …
|
|
446
585
|
```
|
|
447
586
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
587
|
+
Neither sequence selects a project. Greenfield uses `guardrails/project-specific/`, which
|
|
588
|
+
`.appweaver/domain.json` names as shipped; `onboard` creates `guardrails/brf-<domain>/` from
|
|
589
|
+
the code and points `domain.json` at it. The optional `select-domain` switches `domain.json` to
|
|
590
|
+
another project folder under `guardrails/` that already exists. On an existing codebase `select-stack` is mostly confirmation — it detects the
|
|
591
|
+
stack and the lane root folder from the repo's own files and leads the menu with them — but it is
|
|
451
592
|
a person's confirmation, which is what detection on a legacy repo needs.
|
|
452
593
|
|
|
453
594
|
## 6. Subcommands
|
|
@@ -460,7 +601,11 @@ implementation and the twins cannot drift.
|
|
|
460
601
|
|---|---|
|
|
461
602
|
| `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
603
|
| `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 —
|
|
604
|
+
| `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 WHOLE lane — its one caller is `select-stack` (§5). `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 |
|
|
605
|
+
| `lane-set (--id lane\|--kind k) [--set KEY=VALUE]… [--unset KEY]…` | change only `contract_run`, `contract` or `client` on a lane already declared, leaving the rest of it alone. An empty VALUE changes nothing; `--unset` puts the key back on the profile's value. Called by `foundation` (a pack's lane settings) and `implement` (the client command a developer supplies) — re-declaring would reset every other command |
|
|
606
|
+
| `contract-export [--timeout s]` | start the service lane with `contract_run`, fetch `contract`, save it to `<service root>/contracts/openapi.json`, stop the service and everything it started. A service already answering at the URL is used as it is, and said so (§4.5) |
|
|
607
|
+
| `contract-client` | `run-phase --phase client` for the frontend lane — its `client` command, `{{contract}}` expanded |
|
|
608
|
+
| `contract-check --plan p [--doc d]` | the operation ids a frontend plan cites (its §5 `Operation id` column, or a delta plan's `Contract Binding` rows) against the document's `operationId`s — by script, no model |
|
|
464
609
|
| `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
610
|
| `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
611
|
| `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 +619,11 @@ set**, so a caller binds outputs without knowing which branch ran.
|
|
|
474
619
|
|---|---|---|
|
|
475
620
|
| `build-compile` | every declared lane's own `build`, via `run-phase` | the historic first-match-wins ladder |
|
|
476
621
|
| `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
|
|
478
|
-
| `find-host-project` | searches the service lane
|
|
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
|
|
622
|
+
| `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/` |
|
|
623
|
+
| `find-host-project` | searches the service lane root folder, and reports the lane's `ecosystem` | searches `src/` |
|
|
624
|
+
| `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` |
|
|
625
|
+
| `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 |
|
|
626
|
+
| `build-compile --kind` | that one lane's own `build` — what `foundation` runs after a spec build | the ladder; `--kind` is ignored |
|
|
481
627
|
| `resolve-tech` | expands stack placeholders and flags a corpus mismatch (§7a) | no placeholders resolve; nothing is flagged |
|
|
482
628
|
|
|
483
629
|
**Why `--scope slice` is the one exception.** VSTest's `--filter FullyQualifiedName~` is the
|
|
@@ -498,16 +644,17 @@ No workflow or script changes. If adding a stack ever requires touching a workfl
|
|
|
498
644
|
model is wrong and that is the bug to fix.
|
|
499
645
|
|
|
500
646
|
That gives the new stack every phase that is driven by a **command** — build, test, run,
|
|
501
|
-
migrate — plus the correct
|
|
647
|
+
migrate — plus the correct lane root folder everywhere, immediately. Two things are opt-in because
|
|
502
648
|
they are content the kit cannot invent:
|
|
503
649
|
|
|
504
650
|
- **Technical rules** — a corpus at `guardrails/technology/<id>/`, mapped from the project's
|
|
505
651
|
`tech-rule-bindings.md` (§7a). Until one exists the stack runs with the universal
|
|
506
652
|
`ai-rules.md` and the technical constitution, which is the greenfield-safe default, not an
|
|
507
653
|
error.
|
|
508
|
-
- **A foundation
|
|
509
|
-
foundation` to
|
|
510
|
-
|
|
654
|
+
- **A foundation pack or spec** — `foundations/<ecosystem>/<Name>/`, only if you want
|
|
655
|
+
`/appweaver foundation` to offer one for this stack (give it `# kind:` / `# profiles:`
|
|
656
|
+
header lines). Without one, `foundation` still offers `[manual spec]` for the lane, and
|
|
657
|
+
scaffolding with the stack's own tooling is fully supported: `implement` generates INTO a
|
|
511
658
|
project tree and does not require that the kit created it.
|
|
512
659
|
|
|
513
660
|
## 7a. Mapping rules to a stack without pinning the map to one
|
|
@@ -523,15 +670,20 @@ they are content the kit cannot invent:
|
|
|
523
670
|
| `{{backend_ecosystem}}` / `{{frontend_ecosystem}}` | that lane's `ecosystem` |
|
|
524
671
|
|
|
525
672
|
```md
|
|
526
|
-
## /aw-spec-plan
|
|
673
|
+
## /aw-spec-plan-backend
|
|
527
674
|
- guardrails/technology/{{stack}}/persistence.md
|
|
528
|
-
|
|
675
|
+
|
|
676
|
+
## /aw-spec-plan-frontend
|
|
677
|
+
- guardrails/technology/{{frontend_stack}}/component-architecture.md
|
|
678
|
+
- guardrails/technology/{{frontend_stack}}/api-integration.{{backend_stack}}.md
|
|
529
679
|
```
|
|
530
680
|
|
|
531
681
|
One map, read from a Spring repo, loads `java-spring/persistence.md`; read from a .NET repo it
|
|
532
|
-
loads `dotnet-webapi/persistence.md`.
|
|
533
|
-
|
|
534
|
-
|
|
682
|
+
loads `dotnet-webapi/persistence.md`. Each heading holds one lane's rules, and a workflow loads
|
|
683
|
+
a lane's heading only when that lane runs. A placeholder for a lane that is not declared
|
|
684
|
+
**skips** that bullet — in a frontend-only repo the `api-integration.{{backend_stack}}.md`
|
|
685
|
+
pairing bullet is skipped, not an error — and a resolved path with no file on disk is reported
|
|
686
|
+
under `missing` rather than silently dropped.
|
|
535
687
|
|
|
536
688
|
**A bullet that spells the stack out is still used**, because a shared corpus may be referenced
|
|
537
689
|
on purpose. But when that directory is not one the repo declares, `resolve-tech` returns
|