aw-spec-cli 0.1.29 → 0.1.31

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 (67) hide show
  1. package/assets/.amp/agent.json +22 -0
  2. package/assets/.amp/settings-edit.json +5 -0
  3. package/assets/.amp/settings-read.json +12 -0
  4. package/assets/.appweaver/DESIGN-FORMAT.md +9 -0
  5. package/assets/.appweaver/STACK-PROFILES.md +102 -15
  6. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +40 -18
  7. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +56 -4
  8. package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +17 -1
  9. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +12 -3
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +289 -24
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +370 -85
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +349 -65
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +39 -7
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +63 -4
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +5 -4
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +430 -138
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +392 -76
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +104 -0
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +20 -3
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +26 -3
  21. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +314 -44
  22. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +249 -20
  23. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +17 -2
  24. package/assets/.appweaver/scripts/python/aw-plangate.py +109 -19
  25. package/assets/.appweaver/scripts/python/aw-stack.py +1295 -9
  26. package/assets/.appweaver/templates/delta-design-template.md +7 -5
  27. package/assets/.appweaver/templates/delta-spec-template.md +2 -0
  28. package/assets/.appweaver/templates/design-template.md +17 -0
  29. package/assets/.appweaver/templates/ui-design-template.md +19 -4
  30. package/assets/.augment/agent.json +22 -0
  31. package/assets/.augment/commands/appweaver.md +12 -0
  32. package/assets/.cline/agent.json +22 -0
  33. package/assets/.clinerules/workflows/appweaver.md +7 -0
  34. package/assets/.codex/agent.json +22 -0
  35. package/assets/.continue/agent.json +22 -0
  36. package/assets/.continue/prompts/appweaver.md +13 -0
  37. package/assets/.cursor/agent.json +22 -0
  38. package/assets/.cursor/commands/appweaver.md +7 -0
  39. package/assets/.devin/agent.json +22 -0
  40. package/assets/.devin/workflows/appweaver.md +11 -0
  41. package/assets/.factory/agent.json +22 -0
  42. package/assets/.factory/commands/appweaver.md +12 -0
  43. package/assets/.gemini/agent.json +22 -0
  44. package/assets/.github/prompts/appweaver.prompt.md +13 -0
  45. package/assets/.kilo/agent.json +28 -0
  46. package/assets/.kilo/agents/appweaver-edit.md +12 -0
  47. package/assets/.kilo/agents/appweaver-read.md +12 -0
  48. package/assets/.kilo/commands/appweaver.md +11 -0
  49. package/assets/.kiro/agent.json +22 -0
  50. package/assets/.kiro/skills/appweaver/SKILL.md +12 -0
  51. package/assets/.opencode/agent.json +22 -0
  52. package/assets/.opencode/agents/appweaver-edit.md +12 -0
  53. package/assets/.opencode/agents/appweaver-read.md +13 -0
  54. package/assets/.opencode/commands/appweaver.md +11 -0
  55. package/assets/.roo/agent.json +22 -0
  56. package/assets/.roo/commands/appweaver.md +12 -0
  57. package/dist/commands/init.d.ts.map +1 -1
  58. package/dist/commands/init.js +14 -1
  59. package/dist/commands/init.js.map +1 -1
  60. package/dist/utils/config.d.ts.map +1 -1
  61. package/dist/utils/config.js +14 -0
  62. package/dist/utils/config.js.map +1 -1
  63. package/dist/utils/copy-assets.d.ts +16 -3
  64. package/dist/utils/copy-assets.d.ts.map +1 -1
  65. package/dist/utils/copy-assets.js +29 -14
  66. package/dist/utils/copy-assets.js.map +1 -1
  67. package/package.json +1 -1
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "amp",
3
+ "runtime": "Amp CLI (amp -x)",
4
+ "note": "amp has no system-prompt flag, so 'system' is folded into the prompt (system: prepend), and no model flag (its --mode picks model, system prompt and tools together), so --model is ignored. No tools allowlist or bare mode, so isolation is best-effort: AGENTS.md still loads. -x is execute mode: the prompt is read from stdin and only the last assistant message is printed. --no-ide keeps the open editor's file and selection out of the prompt. Amp has no permission flags, so each step passes one of the kit's settings files with --settings-file: .amp/settings-edit.json allows every tool; .amp/settings-read.json rejects file writes, shell commands and subagents. That file stands in for the developer's own Amp user settings (MCP servers included) for spawned steps only; a workspace .amp/settings.json still applies on top of it. Amp has no launcher of its own: it reads .agents/skills/appweaver/SKILL.md. No sdk block: Amp has no Python SDK adapter, so sdk/sdk-apply are unavailable.",
5
+ "bin": "amp",
6
+ "args": ["-x", "--no-ide"],
7
+ "model_flag": null,
8
+ "system": "prepend",
9
+ "user": { "mode": "stdin" },
10
+ "tools_flag": null,
11
+ "approval": {
12
+ "acceptEdits": ["--settings-file", ".amp/settings-edit.json"],
13
+ "bypassPermissions": ["--settings-file", ".amp/settings-edit.json"],
14
+ "default": [],
15
+ "readonly": ["--settings-file", ".amp/settings-read.json"]
16
+ },
17
+ "isolation": {
18
+ "bare": [],
19
+ "fresh": []
20
+ },
21
+ "result_keys": ["result"]
22
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "amp.permissions": [
3
+ { "tool": "*", "action": "allow" }
4
+ ]
5
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "amp.permissions": [
3
+ { "tool": "create_file", "action": "reject" },
4
+ { "tool": "edit_file", "action": "reject" },
5
+ { "tool": "apply_patch", "action": "reject" },
6
+ { "tool": "delete_file", "action": "reject" },
7
+ { "tool": "Bash", "action": "reject" },
8
+ { "tool": "shell_command", "action": "reject" },
9
+ { "tool": "async_shell_command", "action": "reject" },
10
+ { "tool": "Task", "action": "reject" }
11
+ ]
12
+ }
@@ -128,6 +128,15 @@ a contract delta raised back to the service lane, never a client workaround. ⌛
128
128
  will emit that surface as a `design-contract.md` projection; the rule does not change when
129
129
  it does, only what §5 reads.
130
130
 
131
+ **Contract deltas are a request ledger between the two plans.** frontend-plan.md §5 "Contract
132
+ deltas needed" is a table, one `RQ-n` per operation the experience needs and the surface does
133
+ not offer, with the operation id and field shapes it proposes; a request keeps its id for
134
+ life. backend-plan.md §3 answers each in its "Requests from the frontend lane" table —
135
+ `published` under an operation id, or `declined` with the reason — so each lane's developer
136
+ writes only their own plan. `contract-requests` reads both: an open request is a backend-lane
137
+ gap at `analyze-plan`, a declined one or an answered one not yet re-bound is a frontend-lane
138
+ gap. With no backend plan at all, every binding is a request.
139
+
131
140
  **`UX-` lives here and only here.** The service design excludes the spec's Experience
132
141
  Standards by contract — it realises no experience standard, and a traceability row for one
133
142
  would be a fabrication. So a `UX-` that reaches neither design is a dropped requirement,
@@ -174,6 +174,70 @@ Declaring **`(none)`** for a lane is a real answer and the way a project says it
174
174
 
175
175
  ---
176
176
 
177
+ ## 2a. `.appweaver/lane.json` — the developer's working lane
178
+
179
+ `stacks.json` says which lanes the **project** has. `lane.json` says which of them the
180
+ **developer on this machine** works on — `backend`, `frontend` or `both`:
181
+
182
+ ```json
183
+ {
184
+ "_note": "This developer's working lane: backend, frontend or both. Not committed. …",
185
+ "lane": "frontend",
186
+ "set_by": "select-lane",
187
+ "updated": "2026-10-11T07:46:32Z"
188
+ }
189
+ ```
190
+
191
+ - **Not committed** (the kit's `.gitignore` lists it). A backend developer and a frontend
192
+ developer on one repository each keep their own, so each can work on their lane at a
193
+ different time from the other.
194
+ - **`select-stack` writes the default** after every declaration — `both` when both lanes are
195
+ declared, otherwise the one that is — so a re-stacked project starts again from every
196
+ declared lane. **`select-lane` narrows it**; its options come from `stacks.json` (all three
197
+ when both lanes are declared, only the declared one otherwise, picked without a question).
198
+ - **A missing file reads as the default**, and so does one that names a lane `stacks.json`
199
+ no longer declares or that cannot be read — `working_note` says which. A stale choice never
200
+ switches a lane off.
201
+
202
+ Every reader (`lane-resolve`, `lane-verify`) reports **`<kind>_working`** beside
203
+ `<kind>_declared`, plus `working_lane`, `working_kinds[]` and three fields for callers that
204
+ narrow: `working_kind` (the ONE kind being worked, or empty when every declared lane is),
205
+ `working_root` and `working_test_path` (that lane's two paths, empty likewise). Empty is
206
+ deliberate — passed as `--kind` or `--under`, it means "every declared lane", which is the
207
+ behaviour from before the file existed.
208
+
209
+ **The rule the phases follow: write the working lane, read the declared one.**
210
+
211
+ | Phase | With a working lane narrower than the declared lanes |
212
+ |---|---|
213
+ | `plan` | scaffolds, authors, re-derives and delta-plans only the working lane; the menu and the re-derive question look only at its master. The other lane's plan is read — the frontend plan binds to the backend plan's published surface. The backend half answers the frontend's open contract requests and writes `api-draft.json`; the frontend half re-binds answered requests and raises new ones (§4.5) |
214
+ | `analyze-plan` | gates, repairs and receipts only the working lane's plan (`.aw-plan-gate.<lane>.json`, that lane's verdict); check 3 reads the other plan and the request ledger, and a finding only it can close is reported for its developer (`plan-gaps --lanes` → `other`). The frontend receipt also hashes the backend plan's §3, so a backend contract change re-gates the frontend |
215
+ | `implement` | offers, receipt-checks and generates only the working lane; a refused receipt skips that lane with the reason. The backend half exports the API document; the frontend half reads it, combined with the drafts, and never starts the service (§4.5) |
216
+ | `build` | compiles only the working lane (`build-compile --kind`); `lane-verify --working-only` does not require the other lane's root on disk. With the backend worked, a green compile re-exports the API document |
217
+ | `analyze-slice` | tests only the working lane (`test-run --kind`); the fix loops' file list is narrowed with `concept-paths --under`; the composition pass runs when the backend is worked |
218
+ | `specify`, `finalize-delta`, `close-delta` | **ignore it** — the spec is written whole, and a change merges and closes with whatever its lanes delivered |
219
+
220
+ **A change records the lanes it touches**, separately from anyone's working lane: the
221
+ `**Lanes**:` line of its `delta-spec.md` — `backend`, `frontend`, or `backend, frontend`.
222
+ `specify` asks when it opens a change, and `plan new` when it opens a plan-only one; the
223
+ options come from `stacks.json`, both lanes first as the default, and one declared lane is
224
+ taken without a question. The answer is not narrowed to the working lane of whoever opened
225
+ the change. A delta-spec without the line, or with the template's `[…]` placeholder, touches
226
+ every declared lane. `cr-lanes` (§6) reads and writes it; `cr-list` and `cr-paths` report it,
227
+ and `cr-list --lane` lists only the changes touching the given lanes.
228
+
229
+ What reads it:
230
+
231
+ | Phase | With a change that names fewer lanes than are declared |
232
+ |---|---|
233
+ | `plan` | the change menu lists only the changes touching a working lane (`cr-list --lane`) and names the others in its question; `plan CR-…` naming one of those stops and says why. A change's delta plans are written only for the lanes it names that are worked and have a master |
234
+ | `analyze-plan` | decides master or report mode per lane: a lane is repaired and receipted unless an open change names it (`cr-list --lane`), so a backend-only change leaves the frontend plan gateable while it is in flight. A report-mode lane's gaps are listed with those this run does not repair |
235
+ | `implement` | changes mode only for the changes naming a working lane (`cr-list --lane`); the others are named in a note and the lane builds from its master. On a change, a working lane it does not name is skipped with that reason; `implement CR-…` naming another lane's change stops |
236
+ | `analyze-plan-delta` | lists only the changes naming a working lane, and gates only the lanes the change names that are worked — a backend-only change needs no frontend delta plan. The other developer's delta plan is read for check 3; a gap only it can close is reported for them (`plan-gaps --lanes`), and its absence is a note. `contract-impact` warns when the service delta changes or removes an operation the frontend master binds to that the frontend delta does not name. A developer working one lane of two writes its report as `delta-plan-analyze-report.<lane>.md` |
237
+ | `analyze-slice-delta` | lists only the changes naming a working lane, and gates only the lanes the change names that are worked and have a delta plan. Each gated lane runs its own tests (`test-run --kind`) and has its result written to `changes/<CR-id>/.aw-slice-gate.<lane>.json` (`slice-gate-stamp`) — `pass` when no test failed and its requirement checks are met, else `fail`, with `tests_ran` saying whether a suite actually ran — committed with the change. A named lane somebody else works on is a note: its developer gates it in their own run. A developer working one lane of two writes the report as `delta-slice-analyze-report.<lane>.md` |
238
+
239
+ ---
240
+
177
241
  ## 3. The ownership rule
178
242
 
179
243
  **Lane root folders own their subtree and must be disjoint.** Two generators must never be able
@@ -445,18 +509,34 @@ A layout the table gets wrong is fixed by writing `test_root` on the lane in `st
445
509
  ### 4.5 `contract_run`, `contract` and `client` — the API contract between the lanes
446
510
 
447
511
  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
512
+ the document that counts is the one the **built code** serves. **The backend lane publishes
513
+ it and the frontend lane reads it**, so the two can be built by different developers at
514
+ different times, and a frontend developer never needs the service's code built or its
515
+ runtime installed. Every step warns rather than stops:
516
+
517
+ 1. **Export** (`contract-export`) — run by whoever works on the service lane: `implement`
518
+ after its backend half generates, and `build` after a green compile. Start the service
519
+ with the backend lane's `contract_run`, fetch its `contract` URL, write the document to
453
520
  `<service lane root folder>/contracts/openapi.json`, and stop the service with
454
521
  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.
522
+ the run says it may be older than the code. The document is committed with the service.
523
+ 2. **Draft** (`contract-draft`) — run by the backend lane's `plan` and `analyze-plan`:
524
+ `backend-plan.md` §3 drafted by script into `specs/<ctx>/<feature>/api-draft.json`, an
525
+ OpenAPI document of what the plan says the service will serve. Committed with the plan.
526
+ 3. **Combine** (`contract-combine`) — run by the frontend half of `implement`: the exported
527
+ document plus every drafted operation it does not serve yet, each marked `x-aw-draft`,
528
+ written to `.appweaver/contracts/openapi.combined.json` (not committed). A drafted
529
+ operation whose id the service serves drops out; one whose route and verb the service
530
+ answers under another id is left out and reported.
531
+ 4. **Client** (`contract-client --doc`) — run the frontend lane's `client` command, with
532
+ `{{contract}}` standing for the combined document as seen from where the command runs.
533
+ 5. **Check** (`contract-check`) — every operation id the frontend plan cites must be an
534
+ `operationId` in the combined document; the ids that are only drafts are named apart.
535
+
536
+ Between the plans, what the frontend needs and the backend does not publish travels as a
537
+ **request ledger**: `frontend-plan.md` §5 "Contract deltas needed" holds one `RQ-n` per
538
+ request, and `backend-plan.md` §3 "Requests from the frontend lane" answers each —
539
+ published under an operation id, or declined. `contract-requests` reads both (§6).
460
540
 
461
541
  Each key is read from the lane first and from its profile second, so a lane declared
462
542
  before the keys existed still answers them. Where the values come from:
@@ -604,10 +684,17 @@ implementation and the twins cannot drift.
604
684
  | `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
685
  | `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
686
  | `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 |
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 |
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 |
687
+ | `contract-draft --feature-dir p` | draft `api-draft.json` beside the feature's plans from `backend-plan.md`'s API section, by script: each `### \`VERB /route\`` endpoint with its operation id, request and response field shapes (conceptual types → JSON schema, a use case's DTO standing in for a named shape) and error statuses. Rewritten only when it differs; removed when the plan names no endpoint (§4.5) |
688
+ | `contract-combine` | write `.appweaver/contracts/openapi.combined.json`: the exported document plus every feature's drafted operations it does not serve, marked `x-aw-draft`. `ok:false` with a reason when there is neither (§4.5) |
689
+ | `contract-requests --feature-dir p` | the request ledger: the frontend plan's `RQ-n` rows against the backend plan's answers — `open`, `published` (and whether the frontend still has to re-bind), `declined` — with one line per lane's to-do for a prompt to quote |
690
+ | `contract-client [--doc d]` | `run-phase --phase client` for the frontend lane — its `client` command, `{{contract}}` expanded to `--doc` (`implement` passes the combined document), else to the exported one |
691
+ | `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; never the request table) against the document's `operationId`s — by script, no model. `drafted[]` names the cited ids that are only draft operations |
692
+ | `contract-impact --delta-backend p --frontend-plan p [--delta-frontend p]` | the operations a change's service delta marks `changed` or `removed` in its "Contract deltas for the frontend lane" table that the frontend master plan binds to, and which of them the change's frontend delta plan does not name (`unaddressed[]`) — `analyze-plan-delta`'s warning. By script, no model |
693
+ | `lane-resolve [--kind k] [--id lane] [--feature-dir p]` | 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 — and the working-lane fields of §2a |
694
+ | `lane-verify [--require backend\|frontend]… [--allow-missing-root] [--working-only] [--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. `--working-only` checks on disk only the working lanes' root folders and the required kinds (§2a) |
695
+ | `lane-select [--lane backend\|frontend\|both] [--default] [--preselect text]` | read or write `.appweaver/lane.json` (§2a). No argument reads it and returns the `select_options[]` the declared stacks offer; `--lane` writes one of them; `--default` writes the default — `select-stack`'s call |
696
+ | `cr-lanes [--cr-id id [--feature-dir p] [--stamp] [--set both\|backend\|frontend]]` | the lanes a change touches (§2a). No `--cr-id` returns the `select_options[]` the declared stacks offer, both first; with it, reads that change's `**Lanes**:` line — `lanes[]` is what it touches among the lanes declared now, every declared lane when nothing is recorded. `--set` writes an answer (a whole option line is accepted); `--stamp` alone writes every declared lane |
697
+ | `slice-gate-stamp --cr-id id --lane backend\|frontend [--feature-dir p] [--tests-green b] [--tests-ran b] [--requirements-ok b] [--findings json]` | write one lane's `analyze-slice-delta` result to `changes/<id>/.aw-slice-gate.<lane>.json` (§2a): `verdict` `pass` when no test failed and the requirements are met, else `fail`, with `tests_ran` (false when the lane declares no test command, so nothing ran), the findings counts and the sha256 of the delta-spec and the lane's delta plan it was gated against. Findings that cannot be read are recorded as `null`, never as none |
611
698
  | `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` |
612
699
 
613
700
  Both `lane-declare` branches — declaring a lane and declaring `none` — emit the **same key
@@ -618,7 +705,7 @@ set**, so a caller binds outputs without knowing which branch ran.
618
705
  | Subcommand | With a declaration | With none |
619
706
  |---|---|---|
620
707
  | `build-compile` | every declared lane's own `build`, via `run-phase` | the historic first-match-wins ladder |
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 |
708
+ | `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. `--kind` runs one lane only; `--kind frontend` always delegates, since the VSTest path runs the service's suite | the historic ladder |
622
709
  | `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
710
  | `find-host-project` | searches the service lane root folder, and reports the lane's `ecosystem` | searches `src/` |
624
711
  | `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` |
@@ -42,8 +42,17 @@
42
42
  #
43
43
  # Runs in the CALLER's context (include semantics): shares vars + the sticky standards
44
44
  # (plan's per-lane rule sets) + the caller's scripts map + replay cache.
45
+ # ONLY THE WORKING LANES THE CHANGE NAMES ARE AUTHORED. be_existing / fe_existing arrive
46
+ # from the caller already narrowed to the developer's working lanes (.appweaver/lane.json)
47
+ # and to the lanes on the change's **Lanes** line, so a lane that is somebody else's, or
48
+ # that the change does not touch, gets no delta plan here. The other half of the change is
49
+ # READ: the frontend delta plan binds to delta_backend_plan_read, the change's service delta
50
+ # plan as the backend developer left it — absent for a frontend-only change, whose bindings
51
+ # are to the master's published surface.
52
+ #
45
53
  # EXPECTS caller vars: feature_dir, spec_file, backend_plan, frontend_plan, domain_model,
46
54
  # be_existing, fe_existing, fe_lane_id, source_files, source_count, USER_INPUT,
55
+ # delta_backend_plan_read,
47
56
  # and — resolved by the caller from the change it chose and activated — cr_id, cr_dir,
48
57
  # delta_spec, delta_model, wave, predecessor_files, predecessor_count, predecessor_ids,
49
58
  # sibling_count, sibling_ids, related_docs.
@@ -82,6 +91,7 @@ vars:
82
91
  related_docs: []
83
92
  delta_backend_plan: ""
84
93
  delta_frontend_plan: ""
94
+ delta_backend_plan_read: ""
85
95
 
86
96
  workflow:
87
97
 
@@ -134,13 +144,16 @@ workflow:
134
144
  # the lane's ratified technology base, the same one
135
145
  # the master plan is authored under — no domain.
136
146
  - { path: "{{cr_dir}}/delta-slice-analyze-report.md", optional: true }
147
+ - { path: "{{cr_dir}}/delta-slice-analyze-report.backend.md", optional: true }
137
148
  # WHAT THE LAST GATE FOUND, when this change
138
- # has already been through analyze-slice-delta.
139
- # A `plan` finding there says THIS document is
140
- # what was wrong, and re-deriving it is how that
141
- # finding is closed — the alternative is hand-
142
- # editing a generated plan, which every
143
- # authoring rule forbids. Absent on a first pass.
149
+ # has already been through analyze-slice-delta —
150
+ # the run that gated every lane, and the one that
151
+ # gated the backend alone. A `plan` finding there
152
+ # says THIS document is what was wrong, and
153
+ # re-deriving it is how that finding is closed —
154
+ # the alternative is hand-editing a generated
155
+ # plan, which every authoring rule forbids.
156
+ # Absent on a first pass.
144
157
  - { path: .appweaver/DESIGN-FORMAT.md }
145
158
  - id: author_backend_delta
146
159
  condition: "{{be_existing}} == true"
@@ -159,8 +172,10 @@ workflow:
159
172
  IS, and a delta that ignores the difference plans an edit to something that is not
160
173
  there. Where the two disagree, plan against the code and record the divergence as
161
174
  a WA-n.
162
- - THE LAST GATE'S REPORT, when one is in context (delta-slice-analyze-report.md).
163
- It records what analyze-slice-delta found the last time this change was gated. A
175
+ - THE LAST GATE'S REPORT, when one is in context (delta-slice-analyze-report.md from
176
+ a run that gated every lane, delta-slice-analyze-report.backend.md from one that
177
+ gated the backend alone; when both are, the one with the later date is the last
178
+ gate). It records what analyze-slice-delta found the last time this change was gated. A
164
179
  finding tagged `fix: plan` says THIS document was what was wrong — re-derive the
165
180
  rows it names, against the code it names. EVIDENCE, never a requirement source: the
166
181
  delta-spec stays the only authority on what the change must do, and a finding
@@ -245,21 +260,26 @@ workflow:
245
260
  - { path: "{{delta_model}}", optional: true }
246
261
  - "{{spec_file}}" # the current master spec (READ ONLY)
247
262
  - "{{frontend_plan}}" # the current master plan (READ ONLY — never amended here)
248
- - { path: "{{delta_backend_plan}}", optional: true }
263
+ - { path: "{{delta_backend_plan_read}}", optional: true }
249
264
  # THE SERVICE HALF of this same change — its contract
250
- # table is what this lane binds to. Optional: an
265
+ # table is what this lane binds to. READ here, and
266
+ # authored above only when the backend is a working
267
+ # lane: another developer may own it. Optional: an
251
268
  # experience-only change has no service delta, and a
252
269
  # frontend-only project has no service lane at all.
253
270
  - { path: "{{domain_model}}", optional: true }
254
271
  - { path: .appweaver/memory/constitution.ui.md, optional: true }
255
272
  - { path: "{{cr_dir}}/delta-slice-analyze-report.md", optional: true }
273
+ - { path: "{{cr_dir}}/delta-slice-analyze-report.frontend.md", optional: true }
256
274
  # WHAT THE LAST GATE FOUND, when this change
257
- # has already been through analyze-slice-delta.
258
- # A `plan` finding there says THIS document is
259
- # what was wrong, and re-deriving it is how that
260
- # finding is closed — the alternative is hand-
261
- # editing a generated plan, which every
262
- # authoring rule forbids. Absent on a first pass.
275
+ # has already been through analyze-slice-delta —
276
+ # the run that gated every lane, and the one that
277
+ # gated the frontend alone. A `plan` finding there
278
+ # says THIS document is what was wrong, and
279
+ # re-deriving it is how that finding is closed —
280
+ # the alternative is hand-editing a generated
281
+ # plan, which every authoring rule forbids.
282
+ # Absent on a first pass.
263
283
  - { path: .appweaver/DESIGN-FORMAT.md }
264
284
  - id: author_frontend_delta
265
285
  condition: "{{fe_existing}} == true"
@@ -276,8 +296,10 @@ workflow:
276
296
  feature: {{source_files}}. Read the ones this change touches — a delta that plans
277
297
  an edit to a component that is not there, or that duplicates one that is, costs a
278
298
  whole implementation pass to discover.
279
- - THE LAST GATE'S REPORT, when one is in context (delta-slice-analyze-report.md).
280
- It records what analyze-slice-delta found the last time this change was gated. A
299
+ - THE LAST GATE'S REPORT, when one is in context (delta-slice-analyze-report.md from
300
+ a run that gated every lane, delta-slice-analyze-report.frontend.md from one that
301
+ gated the frontend alone; when both are, the one with the later date is the last
302
+ gate). It records what analyze-slice-delta found the last time this change was gated. A
281
303
  finding tagged `fix: plan` says THIS document was what was wrong — re-derive the
282
304
  rows it names, against the code it names. EVIDENCE, never a requirement source: the
283
305
  delta-spec stays the only authority on what the change must do, and a finding
@@ -25,11 +25,17 @@
25
25
  # Runs in the CALLER's context (include semantics): shares vars + the sticky standards
26
26
  # (specify's DDD authority + the selected domain rule files) + the caller's scripts map
27
27
  # + replay cache.
28
+ # THE CHANGE'S LANES. Before opening the change this module asks which lanes it touches —
29
+ # both (the default), backend or frontend, from the stacks declared — and stamps the answer
30
+ # on the delta-spec's **Lanes** line. That is the only thing the answer does here: the
31
+ # delta is still specified whole. It does not follow .appweaver/lane.json either; the
32
+ # question is about the change, not about who is running specify.
33
+ #
28
34
  # EXPECTS caller vars: feature_dir, spec_file, USER_INPUT, domain_model,
29
35
  # pending_model_files.
30
- # PRODUCES: cr_id, cr_dir, delta_spec, delta_model, wave, open_count (and, via cr-setup,
31
- # an OPEN change). The change's LANE PLANS are scaffolded by `plan`, per declared
32
- # lane — the spec phase does not know which lanes exist.
36
+ # PRODUCES: cr_id, cr_dir, delta_spec, delta_model, wave, open_count, change_lanes_csv (and,
37
+ # via cr-setup, an OPEN change). The change's LANE PLANS are scaffolded by `plan`,
38
+ # per declared lane.
33
39
  # cr_dir is what lets the caller's quality loop target THIS change
34
40
  # (create-checklist --kind delta).
35
41
  # =============================================================================
@@ -46,8 +52,38 @@ scripts:
46
52
  sh: .appweaver/scripts/bash/aw-spec-actions.sh
47
53
  ps1: .appweaver/scripts/powershell/aw-spec-actions.ps1
48
54
 
55
+ vars:
56
+ change_lane_options: []
57
+ change_lane_has: "false" # the declared stacks offer an answer (no lane declared ⇒ nothing to ask)
58
+ change_lanes: "" # the answer picked; empty ⇒ every declared lane is stamped
59
+ change_lanes_csv: ""
60
+
49
61
  workflow:
50
62
 
63
+ # THE LANES THIS CHANGE TOUCHES, asked before the change is opened so that a cancelled
64
+ # answer leaves nothing behind. The options come from stacks.json: both — listed first,
65
+ # so it is the default — then each lane alone. One declared lane is the only answer and
66
+ # is taken without a question; with no lane declared there is nothing to ask. The answer
67
+ # is written onto the delta-spec's **Lanes** line once the change exists. The spec half
68
+ # below is authored whole whatever the answer: which lanes plan and build a change is a
69
+ # delivery question, not a requirements one.
70
+ - id: change_lane_options
71
+ action: execute_script
72
+ script: { ref: actions }
73
+ subcommand: cr-lanes
74
+ outputs:
75
+ - { name: change_lane_options, from: "$.select_options" }
76
+ - { name: change_lane_has, from: "$.has_options" }
77
+
78
+ - id: ask_change_lanes
79
+ condition: "{{change_lane_has}} == true"
80
+ action: user_select
81
+ prompt: "Which lanes does this change touch? The answer is recorded on the change's **Lanes** line as the lanes that plan and build it; the spec is written whole either way."
82
+ options_from_variable: change_lane_options
83
+ single_option_confirm: true
84
+ outputs:
85
+ - { name: change_lanes, from: selection }
86
+
51
87
  # Open the change + scaffold its TWO documents: the delta-spec under changes/<cr-id>/,
52
88
  # and the model delta BESIDE the context's master model. The delta PLANS are not
53
89
  # scaffolded here — `plan` owns them and creates them with delta-plan-scaffold when it
@@ -67,6 +103,19 @@ workflow:
67
103
  - { name: wave, from: "$.wave" }
68
104
  - { name: open_count, from: "$.open_count" }
69
105
 
106
+ # The answer, onto the delta-spec's **Lanes** line — before the author fills the document
107
+ # around it. --set goes last: an empty answer is dropped by PowerShell 5.1, and the
108
+ # script then stamps every declared lane. A failure leaves the template placeholder,
109
+ # which reads as every declared lane, so it does not stop the change from being written.
110
+ - id: stamp_change_lanes
111
+ action: execute_script
112
+ on_error: continue
113
+ script: { ref: actions }
114
+ subcommand: cr-lanes
115
+ args: ["--cr-id", "{{cr_id}}", "--feature-dir", "{{feature_dir}}", "--stamp", "--set", "{{change_lanes}}"]
116
+ outputs:
117
+ - { name: change_lanes_csv, from: "$.lanes_csv" }
118
+
70
119
  - id: ctx
71
120
  action: build_context
72
121
  context_from: pending_model_files # EVERY other open delta's model change, so this
@@ -142,7 +191,10 @@ workflow:
142
191
  Wave rationale — that is a wave dependency, and the wave must then be RAISED above
143
192
  that delta's — name it under `## Depends on` and the wave is raised from that. Do not silently assume their
144
193
  changes: until merged they are proposals, not the master.
145
- - Do NOT edit the Wave / Created / Base version headers — scripts stamp those.
194
+ - Do NOT edit the Wave / Lanes / Created / Base version headers — scripts stamp
195
+ those. Lanes ({{change_lanes_csv}}) says which lanes plan and build this change;
196
+ it does NOT narrow what you write here — the delta is specified whole, behaviour and
197
+ experience alike.
146
198
  Change request: {{USER_INPUT}}
147
199
 
148
200
  # The MODEL half of the same change, authored in the same run so the model can never
@@ -33,6 +33,16 @@
33
33
  # caller reads none of.
34
34
  # command — the phase being gated, quoted back in the failure message
35
35
  #
36
+ # THE WORKING LANE. A declared lane that the developer on this machine does not work on
37
+ # (.appweaver/lane.json — select-stack writes the default, select-lane narrows it) is not
38
+ # required to be on disk here: a backend developer's build must not stop because the
39
+ # frontend lane, somebody else's, has not been scaffolded yet. `--working-only` narrows the
40
+ # on-disk check to the working lanes and the required kind; a lane with no root and two
41
+ # lanes overlapping are still checked for every lane. The working lane's fields come back
42
+ # too — `working_kind` is the ONE kind being worked, or empty when every declared lane is,
43
+ # which is what a caller passes as `--kind` so that both lanes behave as before — and
44
+ # `be_working` / `fe_working` say per kind whether it is declared and worked here.
45
+ #
36
46
  # Produces, for the required kind: WHERE it lives (lane_root / lane_source_path),
37
47
  # WHAT it is (lane_profile / lane_title / lane_language / lane_ecosystem) and HOW it
38
48
  # is driven (lane_build / lane_test / lane_run), plus both kinds' flattened fields,
@@ -66,9 +76,15 @@ workflow:
66
76
  action: execute_script
67
77
  script: { ref: actions }
68
78
  subcommand: lane-verify
69
- args: ["--require", "{{require}}", "--command", "{{command}}"]
79
+ args: ["--require", "{{require}}", "--command", "{{command}}", "--working-only"]
70
80
  outputs:
71
81
  - { name: lane_ok, from: "$.ok" }
82
+ - { name: be_working, from: "$.backend_working" }
83
+ - { name: fe_working, from: "$.frontend_working" }
84
+ - { name: working_lane, from: "$.working_lane" }
85
+ - { name: working_kind, from: "$.working_kind" }
86
+ - { name: working_root, from: "$.working_root" }
87
+ - { name: working_test_path, from: "$.working_test_path" }
72
88
  - { name: lane_issues, from: "$.message" }
73
89
  - { name: be_lane_id, from: "$.backend_lane_id" }
74
90
  - { name: be_source_path, from: "$.backend_source_path" }
@@ -230,9 +230,18 @@ does none.]
230
230
  |---|---|---|---|---|---|---|
231
231
  | `[Screen]` | `[OperationId]` | `[operation]` | [field: type, …] | [field: type, …] | [§4 state] | FR-00X · IC-00X |
232
232
 
233
- **Contract deltas needed**: [anything the experience requires that the contract
234
- does not offer — raised back to the service lane, never worked around in the
235
- client. Or `None`.]
233
+ **Contract deltas needed**
234
+
235
+ <!-- THE REQUEST LEDGER. One row per operation the experience needs that the
236
+ contract does not offer — raised back to the service lane, never worked
237
+ around in the client. Each request keeps its RQ-n for life. The backend plan
238
+ answers in its "Requests from the frontend lane" table, published under an
239
+ operation id or declined with the reason; an answered request is re-bound
240
+ above to that id. Write `None` instead of the table when there is none. -->
241
+
242
+ | Request | Operation id | Needed by | Sends (field shape) | Receives (field shape) | Serves |
243
+ |---|---|---|---|---|---|
244
+ | RQ-[n] | `[ProposedOperationId]` | `[Screen / component]` | [field: type, …] | [field: type, …] | FR-00X |
236
245
 
237
246
  ## 6. Cross-Cutting
238
247