aw-spec-cli 0.1.21 → 0.1.22
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/.appweaver/STACK-PROFILES.md +87 -8
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +30 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +41 -8
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +62 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +18 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +115 -27
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +225 -27
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +39 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +28 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +100 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +141 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +386 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-workflow-model.orch.yaml +135 -0
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +190 -209
- package/assets/.appweaver/scripts/bash/aw-xref.sh +4 -1
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +182 -224
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +4 -1
- package/assets/.appweaver/scripts/python/aw-crstate.py +127 -2
- package/assets/.appweaver/scripts/python/aw-gaps.py +119 -0
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +17 -1
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +84 -4
- package/assets/.appweaver/scripts/python/aw-plangate.py +216 -0
- package/assets/.appweaver/scripts/python/aw-principles.py +108 -0
- package/assets/.appweaver/scripts/python/aw-stack.py +6 -0
- package/assets/.appweaver/scripts/python/aw-xref.py +141 -4
- package/assets/.appweaver/stack-profiles/angular.json +26 -0
- package/assets/.appweaver/stack-profiles/blazor-wasm.json +38 -0
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +38 -0
- package/assets/.appweaver/stack-profiles/java-spring.json +19 -0
- package/assets/.appweaver/stack-profiles/nextjs.json +26 -0
- package/assets/.appweaver/stack-profiles/node-express.json +26 -0
- package/assets/.appweaver/stack-profiles/python-fastapi.json +15 -0
- package/assets/.appweaver/stack-profiles/react-vite.json +26 -0
- package/assets/.appweaver/stack-profiles/sveltekit.json +26 -0
- package/assets/.appweaver/stack-profiles/vue-vite.json +26 -0
- package/assets/.appweaver/templates/ui-model-template.md +67 -0
- package/assets/.appweaver/templates/workflow-model-template.md +62 -0
- package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +44 -0
- package/package.json +1 -1
|
@@ -51,15 +51,17 @@ no heading to key on. Where the two lanes are folded into one command, as `plan`
|
|
|
51
51
|
| `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
52
|
| `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
53
|
| `root` | the lane's **source path** — the subtree it owns (see §3). `--source-path` and `--root` are two spellings of this one field |
|
|
54
|
+
| `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 |
|
|
54
55
|
| `run_from` | *(optional)* **where the commands run**: `repo` (the default, and the shape of every lane written before this key existed) at the repository root, or `root` inside the lane's own source path. See §3.1 |
|
|
55
56
|
| `build` / `test` / `run` | project-supplied command strings the engine shells out to. These are the matched **profile's** defaults and may not fit a legacy layout — reviewing them is what `select-stack` is for. `{{root}}` in a command resolves to this lane's `root` (§4.2) |
|
|
56
57
|
| `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) |
|
|
57
58
|
|
|
58
59
|
### Three properties matter more than the schema
|
|
59
60
|
|
|
60
|
-
1. **`root` is ownership, and it is the source path.** `implement`'s service lane writes
|
|
61
|
-
under a backend lane's root and its frontend lane only under a frontend lane's,
|
|
62
|
-
`build`/`test`/`run` act on it, and `onboard` reverse-engineers out of it.
|
|
61
|
+
1. **`root` is ownership, and it is the source path.** `implement`'s service lane writes its
|
|
62
|
+
code only under a backend lane's root and its frontend lane only under a frontend lane's,
|
|
63
|
+
`build`/`test`/`run` act on it, and `onboard` reverse-engineers out of it. The lane's
|
|
64
|
+
**test path** (§4.3) is owned the same way, and is where `implement` writes the lane's tests.
|
|
63
65
|
2. **The commands are project-supplied strings.** The engine stays stdlib-only and
|
|
64
66
|
stack-ignorant. This is how `build` already avoids knowing what a compiler is, and it
|
|
65
67
|
is the only thing that makes "any frontend stack" tractable.
|
|
@@ -151,12 +153,17 @@ Declaring **`(none)`** for a lane is a real answer and the way a project says it
|
|
|
151
153
|
## 3. The ownership rule
|
|
152
154
|
|
|
153
155
|
**Lane roots own their subtree and must be disjoint.** Two generators must never be able
|
|
154
|
-
to write the same file.
|
|
156
|
+
to write the same file. A lane owns its test path too, so the rule covers both: no source or
|
|
157
|
+
test path of one lane may overlap a source or test path of another.
|
|
155
158
|
|
|
156
|
-
`lane-declare` refuses a declaration whose
|
|
159
|
+
`lane-declare` refuses a declaration whose paths contain, or are contained by, another
|
|
157
160
|
lane's — compared as `/`-terminated paths, so `src/` is clear of `src-gen/` but collides
|
|
158
161
|
with `src/api/`. The refusal is a non-zero exit and **`stacks.json` is left untouched**.
|
|
159
162
|
|
|
163
|
+
The one write `implement` may make outside a lane's two paths is registering a test project
|
|
164
|
+
it had to create, in the file the lane's test command discovers tests through — the solution
|
|
165
|
+
file for .NET. `analyze-slice-delta`'s placement check accepts that edit and nothing else.
|
|
166
|
+
|
|
160
167
|
This is checked at declaration time on purpose. The alternative — discovering it when two
|
|
161
168
|
implement phases fight over a file — is a much worse place to find out.
|
|
162
169
|
|
|
@@ -176,12 +183,15 @@ workflow.**
|
|
|
176
183
|
"language": "TypeScript", // shown in the menu, parenthesised
|
|
177
184
|
"ecosystem": "node", // which stack-partitioned corpus this stack draws from
|
|
178
185
|
"root": "web/", // the lane root this profile defaults to
|
|
186
|
+
"test_root": "", // optional — where tests go when not inside root — see §4.3
|
|
179
187
|
"run_from": "root", // where the commands run — see §3.1
|
|
180
188
|
"build": "npm run build",
|
|
181
189
|
"test": "npm test",
|
|
182
190
|
"run": "npm run dev",
|
|
183
191
|
"migrate": "", // optional — schema migration, if the stack has one
|
|
184
192
|
"prototype_run": "npm run dev --prefix prototype", // frontend only — fills the prototype block
|
|
193
|
+
"reserved_names": ["Event", "Request", "Response"], // optional — names this stack's AMBIENT
|
|
194
|
+
// scope already defines; see §4.4
|
|
185
195
|
"detect": ["web/package.json", "web/vite.config.*"] // ANY-of markers — how stack-detect recognises this stack
|
|
186
196
|
}
|
|
187
197
|
```
|
|
@@ -206,10 +216,23 @@ trees that are physically per-ecosystem:
|
|
|
206
216
|
compiling project and a component runbook edits a host project's build files — both are
|
|
207
217
|
executable artifacts in one toolchain, so offering a `dotnet` bundle to a Spring repo does not
|
|
208
218
|
produce a scaffold, it produces a second foreign project beside the real one. `foundation-list`
|
|
209
|
-
therefore narrows
|
|
210
|
-
|
|
219
|
+
therefore narrows by ecosystem, and reports `all_foundations` alongside so the gate can say
|
|
220
|
+
*what exists and why none of it applies*. `--ecosystem` narrows to the ONE lane being
|
|
221
|
+
scaffolded, which is what `foundation` passes: a repo declaring a .NET service and a Node UI
|
|
222
|
+
must not be offered its .NET bundle while the UI lane is the target. Without that argument the
|
|
223
|
+
filter falls back to every declared lane's ecosystem, and a repo that has declared nothing yet
|
|
211
224
|
is not filtered — there is no answer to filter on.
|
|
212
225
|
|
|
226
|
+
**A scaffold lands in a lane.** `foundation` unpacks a bundle into the SOURCE PATH of the lane
|
|
227
|
+
it is scaffolding — never the repository root, and there is no output-directory argument. One
|
|
228
|
+
bundle fills one lane, so a fullstack repo runs `foundation` once per lane. This is what makes
|
|
229
|
+
every phase's "write only into your lane" rule true of the scaffold as well as of generated
|
|
230
|
+
code: `implement`, `build`, `test` and `run` all resolve the same path the bundle unpacked into.
|
|
231
|
+
A pack carrying its own test tree has it lifted onto the lane's **test path** (§4.3) afterwards,
|
|
232
|
+
because for most backend stacks that path is a sibling of the source path rather than a
|
|
233
|
+
directory inside it. Lanes must therefore be declared before `foundation` runs, which is why
|
|
234
|
+
`select-stack` is the first command of both sequences.
|
|
235
|
+
|
|
213
236
|
The technical RULE corpora at `guardrails/technology/<stack>/` are keyed by **profile id**, not
|
|
214
237
|
ecosystem, because Spring and Quarkus are both `java` and do not share a persistence rule. They
|
|
215
238
|
are selected through the project's `tech-rule-bindings.md` rather than filtered here — see §7.
|
|
@@ -274,6 +297,61 @@ about a backend, and the two errors are not symmetric. A false positive authors
|
|
|
274
297
|
the project never asked for; a false negative only produces a warning that names the fix. Pick
|
|
275
298
|
ids accordingly — a profile whose id is a common English word will be matched too eagerly.
|
|
276
299
|
|
|
300
|
+
### 4.4 `reserved_names` — names the stack already defines
|
|
301
|
+
|
|
302
|
+
A stack's ambient scope defines types nobody imported: C#'s implicit usings put `System`,
|
|
303
|
+
`System.Threading.Tasks`, `Microsoft.AspNetCore.Http` and `Microsoft.AspNetCore.Routing` in
|
|
304
|
+
every file of a `Microsoft.NET.Sdk.Web` project; a browser bundle has `Event`, `Request` and
|
|
305
|
+
`Response`; `java.lang` is always imported; `typing` is star-imported by convention.
|
|
306
|
+
|
|
307
|
+
A domain noun that matches one of those is **not a naming mistake** — `Route`, `Task`,
|
|
308
|
+
`Order` and `Activity` are ordinary domain vocabulary, the plan carries the ubiquitous
|
|
309
|
+
language, and the generator spells it verbatim. But written bare it is ambiguous, and in C#
|
|
310
|
+
it does not compile (CS0104). The conflict is invisible in the file: the framework name
|
|
311
|
+
arrives through an implicit import and appears in no line of it.
|
|
312
|
+
|
|
313
|
+
`reserved_names` lists the ones that are BOTH ambient on this stack AND plausible domain
|
|
314
|
+
vocabulary — curated, not an exhaustive dump of the standard library. `lane-resolve` and
|
|
315
|
+
`lane-verify` report it as `<kind>_reserved_names` / `<kind>_reserved_csv`; `implement`
|
|
316
|
+
passes it to `plan-coverage --reserved`, which intersects it with the plan's own element
|
|
317
|
+
names and hands the generating lane the actual collisions before it writes anything. The
|
|
318
|
+
per-stack resolution convention is in that stack's rule corpus — for `dotnet-webapi`, in
|
|
319
|
+
[[framework-api-map]]: qualify with the enclosing namespace segment (`Domain.Route`), never
|
|
320
|
+
rename the domain type.
|
|
321
|
+
|
|
322
|
+
Absent, nothing is reported and generation is unchanged.
|
|
323
|
+
|
|
324
|
+
### 4.3 `test_root` — where a lane's tests go
|
|
325
|
+
|
|
326
|
+
Most backend stacks keep their tests **beside** the source, not inside it: .NET's `tests/` next
|
|
327
|
+
to `src/`, Maven's `src/test/java/` next to `src/main/java/`, pytest's `tests/` next to `app/`.
|
|
328
|
+
A lane that owned only its source path could not hold its own tests, so each lane also owns a
|
|
329
|
+
**test path**, and `implement` writes the lane's paired tests there.
|
|
330
|
+
|
|
331
|
+
A profile's `test_root` is the test path that pairs with its default `root`. `lane-resolve` and
|
|
332
|
+
`lane-verify` report it per kind as `backend_test_path` / `frontend_test_path`, worked out at
|
|
333
|
+
read time:
|
|
334
|
+
|
|
335
|
+
| The lane | Its test path |
|
|
336
|
+
|---|---|
|
|
337
|
+
| declares `test_root` itself | that value |
|
|
338
|
+
| sits at the profile's default root (`src/`) | the profile's `test_root` (`tests/`) |
|
|
339
|
+
| sits at a path ending in the default root (`backend/src/`) | the profile's `test_root` under the same prefix (`backend/tests/`) |
|
|
340
|
+
| anything else, or the profile declares no `test_root` | the source path itself |
|
|
341
|
+
|
|
342
|
+
The last row is right for every shipped frontend (tests under `web/tests/`), for Go's
|
|
343
|
+
colocated `_test.go` files, and for a Maven lane rooted at the directory holding its
|
|
344
|
+
`pom.xml`, where `src/test/java/` is already inside the lane. A layout the table gets wrong is
|
|
345
|
+
fixed by writing `test_root` on the lane in `stacks.json`.
|
|
346
|
+
|
|
347
|
+
| Profile | `root` | `test_root` |
|
|
348
|
+
|---|---|---|
|
|
349
|
+
| `dotnet-webapi` | `src/` | `tests/` |
|
|
350
|
+
| `java-spring` | `src/main/java/` | `src/test/java/` |
|
|
351
|
+
| `python-fastapi` | `app/` | `tests/` |
|
|
352
|
+
| `node-express` | `src/` | `tests/` |
|
|
353
|
+
| `go-chi` and every frontend | — | *(none — inside the source path)* |
|
|
354
|
+
|
|
277
355
|
---
|
|
278
356
|
|
|
279
357
|
## 5. Who may write this file
|
|
@@ -398,7 +476,8 @@ set**, so a caller binds outputs without knowing which branch ran.
|
|
|
398
476
|
| `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 |
|
|
399
477
|
| `run-migrate` | a lane's declared `migrate` (§4.2) when there is one; otherwise `dotnet ef` searched under the **service lane's root** rather than an assumed `src/` | `dotnet ef` under `src/` |
|
|
400
478
|
| `find-host-project` | searches the service lane's root, and reports the lane's `ecosystem` | searches `src/` |
|
|
401
|
-
| `foundation-list`, `list-components` | narrowed to the
|
|
479
|
+
| `foundation-list`, `list-components` | narrowed to the `ecosystem` of the lane being scaffolded, else of every declared lane (§4.1) | unfiltered / `dotnet` |
|
|
480
|
+
| `foundation-unpack` | unpacks into the lane's **source path**, and lifts a packed test tree onto its **test path** (§4.1, §4.3) | cannot run — `foundation` stops until a lane is declared |
|
|
402
481
|
| `resolve-tech` | expands stack placeholders and flags a corpus mismatch (§7a) | no placeholders resolve; nothing is flagged |
|
|
403
482
|
|
|
404
483
|
**Why `--scope slice` is the one exception.** VSTest's `--filter FullyQualifiedName~` is the
|
|
@@ -130,6 +130,14 @@ workflow:
|
|
|
130
130
|
# the context's CANONICAL model — the aggregate root,
|
|
131
131
|
# its entities and its invariants live here, not in the
|
|
132
132
|
# spec and not in this plan.
|
|
133
|
+
- { path: "{{cr_dir}}/delta-slice-analyze-report.md", optional: true }
|
|
134
|
+
# WHAT THE LAST GATE FOUND, when this change
|
|
135
|
+
# has already been through analyze-slice-delta.
|
|
136
|
+
# A `plan` finding there says THIS document is
|
|
137
|
+
# what was wrong, and re-deriving it is how that
|
|
138
|
+
# finding is closed — the alternative is hand-
|
|
139
|
+
# editing a generated plan, which every
|
|
140
|
+
# authoring rule forbids. Absent on a first pass.
|
|
133
141
|
- { path: .appweaver/DESIGN-FORMAT.md }
|
|
134
142
|
- id: author_backend_delta
|
|
135
143
|
condition: "{{be_existing}} == true"
|
|
@@ -148,6 +156,13 @@ workflow:
|
|
|
148
156
|
IS, and a delta that ignores the difference plans an edit to something that is not
|
|
149
157
|
there. Where the two disagree, plan against the code and record the divergence as
|
|
150
158
|
a WA-n.
|
|
159
|
+
- THE LAST GATE'S REPORT, when one is in context (delta-slice-analyze-report.md).
|
|
160
|
+
It records what analyze-slice-delta found the last time this change was gated. A
|
|
161
|
+
finding tagged `fix: plan` says THIS document was what was wrong — re-derive the
|
|
162
|
+
rows it names, against the code it names. EVIDENCE, never a requirement source: the
|
|
163
|
+
delta-spec stays the only authority on what the change must do, and a finding
|
|
164
|
+
tagged `implement` or `specify` is closed elsewhere and is not this pass's work.
|
|
165
|
+
Absent on a first pass, which is the normal case.
|
|
151
166
|
- Classify every change to the plan as ADDED / MODIFIED / REMOVED against the
|
|
152
167
|
master, per the master's OWN section structure (whatever sections it actually has
|
|
153
168
|
— do not assume a fixed layering), plus the cross-section impact, the regression
|
|
@@ -231,6 +246,14 @@ workflow:
|
|
|
231
246
|
# frontend-only project has no service lane at all.
|
|
232
247
|
- { path: "{{domain_model}}", optional: true }
|
|
233
248
|
- { path: .appweaver/memory/constitution.ui.md, optional: true }
|
|
249
|
+
- { path: "{{cr_dir}}/delta-slice-analyze-report.md", optional: true }
|
|
250
|
+
# WHAT THE LAST GATE FOUND, when this change
|
|
251
|
+
# has already been through analyze-slice-delta.
|
|
252
|
+
# A `plan` finding there says THIS document is
|
|
253
|
+
# what was wrong, and re-deriving it is how that
|
|
254
|
+
# finding is closed — the alternative is hand-
|
|
255
|
+
# editing a generated plan, which every
|
|
256
|
+
# authoring rule forbids. Absent on a first pass.
|
|
234
257
|
- { path: .appweaver/DESIGN-FORMAT.md }
|
|
235
258
|
- id: author_frontend_delta
|
|
236
259
|
condition: "{{fe_existing}} == true"
|
|
@@ -247,6 +270,13 @@ workflow:
|
|
|
247
270
|
feature: {{source_files}}. Read the ones this change touches — a delta that plans
|
|
248
271
|
an edit to a component that is not there, or that duplicates one that is, costs a
|
|
249
272
|
whole implementation pass to discover.
|
|
273
|
+
- THE LAST GATE'S REPORT, when one is in context (delta-slice-analyze-report.md).
|
|
274
|
+
It records what analyze-slice-delta found the last time this change was gated. A
|
|
275
|
+
finding tagged `fix: plan` says THIS document was what was wrong — re-derive the
|
|
276
|
+
rows it names, against the code it names. EVIDENCE, never a requirement source: the
|
|
277
|
+
delta-spec stays the only authority on what the change must do, and a finding
|
|
278
|
+
tagged `implement` or `specify` is closed elsewhere and is not this pass's work.
|
|
279
|
+
Absent on a first pass, which is the normal case.
|
|
250
280
|
- Classify every change ADDED / MODIFIED / REMOVED against the master, per the
|
|
251
281
|
master's OWN sections. A shared component or a shared state container is MODIFIED
|
|
252
282
|
in place, never copied into the change.
|
|
@@ -105,6 +105,7 @@ vars:
|
|
|
105
105
|
may_repair: "false" # the ask, the repair loop and the conformance stop are MASTER-only
|
|
106
106
|
gate_ok: "false" # raised by a run with no plan gap left OR by report mode, which cannot repair
|
|
107
107
|
stamp_receipt: "false" # master mode ⇒ leave the receipt implement reads, whatever the verdict
|
|
108
|
+
repairs_left: "true" # lowered on the loop's final pass, which checks and never rewrites
|
|
108
109
|
# THE GAP SPLIT, from plan-gaps after each analyze pass. Defaulted to the refusing
|
|
109
110
|
# answer, so a classification that never arrived neither ends the loop nor passes.
|
|
110
111
|
plan_ok: "false" # no gap left that a plan rewrite closes
|
|
@@ -383,13 +384,23 @@ workflow:
|
|
|
383
384
|
|
|
384
385
|
# 5. Conformance + REPAIR loop (spec is the anchor): check → split the gaps → REPORT +
|
|
385
386
|
# ASK → Repair on approval → re-check; "Report and Skip" writes plan-analyze-report.md
|
|
386
|
-
# and stops.
|
|
387
|
-
#
|
|
388
|
-
#
|
|
387
|
+
# and stops. The loop runs while a PLAN gap is open: once only routed gaps remain
|
|
388
|
+
# there is nothing a repair can change. The repair is PER LANE, so a frontend drift
|
|
389
|
+
# never rewrites a conformant backend plan.
|
|
390
|
+
#
|
|
391
|
+
# THREE REPAIRS, FOUR PASSES — the last pass checks and never rewrites. With the two
|
|
392
|
+
# equal, the third repair was the last thing to run: no pass ever saw its result, so a
|
|
393
|
+
# run that converged on it still reported `gaps` and told the operator it had not
|
|
394
|
+
# converged. Worse, plan_ok / gate_verdict / plan_gap_count then described the plans as
|
|
395
|
+
# they were BEFORE that repair, while plan-gate-stamp below hashed the files as they
|
|
396
|
+
# are AFTER it — a receipt pairing post-repair bytes with a pre-repair verdict, which
|
|
397
|
+
# plan-gate-check would later read as fresh. The extra pass costs one analyze in the
|
|
398
|
+
# case that used to end ambiguously, and it makes the last thing to touch the plans
|
|
399
|
+
# always earlier than the last thing to judge them, on every exit path.
|
|
389
400
|
- id: analyze_loop
|
|
390
401
|
action: loop
|
|
391
402
|
while: "{{plan_ok}} != true"
|
|
392
|
-
max_iterations:
|
|
403
|
+
max_iterations: 4
|
|
393
404
|
steps:
|
|
394
405
|
- id: analyze
|
|
395
406
|
action: agent_query
|
|
@@ -546,7 +557,19 @@ workflow:
|
|
|
546
557
|
action: break
|
|
547
558
|
condition: "{{plan_ok}} == true"
|
|
548
559
|
|
|
560
|
+
# THE LAST PASS CONFIRMS THE LAST REPAIR AND DOES NOT ADD ONE. Raised for every pass
|
|
561
|
+
# and lowered on the final one, so the ask below and both lane rewrites are skipped
|
|
562
|
+
# there while the analyze above still runs and still sets the verdict.
|
|
563
|
+
- id: repairs_left_yes
|
|
564
|
+
action: set
|
|
565
|
+
vars: { repairs_left: "true" }
|
|
566
|
+
- id: repairs_left_no
|
|
567
|
+
condition: "{{iteration}} == 3"
|
|
568
|
+
action: set
|
|
569
|
+
vars: { repairs_left: "false" }
|
|
570
|
+
|
|
549
571
|
- id: ask_repair
|
|
572
|
+
condition: "{{repairs_left}} == true"
|
|
550
573
|
action: ask
|
|
551
574
|
prompt: |
|
|
552
575
|
analyze-plan found spec↔plan gaps (spec.md is the anchor; domain-model.md is
|
|
@@ -582,9 +605,11 @@ workflow:
|
|
|
582
605
|
condition: "{{repair_choice}} == Report and Skip"
|
|
583
606
|
|
|
584
607
|
# WHICH DOCUMENT GETS REWRITTEN. A repair runs only where the analyze pass reported
|
|
585
|
-
# drift in that lane AND that lane is present
|
|
586
|
-
#
|
|
587
|
-
#
|
|
608
|
+
# drift in that lane AND that lane is present AND this pass still carries a repair —
|
|
609
|
+
# three ordered assignments per lane, because the dialect takes no compound
|
|
610
|
+
# condition. Cleared first so a lane that was repaired on the previous iteration and
|
|
611
|
+
# is clean on this one is not rewritten again, and cleared last on the final pass,
|
|
612
|
+
# which exists to check the previous repair rather than to add another.
|
|
588
613
|
- id: be_repair_clear
|
|
589
614
|
action: set
|
|
590
615
|
vars: { be_repair: "false" }
|
|
@@ -592,6 +617,10 @@ workflow:
|
|
|
592
617
|
condition: "{{backend_repair}} == true"
|
|
593
618
|
action: set
|
|
594
619
|
vars: { be_repair: "{{be_present}}" }
|
|
620
|
+
- id: be_repair_last_pass
|
|
621
|
+
condition: "{{repairs_left}} == false"
|
|
622
|
+
action: set
|
|
623
|
+
vars: { be_repair: "false" }
|
|
595
624
|
- id: fe_repair_clear
|
|
596
625
|
action: set
|
|
597
626
|
vars: { fe_repair: "false" }
|
|
@@ -599,6 +628,10 @@ workflow:
|
|
|
599
628
|
condition: "{{frontend_repair}} == true"
|
|
600
629
|
action: set
|
|
601
630
|
vars: { fe_repair: "{{fe_present}}" }
|
|
631
|
+
- id: fe_repair_last_pass
|
|
632
|
+
condition: "{{repairs_left}} == false"
|
|
633
|
+
action: set
|
|
634
|
+
vars: { fe_repair: "false" }
|
|
602
635
|
|
|
603
636
|
- id: repair_backend
|
|
604
637
|
condition: "{{be_repair}} == true"
|
|
@@ -718,7 +751,7 @@ workflow:
|
|
|
718
751
|
action: verify_context
|
|
719
752
|
require_true: gate_ok
|
|
720
753
|
on_false: stop
|
|
721
|
-
on_false_message: "The lane plans do not conform to the spec: {{plan_gap_count}} gap(s) a plan rewrite closes are still open (all gaps: {{analyze_gaps}})
|
|
754
|
+
on_false_message: "The lane plans do not conform to the spec: {{plan_gap_count}} gap(s) a plan rewrite closes are still open (all gaps: {{analyze_gaps}}). Either you chose `Report and Skip`, in which case plan-analyze-report.md beside the plans lists them — or repair ran up to three times and the fourth pass, which checks and does not rewrite, still found these. Every gap above was read off the plans as they stand on disk now. The receipt records verdict `gaps`, so /appweaver implement will refuse until this gate is re-run clean. A `contract` gap is closed in the lane its row names. Gaps routed to `specify` ({{routed_csv}}) are not what stopped this run: they need /appweaver specify, which authors the spec and the model. Fix out of band, or re-run and choose Repair."
|
|
722
755
|
|
|
723
756
|
# (No change-close here by design — this stage never archives anything in either mode,
|
|
724
757
|
# and cr-archive belongs to close-delta, which runs after finalize-delta's merge.)
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
# =============================================================================
|
|
71
71
|
|
|
72
72
|
name: aw-spec-analyze-slice-delta
|
|
73
|
-
version: 1.
|
|
73
|
+
version: 1.2.0
|
|
74
74
|
description: "The delta-spec↔delta-code gate — verify that the open change DOES WHAT IT WAS ASKED TO DO, before it reaches the master documents. delta-spec.md is the anchor, exactly as it is for analyze-plan-delta. Asks which open change it is gating, runs the slice's tests (the first execution on the change path, since build only compiles), then evaluates the code and its paired tests against the change's REQUIREMENTS — every ADDED or MODIFIED delta-spec item realised in code, every one covered by a test that cites its id, every invariant the model delta adds actually enforced — with the delta plan in context as the map from requirement to code and cross-checked in its own right, so an element the plan promised and nobody built is caught too. The dynamic twin of analyze-plan-delta and the delta twin of analyze-slice, which cannot see a missing implementation because a behaviour that was never built leaves no failing test. Verify-only — it reports each finding with the command that closes it and stops; it generates no code and repairs nothing."
|
|
75
75
|
agent_agnostic: true
|
|
76
76
|
dialect: aw-spec.orchestrator/v1
|
|
@@ -130,6 +130,18 @@ vars:
|
|
|
130
130
|
test_matched: "false"
|
|
131
131
|
# Verdict.
|
|
132
132
|
requirements_ok: "false"
|
|
133
|
+
# Findings split by the command that closes them (gap-route). Defaults read as
|
|
134
|
+
# "nothing found", so a run where the router could not execute still prints.
|
|
135
|
+
gap_summary: "no findings"
|
|
136
|
+
gap_total: "0"
|
|
137
|
+
fix_code: "none"
|
|
138
|
+
fix_code_n: "0"
|
|
139
|
+
fix_plan: "none"
|
|
140
|
+
fix_plan_n: "0"
|
|
141
|
+
fix_spec: "none"
|
|
142
|
+
fix_spec_n: "0"
|
|
143
|
+
fix_review: "none"
|
|
144
|
+
fix_review_n: "0"
|
|
133
145
|
gate_ok: "false"
|
|
134
146
|
|
|
135
147
|
workflow:
|
|
@@ -547,6 +559,30 @@ workflow:
|
|
|
547
559
|
action: set
|
|
548
560
|
vars: { requirements_ok: "false" }
|
|
549
561
|
|
|
562
|
+
# WHO CAN CLOSE WHAT. Both lanes' findings carry a `fix` tag already; this turns the tag
|
|
563
|
+
# into routing. It matters most for a `plan` finding: the delta plan is what is wrong, and
|
|
564
|
+
# `implement` generates FROM that plan, so sending it there reproduces the defect — which
|
|
565
|
+
# is what left the mock run's ChiefComplaintField gap closable only by hand-editing a
|
|
566
|
+
# generated plan. Deterministic, and it never drops a finding: one with no recognised
|
|
567
|
+
# `fix` is routed to `review` and counted, rather than folded into the largest bucket.
|
|
568
|
+
- id: route_gaps
|
|
569
|
+
action: execute_script
|
|
570
|
+
on_error: continue
|
|
571
|
+
script: { ref: actions }
|
|
572
|
+
subcommand: gap-route
|
|
573
|
+
args: ["--cr", "{{cr_id}}", "--gaps", "{{be_gaps}}", "--gaps", "{{fe_gaps}}"]
|
|
574
|
+
outputs:
|
|
575
|
+
- { name: gap_summary, from: "$.summary" }
|
|
576
|
+
- { name: gap_total, from: "$.total" }
|
|
577
|
+
- { name: fix_code, from: "$.implement_text" }
|
|
578
|
+
- { name: fix_code_n, from: "$.implement_count" }
|
|
579
|
+
- { name: fix_plan, from: "$.plan_text" }
|
|
580
|
+
- { name: fix_plan_n, from: "$.plan_count" }
|
|
581
|
+
- { name: fix_spec, from: "$.specify_text" }
|
|
582
|
+
- { name: fix_spec_n, from: "$.specify_count" }
|
|
583
|
+
- { name: fix_review, from: "$.review_text" }
|
|
584
|
+
- { name: fix_review_n, from: "$.review_count" }
|
|
585
|
+
|
|
550
586
|
# gate_ok = the tests ran green AND every gated lane's requirements are met.
|
|
551
587
|
- id: gate_flag
|
|
552
588
|
condition: "{{test_ok}} == true"
|
|
@@ -616,7 +652,31 @@ workflow:
|
|
|
616
652
|
action: verify_context
|
|
617
653
|
require_true: gate_ok
|
|
618
654
|
on_false: stop
|
|
619
|
-
on_false_message:
|
|
655
|
+
on_false_message: >
|
|
656
|
+
Change {{cr_id}} does NOT yet meet its delta spec. Tests green={{test_ok}} (tool
|
|
657
|
+
`{{test_tool}}`); service lane gated={{be_gated}} ok={{be_ok}}; experience lane
|
|
658
|
+
gated={{fe_gated}} ok={{fe_ok}}. {{gap_summary}}
|
|
659
|
+
|
|
660
|
+
EACH FINDING IS CLOSED BY A DIFFERENT COMMAND — they are not interchangeable, and a
|
|
661
|
+
plan defect sent to implement comes back unchanged, because implement generates FROM
|
|
662
|
+
the plan.
|
|
663
|
+
|
|
664
|
+
SPEC / MODEL ({{fix_spec_n}}): {{fix_spec}}
|
|
665
|
+
|
|
666
|
+
DELTA PLAN ({{fix_plan_n}}): {{fix_plan}}
|
|
667
|
+
|
|
668
|
+
CODE ({{fix_code_n}}): {{fix_code}}
|
|
669
|
+
|
|
670
|
+
UNROUTED ({{fix_review_n}}): {{fix_review}}
|
|
671
|
+
|
|
672
|
+
Work them in that order — the spec before the plan that realises it, the plan before
|
|
673
|
+
the code that maps to it. Re-running /appweaver plan {{cr_id}} re-derives the delta
|
|
674
|
+
plans with this report in context, so a plan finding is fixed by re-deriving rather
|
|
675
|
+
than by hand-editing a generated document. The full report is at
|
|
676
|
+
{{cr_dir}}/delta-slice-analyze-report.md. DO NOT run finalize-delta yet: merging now
|
|
677
|
+
would write requirements into spec.md and domain-model.md that the code does not
|
|
678
|
+
satisfy, and the masters would describe an intention rather than the system. Then
|
|
679
|
+
re-run build and re-run this gate.
|
|
620
680
|
|
|
621
681
|
# 6. Post-execution hooks.
|
|
622
682
|
- id: resolve_post_hooks
|
|
@@ -136,9 +136,11 @@ vars:
|
|
|
136
136
|
# Integrity pass (referrer blast radius) defaults — safe values if xref/test-run is
|
|
137
137
|
# skipped or errors, so the gate reads a real verdict rather than an empty var.
|
|
138
138
|
integrity_ok: "false"
|
|
139
|
-
referrer_csv: ""
|
|
139
|
+
referrer_csv: "" # referrers THAT HAVE CODE — what the fence can actually run
|
|
140
140
|
referrer_count: "0"
|
|
141
141
|
referrer_names: ""
|
|
142
|
+
unbuilt_referrers: "" # referrers that are planned and not implemented: nothing to run
|
|
143
|
+
unbuilt_count: "0"
|
|
142
144
|
# Pass-3 (OKF) reporting defaults — the knowledge pass is advisory, so these stay
|
|
143
145
|
# readable in the outputs even when a step is skipped or errors out.
|
|
144
146
|
okf_ok: "unknown"
|
|
@@ -617,6 +619,12 @@ workflow:
|
|
|
617
619
|
# broken referrer STOPS the gate for a human to resolve, it is not auto-fixed here.
|
|
618
620
|
# Empty referrer set (a leaf feature, or the only aggregate in the repo) → the fence is just
|
|
619
621
|
# the feature's own tests again → passes trivially, blocks nothing.
|
|
622
|
+
#
|
|
623
|
+
# THE FENCE TAKES THE BUILT HALF OF THE CLOSURE. Edges are read off the lane PLAN, so a
|
|
624
|
+
# referrer can be fully planned and hold no code: running its tests means running none,
|
|
625
|
+
# and this fence would report that emptiness as a result. An unbuilt referrer is still
|
|
626
|
+
# counted and named — it refers to this aggregate and has never been exercised against
|
|
627
|
+
# it, which is a different statement from passing.
|
|
620
628
|
- id: xref_referrers
|
|
621
629
|
action: execute_script
|
|
622
630
|
on_error: continue
|
|
@@ -624,9 +632,11 @@ workflow:
|
|
|
624
632
|
subcommand: list
|
|
625
633
|
args: ["--feature-dir", "{{feature_dir}}"]
|
|
626
634
|
outputs:
|
|
627
|
-
- { name: referrer_csv,
|
|
628
|
-
- { name: referrer_count,
|
|
629
|
-
- { name: referrer_names,
|
|
635
|
+
- { name: referrer_csv, from: "$.built_closure_csv" }
|
|
636
|
+
- { name: referrer_count, from: "$.built_count" }
|
|
637
|
+
- { name: referrer_names, from: "$.built_closure" }
|
|
638
|
+
- { name: unbuilt_referrers, from: "$.unbuilt_closure_csv" }
|
|
639
|
+
- { name: unbuilt_count, from: "$.unbuilt_count" }
|
|
630
640
|
|
|
631
641
|
# Skipped when PASS 1 found nothing to run: this is the same test command over a wider
|
|
632
642
|
# scope, so with no tests it would report the same emptiness a second time and fail a
|
|
@@ -675,7 +685,10 @@ workflow:
|
|
|
675
685
|
REFER to this feature whose tests went red when this feature's change was run
|
|
676
686
|
against them. This is a triage record for a human — analyze-slice does NOT fix a
|
|
677
687
|
referrer (that is the referrer's own slice's job). Referrer blast radius
|
|
678
|
-
({{referrer_count}}): {{referrer_names}}.
|
|
688
|
+
({{referrer_count}}): {{referrer_names}}. Also referring to this aggregate but NOT
|
|
689
|
+
YET BUILT ({{unbuilt_count}}): {{unbuilt_referrers}} — planned, holding no code, so
|
|
690
|
+
the fence ran nothing for them; say plainly that they were not exercised rather than
|
|
691
|
+
leaving them out of the report. Failing test log:
|
|
679
692
|
{{integrity_log}}
|
|
680
693
|
Write markdown, in this order:
|
|
681
694
|
- `# Broken Referrers — <feature>` and a one-line statement of what this file is:
|