aw-spec-cli 0.1.17 → 0.1.19

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 (53) hide show
  1. package/assets/.appweaver/DESIGN-FORMAT.md +45 -21
  2. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +62 -14
  3. package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +6 -4
  4. package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +9 -1
  5. package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +21 -0
  6. package/assets/.appweaver/orchestrator/orchestrator.py +23 -1
  7. package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +18 -11
  8. package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +5 -3
  9. package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +298 -109
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +161 -34
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +125 -24
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +18 -7
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +182 -39
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +300 -35
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +190 -31
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +93 -8
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +34 -7
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +319 -0
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +125 -24
  20. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +846 -35
  21. package/assets/.appweaver/scripts/bash/common.sh +28 -1
  22. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +843 -46
  23. package/assets/.appweaver/scripts/python/aw-inventory.py +3 -2
  24. package/assets/.appweaver/templates/design-template.md +41 -30
  25. package/assets/.appweaver/templates/spec-template.md +7 -4
  26. package/assets/.appweaver/templates/ui-design-template.md +7 -3
  27. package/assets/guardrails/domain/healthcare/auditing.domain.md +3 -3
  28. package/assets/guardrails/domain/pharma/auditing.domain.md +3 -3
  29. package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +1 -1
  30. package/assets/guardrails/technology/dotnet-webapi/auditing.md +1 -1
  31. package/assets/guardrails/technology/dotnet-webapi/authorization.md +1 -1
  32. package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +1 -1
  33. package/assets/guardrails/technology/dotnet-webapi/cqrs.md +1 -1
  34. package/assets/guardrails/technology/dotnet-webapi/data-classification.md +1 -1
  35. package/assets/guardrails/technology/dotnet-webapi/eventing.md +1 -1
  36. package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
  37. package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +1 -1
  38. package/assets/guardrails/technology/dotnet-webapi/persistence.md +1 -1
  39. package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +1 -1
  40. package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +1 -1
  41. package/assets/guardrails/technology/nextjs/accessibility-implementation.md +1 -1
  42. package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +1 -1
  43. package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +1 -1
  44. package/assets/guardrails/technology/nextjs/auth-session.md +1 -1
  45. package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
  46. package/assets/guardrails/technology/nextjs/design-system.md +1 -1
  47. package/assets/guardrails/technology/nextjs/error-handling.md +1 -1
  48. package/assets/guardrails/technology/nextjs/forms-and-validation.md +1 -1
  49. package/assets/guardrails/technology/nextjs/i18n.md +1 -1
  50. package/assets/guardrails/technology/nextjs/performance-budgets.md +1 -1
  51. package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
  52. package/assets/guardrails/technology/nextjs/state-management.md +1 -1
  53. package/package.json +1 -1
@@ -25,13 +25,30 @@
25
25
  # rule set, without the template selection and without the feature's real source files,
26
26
  # which is exactly the work `plan` does properly.
27
27
  #
28
- # BOOTSTRAP-ONLY, and it NEVER opens, merges or closes a change. It gates a feature whose
29
- # spec.md + lane plans ARE the masters. With a change OPEN the delta docs are the
30
- # authoritative in-flight edit and the master pair must not be rewritten under them, so
31
- # the run STOPS and routes to /appweaver analyze-plan-delta (the delta's own gate). The
32
- # change is closed by close-delta, the SINGLE cr-archive caller, and only after
28
+ # TWO MODES, AND THE WRITE IS WHAT SEPARATES THEM. This stage NEVER opens, merges or
29
+ # closes a change; what it can do is REWRITE the master lane plans through its repair loop,
30
+ # and that is the act a change in flight forbids — the delta docs are the authoritative
31
+ # edit and the masters must not drift out from under them before finalize-delta merges.
32
+ # MASTER mode (no change open) — the full gate: check, ask, repair, and STOP on gaps
33
+ # that could not be reconciled. spec.md + the lane plans are the masters.
34
+ # REPORT mode (a change is open) — the same checks, read-only: no ask, no repair, no
35
+ # stop. It writes plan-analyze-report.md and routes on. The plans are
36
+ # DERIVED from spec.md + domain-model.md, so after finalize-delta merged a
37
+ # change the drift it reports is the EXPECTED consequence of that merge and
38
+ # the remedy is /appweaver plan main, not a repair here.
39
+ # Report mode exists because the finalize→close window is exactly when the plans are known
40
+ # to be stale, and a stage that refuses to look is not a gate. It reports; it never writes
41
+ # a plan.
42
+ #
43
+ # WHICH MODE IS READ FROM THE DIRECTORY, AND DEFAULTS TO REPORT. changes/<id>/ is the
44
+ # evidence (cr-list), not active_version.json's `active_change` — the ledger is the most
45
+ # recently OPENED change and a crashed run can leave the two disagreeing. The permission to
46
+ # write is RAISED only by cr-list answering that nothing is open, so a reading that never
47
+ # arrives leaves this stage read-only rather than rewriting masters mid-delta.
48
+ #
49
+ # The change is closed by close-delta, the SINGLE cr-archive caller, and only after
33
50
  # finalize-delta has merged the delta into the masters. Also runnable on demand to
34
- # re-anchor a hand-edited spec — again, only when no change is open.
51
+ # re-anchor a hand-edited spec — in master mode, which means once every change is closed.
35
52
  #
36
53
  # RENAMED FROM `analyze-design`, following design → plan. It also absorbs the ⌛
37
54
  # `analyze-design-ui` phase that was never built as its own command: the frontend lane is
@@ -42,8 +59,8 @@
42
59
  # =============================================================================
43
60
 
44
61
  name: aw-spec-analyze-plan
45
- description: The spec↔plan conformance gate for every declared lane — confirm backend-plan.md and frontend-plan.md follow spec.md, that they bind to each other's contract, and that neither contradicts the bounded context's domain model (domain overlays are a backstop); with approval, repair the drifting plan. Lanes come from .appweaver/stacks.json, so one command gates backend-only, frontend-only and full-stack projects. Bootstrap-only — with a change open it stops and routes to analyze-plan-delta; it never closes a change.
46
- version: 3.0.0
62
+ description: The spec↔plan conformance gate for every declared lane — confirm backend-plan.md and frontend-plan.md follow spec.md, that they bind to each other's contract, and that neither contradicts the bounded context's domain model (domain overlays are a backstop); with approval, repair the drifting plan. Lanes come from .appweaver/stacks.json, so one command gates backend-only, frontend-only and full-stack projects. Runs in one of two modes, decided from the changes/ directory rather than the ledger and defaulting to the read-only one — MASTER (no change open) checks, repairs on approval and stops on unreconciled gaps; REPORT (a change open) runs the same checks read-only and writes plan-analyze-report.md without asking, repairing or stopping, because the master plans may not be rewritten while a delta is in flight. It never opens, merges or closes a change.
63
+ version: 3.2.0
47
64
  agent_agnostic: true
48
65
  dialect: aw-spec.orchestrator/v1
49
66
 
@@ -58,9 +75,23 @@ standards:
58
75
  vars:
59
76
  analyze_ok: "false"
60
77
  active_change: ""
61
- has_change: "false"
62
78
  version: ""
63
- no_open_change: "true" # NOT has_change ⇒ the masters are authoritative ⇒ this gate applies
79
+ # WHICH MODE THIS RUN IS IN, read from the changes/ DIRECTORY and defaulted to the
80
+ # read-only answer. has_changes starts at the BLOCKING value because an unanswered
81
+ # question is not a clean answer: cr-list runs without on_error: continue, so a failure
82
+ # stops the run outright, and a field that never arrived leaves this stage read-only
83
+ # instead of letting the repair loop rewrite master plans while a delta is in flight.
84
+ has_changes: "true"
85
+ open_ids: "" # cr-list's ids_csv — what is open, named, for the operator
86
+ open_count: "0"
87
+ no_open_change: "false" # RAISED only by cr-list answering that nothing is open
88
+ gate_mode: "report" # report | master — reported in the outputs
89
+ may_repair: "false" # the ask, the repair loop and the conformance stop are MASTER-only
90
+ gate_ok: "false" # raised by a conformant run OR by report mode, which cannot repair
91
+ stamp_receipt: "false" # master mode AND conformant ⇒ leave the receipt implement reads
92
+ receipt_file: ""
93
+ receipt_gated: ""
94
+ receipt_count: "0"
64
95
  lanes_declared: "false"
65
96
  be_declared: "false"
66
97
  fe_declared: "false"
@@ -156,33 +187,46 @@ workflow:
156
187
  on_false: stop
157
188
  on_false_message: "No spec.md for the current feature. Run /appweaver specify (and /appweaver plan) first."
158
189
 
159
- # 1b. Read the open change (opened by specify, or by plan on a plan-only change).
160
- # has_change drives the BOOTSTRAP-ONLY guard below — it closes nothing.
161
- - id: read_change
190
+ # 1b. IS ANY CHANGE OPEN — asked of the changes/ DIRECTORY, which is what "open" means.
191
+ # Not version-read's `has_change`: that is bool(active_change), the most recently
192
+ # OPENED change in active_version.json, and a run that died between cr-setup writing
193
+ # the directory and the ledger recording it leaves the two disagreeing. The stage
194
+ # that may rewrite masters has to read the authority every other delta stage reads.
195
+ # No on_error: continue — a cr-list that cannot answer stops the run.
196
+ - id: cr_list
162
197
  action: execute_script
163
198
  script: { ref: actions }
164
- subcommand: version-read
199
+ subcommand: cr-list
165
200
  args: ["--feature-dir", "{{feature_dir}}"]
166
201
  outputs:
202
+ - { name: has_changes, from: "$.has_changes" }
203
+ - { name: open_ids, from: "$.ids_csv" }
204
+ - { name: open_count, from: "$.count" }
167
205
  - { name: active_change, from: "$.active_change" }
168
- - { name: has_change, from: "$.has_change" }
169
- - { name: version, from: "$.version" }
170
-
171
- # 1c. BOOTSTRAP-ONLY GUARD. A change open ⇒ the delta spec + delta plans are the
172
- # authoritative in-flight edit and this stage is the WRONG gate: its repair loop
173
- # rewrites the MASTER plans, which would drift the masters out from under the delta
174
- # before finalize-delta merges it. Stop BEFORE any authoring so the wrong-stage run
175
- # has zero side effects, and route to the delta's own gate.
176
- - id: flag_change_open
177
- condition: "{{has_change}} == true"
206
+
207
+ - id: read_version
208
+ action: execute_script
209
+ script: { ref: actions }
210
+ subcommand: version-read
211
+ args: ["--feature-dir", "{{feature_dir}}"]
212
+ outputs:
213
+ - { name: version, from: "$.version" }
214
+
215
+ # 1c. THE WRITE PERMISSION, raised then verified. Nothing open ⇒ spec.md + the lane plans
216
+ # ARE the masters ⇒ this stage owns them and may repair. Anything else — a change
217
+ # open, or an answer that never arrived — leaves may_repair down and the run is
218
+ # read-only: the checks still run and still report, but no plan is rewritten under a
219
+ # delta in flight. The permission is raised by the good state, never knocked down by
220
+ # the bad one, so the failure direction is refusal to write.
221
+ - id: no_open_change_raise
222
+ condition: "{{has_changes}} == false"
178
223
  action: set
179
- vars: { no_open_change: "false" }
224
+ vars: { no_open_change: "true" }
180
225
 
181
- - id: require_no_change
182
- action: verify_context
183
- require_true: no_open_change
184
- on_false: stop
185
- on_false_message: "Change {{active_change}} is OPEN on this feature — analyze-plan is the BOOTSTRAP gate and must not rewrite the master plans while a delta is in flight. Gate the delta instead: /appweaver analyze-plan-delta → implement → build → analyze-slice-delta → finalize-delta (merges the delta into spec.md and domain-model.md — not the lane plans, which are derived from those two) → analyze-slice → close-delta (archives the change + bumps the version). Re-run analyze-plan once every change is closed, to re-anchor the masters."
226
+ - id: enter_master_mode
227
+ condition: "{{no_open_change}} == true"
228
+ action: set
229
+ vars: { gate_mode: "master", may_repair: "true" }
186
230
 
187
231
  # 2. WHICH LANES ARE GATED. Present = declared AND on disk; missing = declared AND not.
188
232
  # Taking the lane's own `declared` value under the exists condition is how the AND is
@@ -378,6 +422,45 @@ workflow:
378
422
  - id: conform_break
379
423
  action: break
380
424
  condition: "{{analyze_ok}} == true"
425
+
426
+ # REPORT MODE — a change is open (or the mode could not be established), so the
427
+ # master plans are off limits and there is nothing to ask: repair is not on offer.
428
+ # Write the report and leave the loop. The report itself has to name the two
429
+ # readings, because the same gaps mean opposite things either side of the merge.
430
+ - id: write_report_readonly
431
+ condition: "{{may_repair}} == false"
432
+ action: agent_author
433
+ include_standards: false
434
+ output_target: { type: file, path: "{{feature_dir}}/plan-analyze-report.md" }
435
+ task: |
436
+ Write a Plan Analyze Report to the output from these spec↔plan conformance gaps
437
+ (spec.md is the ANCHOR). This run was READ-ONLY: change(s) {{open_ids}} are open
438
+ on this feature, so the master lane plans were checked but NOT repaired.
439
+ {{analyze_gaps}}
440
+ Markdown format:
441
+ - "# Plan Analyze Report — <aggregate>" plus today's date.
442
+ - A verdict line: "verdict: GAPS ✗ · <n> gap(s) · read-only (change open: {{open_ids}})".
443
+ - "## Gaps" as a table: | # | check | lane | severity | spec ref | plan location | gap |.
444
+ - "## How to read this" — state BOTH readings and that only the operator knows
445
+ which applies, because it depends on whether finalize-delta has already run:
446
+ (a) AFTER finalize-delta merged a change, these gaps are EXPECTED. The lane
447
+ plans are DERIVED from spec.md + domain-model.md and finalize-delta merges the
448
+ change into those two documents only, so the plans stand at their last authored
449
+ content and the spec has moved ahead of them. The remedy is
450
+ /appweaver plan main (which re-derives them, carrying DA-n / WA-n forward),
451
+ after /appweaver close-delta, not a repair of either plan.
452
+ (b) BEFORE finalize-delta, the masters have not moved, so these gaps are real
453
+ master drift — but this is still not the stage that fixes it, because the
454
+ change in flight is planned in its OWN delta plans. Gate those with
455
+ /appweaver analyze-plan-delta, and re-run /appweaver analyze-plan in master
456
+ mode once every change is closed.
457
+ - "## Not repaired here": this report is the whole output of a read-only run. No
458
+ plan document was written.
459
+ Return the complete report markdown only.
460
+ - id: readonly_break
461
+ action: break
462
+ condition: "{{may_repair}} == false"
463
+
381
464
  - id: ask_repair
382
465
  action: ask
383
466
  prompt: |
@@ -501,13 +584,53 @@ workflow:
501
584
  already-conformant section VERBATIM.
502
585
  Return the COMPLETE updated frontend-plan.md content only.
503
586
 
587
+ # THE STOP IS MASTER MODE'S ALONE. Two independent raises, because the two reasons to
588
+ # pass are unrelated: the plans conform, or this run was never allowed to change them.
589
+ # A read-only run that found gaps has done its whole job by reporting them — stopping
590
+ # would fail the operator for drift that finalize-delta created on purpose and that only
591
+ # /appweaver plan main can close.
592
+ - id: gate_ok_conformant
593
+ condition: "{{analyze_ok}} == true"
594
+ action: set
595
+ vars: { gate_ok: "true" }
596
+ - id: gate_ok_readonly
597
+ condition: "{{may_repair}} == false"
598
+ action: set
599
+ vars: { gate_ok: "true" }
600
+
504
601
  - id: analyze_gate
505
602
  action: verify_context
506
- require_true: analyze_ok
603
+ require_true: gate_ok
507
604
  on_false: stop
508
605
  on_false_message: "The lane plans do not conform to the spec (gaps: {{analyze_gaps}}) — repair was skipped (see plan-analyze-report.md), or it could not be reconciled in 3 passes. A 'LIFT-FAILURE:' gap means a domain constraint from the gate checklist was never lifted into the spec, and a `model` gap means the plan needs an entity or invariant domain-model.md does not define — both are fixed by /appweaver specify, which authors the spec and the model; this gate may not write either. A `contract` gap is closed in the lane its row names. Otherwise fix out of band, or re-run and choose Repair."
509
606
 
510
- # (No change-close here by design — this stage only ever runs with NO change open,
607
+ # THE RECEIPT. implement generates from the plans and deliberately does not read the
608
+ # spec, on the strength of this stage having conformed them to it — which until now was
609
+ # an assumption about the order the operator ran things in, checked by nothing. Record
610
+ # WHAT was proven: the bytes of spec.md, the context's domain-model.md and each lane plan
611
+ # that was present. plan-gate-check reads it at implement.
612
+ #
613
+ # ONLY A GREEN MASTER RUN LEAVES ONE. The AND is the kit's ordered assignment — under
614
+ # master mode, take the conformance verdict as the answer — because the engine has no
615
+ # compound conditions. A REPORT-mode run proves nothing about the masters (it may not
616
+ # even repair them), and a red master run proved the opposite.
617
+ - id: flag_receipt
618
+ condition: "{{may_repair}} == true"
619
+ action: set
620
+ vars: { stamp_receipt: "{{analyze_ok}}" }
621
+
622
+ - id: plan_gate_stamp
623
+ condition: "{{stamp_receipt}} == true"
624
+ action: execute_script
625
+ script: { ref: actions }
626
+ subcommand: plan-gate-stamp
627
+ args: ["--feature-dir", "{{feature_dir}}", "--spec-version", "{{version}}", "--mode", "{{gate_mode}}"]
628
+ outputs:
629
+ - { name: receipt_file, from: "$.file" }
630
+ - { name: receipt_gated, from: "$.gated" }
631
+ - { name: receipt_count, from: "$.count" }
632
+
633
+ # (No change-close here by design — this stage never archives anything in either mode,
511
634
  # and cr-archive belongs to close-delta, which runs after finalize-delta's merge.)
512
635
 
513
636
  # 6. Post-execution hooks.
@@ -531,5 +654,9 @@ outputs:
531
654
  conformant: "{{analyze_ok}}"
532
655
  gaps: "{{analyze_gaps}}"
533
656
  version: "{{version}}"
534
- note: "Checked: spec→backend-plan ({{be_present}}), spec→frontend-plan ({{fe_present}}), the plan↔plan contract binding ({{both_lanes}} — it needs both lanes) and both plans against domain-model.md. The domain model was READ, never written: a gap that needs a new entity, event or invariant is fixed by /appweaver specify."
535
- next: "/appweaver implement (the plans conform to the spec; version unchanged — this gate closes nothing)"
657
+ gate_mode: "{{gate_mode}}" # master (repair allowed) | report (read-only)
658
+ open_changes: "{{open_ids}}" # empty in master mode, by definition
659
+ gate_receipt: "{{receipt_file}}" # written only by a GREEN master run; implement reads it
660
+ gate_receipt_at: "{{receipt_gated}}"
661
+ note: "Ran in {{gate_mode}} mode ({{open_count}} change(s) open: {{open_ids}}). Checked: spec→backend-plan ({{be_present}}), spec→frontend-plan ({{fe_present}}), the plan↔plan contract binding ({{both_lanes}} — it needs both lanes) and both plans against domain-model.md. The domain model was READ, never written: a gap that needs a new entity, event or invariant is fixed by /appweaver specify. In REPORT mode no plan was rewritten either — the master plans are derived documents and may not move while a delta is in flight."
662
+ next: "MASTER mode (no change open, conformant={{analyze_ok}}): /appweaver implement — the plans conform to the spec; version unchanged, this gate closes nothing. REPORT mode (change(s) {{open_ids}} open): this run was read-only and wrote plan-analyze-report.md only. If finalize-delta has already merged, the gaps ARE that merge — the lane plans are derived from spec.md + domain-model.md and finalize-delta moves only those two, so re-derive with /appweaver plan main after /appweaver close-delta, then re-run /appweaver analyze-plan in master mode. If the change has NOT merged yet, gate it where it lives: /appweaver analyze-plan-delta."
@@ -3,13 +3,17 @@
3
3
  #
4
4
  # The second of the two discrete conformance gates on the forward path, and the
5
5
  # sibling of analyze-plan:
6
- # analyze-plan gates spec ↔ plan — STATIC, before implement
7
- # analyze-slice gates plan ↔ slice — DYNAMIC, after build
6
+ # analyze-plan gates spec + model ↔ plans — STATIC, before implement
7
+ # analyze-slice gates spec + model ↔ code — DYNAMIC, after build
8
+ # Both anchor on the same two masters. The difference is what they score against them and
9
+ # how: analyze-plan reads documents and never runs anything; this gate runs the slice's
10
+ # tests and reads the code they cover.
8
11
  # "Slice" is the unit under analysis: the aggregate's code, its paired tests, and
9
12
  # its OKF knowledge concept. Three passes over it —
10
13
  # 1. BEHAVIOUR run the tests (paired to the spec's acceptance criteria by the
11
14
  # design docs); on red, fix up to 3×.
12
- # 2. CONFORMANCE check the code against the domain gate overlays (validate-only).
15
+ # 2. CONFORMANCE check the code against the domain gate overlays AND the bounded
16
+ # context's domain-model.md (validate-only).
13
17
  # 3. KNOWLEDGE refresh + review the slice's OKF concept (advisory).
14
18
  # Every fix made here is recorded in that concept, which is also where the slice's
15
19
  # file list is READ from (2.3.0 — `aw-xref concept-paths`, replacing the retired
@@ -17,7 +21,22 @@
17
21
  # longer carries; the paired `design-reconcile` write-back is retired with it, its job
18
22
  # already done by the concept-upsert --merge steps in pass 3).
19
23
  #
20
- # TWO POSITIONS, one gate — and in BOTH the master spec.md is the whole truth:
24
+ # TWO MASTERS, NOT ONE (2.4.0). The spec says what the feature must DO; the bounded
25
+ # context's domain-model.md says what the domain IS — the aggregate boundary, the entities,
26
+ # and the text of every invariant the spec cites by BR id. Since the model split, the spec
27
+ # carries the ids and not the rules, so a gate reading only the spec scores code against a
28
+ # document that no longer states what it is checking for. Both are in every pass's context
29
+ # and the conformance pass scores against both.
30
+ #
31
+ # SCOPE IS WHAT KEEPS THE MODEL FAIR HERE. domain-model.md describes the whole context
32
+ # while this gate scores ONE feature. An invariant under the slice's own `## Aggregate:`
33
+ # section is BLOCKING — unenforced or enforced differently than stated is a violation, and
34
+ # so is naming an entity or field differently from the model. An invariant under another
35
+ # aggregate's section is REPORTED and never blocking: this slice does not own it and often
36
+ # should not enforce it. A feature outside every bounded context has no model, and the pass
37
+ # degrades to overlays + spec, exactly as it behaved before.
38
+ #
39
+ # TWO POSITIONS, one gate — and in BOTH the merged masters are the whole truth:
21
40
  # BOOTSTRAP implement → build → analyze-slice → run
22
41
  # CHANGE implement (the delta) → build → analyze-slice-delta → finalize-delta
23
42
  # → analyze-slice → close-delta
@@ -42,8 +61,8 @@
42
61
  # =============================================================================
43
62
 
44
63
  name: aw-spec-analyze-slice
45
- description: The design↔code conformance gate — run the slice's tests against its acceptance criteria, check the code against the domain gate overlays, verify the change did not break any referring aggregate (the OKF referrer blast radius) and write a triage report before stopping if it did, refresh + review its OKF concept, and record every fix in it. Sits after finalize-delta on the change path, so the master spec it scores against already carries the change, and before close-delta, so a red gate leaves the change open and recoverable.
46
- version: 2.3.0
64
+ description: The design↔code conformance gate — run the slice's tests against its acceptance criteria, check the code against the domain gate overlays AND the bounded context's domain-model.md (every invariant the slice's own aggregate section states must be enforced as written; one another aggregate owns is reported, not blocking), verify the change did not break any referring aggregate (the OKF referrer blast radius) and write a triage report before stopping if it did, refresh + review its OKF concept, and record every fix in it. Sits after finalize-delta on the change path, so the two masters it scores against already carry the change, and before close-delta, so a red gate leaves the change open and recoverable.
65
+ version: 2.4.0
47
66
  agent_agnostic: true
48
67
  dialect: aw-spec.orchestrator/v1
49
68
 
@@ -69,6 +88,10 @@ standards:
69
88
  vars:
70
89
  test_ok: "false"
71
90
  conformance_ok: "false"
91
+ # Invariants the model states under ANOTHER aggregate's section that this slice appears
92
+ # to touch — reported, never blocking, since this slice does not own them. Defaulted so
93
+ # the gate message and the outputs read a real value on a run that skipped the pass.
94
+ model_notes: ""
72
95
  # NAVIGATION ONLY. Whether a change is open decides where this gate sends you next
73
96
  # (close-delta vs run) — nothing else in this workflow reads them. On the CHANGE path
74
97
  # finalize-delta has already merged the delta into the master docs, so the delta files
@@ -105,6 +128,7 @@ workflow:
105
128
  script: { ref: actions }
106
129
  subcommand: feature-paths
107
130
  outputs:
131
+ - { name: feature, from: "$.feature" }
108
132
  - { name: feature_dir, from: "$.feature_dir" }
109
133
  - { name: spec_file, from: "$.spec_file" }
110
134
 
@@ -114,6 +138,30 @@ workflow:
114
138
  on_empty: stop
115
139
  on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run analyze-slice."
116
140
 
141
+ # The bounded context's DOMAIN MODEL — in every pass's context beside the spec, and the
142
+ # authority the conformance pass scores against. Since the model split the spec no longer
143
+ # states the aggregate boundary, the entities or the text of an invariant: it cites BR-nnn
144
+ # by id and the model holds them. A gate reading only the spec therefore scores code
145
+ # against a document that no longer carries what it is checking for.
146
+ #
147
+ # On the CHANGE path this gate runs after finalize-delta, which has already merged the
148
+ # change's model half into the master — so the master is complete here and the change's
149
+ # `domain-model.<CR>.delta.md` is deliberately NOT loaded, exactly as its delta-spec is not.
150
+ #
151
+ # optional: on every context entry below, because a feature outside every bounded context
152
+ # legitimately has no model and a required file that is missing announces itself.
153
+ # on_error: continue for the same reason — a missing model degrades this gate to its
154
+ # pre-model behaviour (spec + overlays), it never blocks.
155
+ - id: context_paths
156
+ action: execute_script
157
+ on_error: continue
158
+ script: { ref: actions }
159
+ subcommand: context-paths
160
+ outputs:
161
+ - { name: context, from: "$.context" }
162
+ - { name: domain_model, from: "$.domain_model" }
163
+ - { name: domain_model_exists, from: "$.domain_model_exists" }
164
+
117
165
  # Is a change OPEN on this feature? Read for NAVIGATION only — see the vars note. Safe
118
166
  # on the bootstrap path: with no active_version.json, version-read returns
119
167
  # active_change="" / has_change=false and exits 0, so nothing here is conditional on it.
@@ -185,6 +233,7 @@ workflow:
185
233
  test_log: "{{test_log}}"
186
234
  context:
187
235
  - { path: "{{spec_file}}" }
236
+ - { path: "{{domain_model}}", optional: true }
188
237
  context_from: all_paths
189
238
  - id: fix
190
239
  action: agent_generate
@@ -193,7 +242,10 @@ workflow:
193
242
  task: |
194
243
  Tests failed. Make them pass by fixing the code (or a genuinely wrong
195
244
  test) in the files in context, keeping behaviour aligned with the spec's
196
- acceptance criteria (spec in context). Test log:
245
+ acceptance criteria (spec in context). Where the spec cites an invariant
246
+ by id (BR-nnn), the bounded context's domain-model.md in context carries
247
+ its text and the entity names it is stated over — read the rule there
248
+ rather than inferring it from the id. Test log:
197
249
  {{test_log}}
198
250
  outputs:
199
251
  - { name: fixed_files, from: "$.files" }
@@ -238,27 +290,57 @@ workflow:
238
290
  action: build_context
239
291
  context:
240
292
  - { path: "{{spec_file}}" }
293
+ - { path: "{{domain_model}}", optional: true }
241
294
  context_from: all_paths
242
295
  - id: check_overlays
243
296
  action: agent_query
244
297
  include_standards: true # the gate overlays (checklist) are in the standards
245
- schema: { ok: true, violations: [ { rule: "<overlay rule / id>", where: "<file:symbol>", issue: "<what is violated>" } ] }
246
- mock: { ok: true, violations: [] }
298
+ schema: { ok: true, violations: [ { rule: "<overlay rule / BR-nnn>", where: "<file:symbol>", issue: "<what is violated>" } ], model_notes: [ { rule: "<BR-nnn>", owner: "<the aggregate that states it>", issue: "<what is unenforced or mismatched>" } ] }
299
+ mock: { ok: true, violations: [], model_notes: [] }
247
300
  task: |
248
- The domain gate checklist is loaded in the standards (the overlay
249
- forbidden-patterns, refusal conditions, and CI gates). Check the feature's
250
- generated code + tests (in context) against EACH checklist rule and list
251
- EVERY violation, e.g.: a hard delete of a retention-protected aggregate /
252
- version history / audit record; a second write path bypassing the aggregate
253
- or interop adapter; an invented/free-text coded value where a validated code
254
- is required; a missing or weakened audit emission on an audited operation; a
255
- data-classification-restricted field exposed on the public surface or written
256
- to logs/metrics/traces. Also confirm the constraint TESTS exist (audit is
257
- emitted, a restricted field is not exposed, an interop projection round-trips).
258
- ok=true ONLY when no checklist rule is violated.
301
+ TWO authorities, checked in one pass over the feature's generated code + tests
302
+ (in context).
303
+
304
+ 1. THE DOMAIN GATE CHECKLIST, loaded in the standards (the overlay
305
+ forbidden-patterns, refusal conditions, and CI gates). Check the code against
306
+ EACH checklist rule and list EVERY violation, e.g.: a hard delete of a
307
+ retention-protected aggregate / version history / audit record; a second write
308
+ path bypassing the aggregate or interop adapter; an invented/free-text coded
309
+ value where a validated code is required; a missing or weakened audit emission
310
+ on an audited operation; a data-classification-restricted field exposed on the
311
+ public surface or written to logs/metrics/traces. Also confirm the constraint
312
+ TESTS exist (audit is emitted, a restricted field is not exposed, an interop
313
+ projection round-trips).
314
+
315
+ 2. THE BOUNDED CONTEXT'S DOMAIN MODEL, `domain-model.md` in context — the
316
+ authority on what the domain IS. The spec cites invariants by id; this document
317
+ states them. It describes the WHOLE context while this gate scores ONE feature,
318
+ so scope matters:
319
+ • BLOCKING — every BR-nnn stated under `## Aggregate: {{feature}}`, the section
320
+ this slice owns. The rule has to be enforced somewhere in this code, and the
321
+ enforcement has to say what the model says. An owned invariant with no
322
+ enforcement, or one enforced with a different threshold, condition or
323
+ direction than the model states, is a `violations` entry with the BR id as
324
+ its `rule`. The model wins: it is what finalize-delta merged and what the
325
+ plans were conformed against.
326
+ • BLOCKING — the ubiquitous language of that same section. An entity, field or
327
+ state the code names differently from the model is a violation; the model's
328
+ name is the right one.
329
+ • NOT BLOCKING — anything the model states under ANOTHER aggregate's section.
330
+ This slice does not own those and often should not enforce them. Report each
331
+ one this code appears to touch as a `model_notes` entry, naming the owning
332
+ aggregate, and carry on.
333
+ If no domain-model.md is in context, this feature is outside every bounded
334
+ context: skip part 2 entirely and report nothing for it. Never infer an
335
+ invariant's text from its id, and never treat a rule the model does not state
336
+ as one.
337
+
338
+ ok=true ONLY when no checklist rule is violated AND no invariant this aggregate
339
+ owns is unenforced or contradicted. `model_notes` never affects ok.
259
340
  outputs:
260
341
  - { name: conformance_ok, from: "$.ok" }
261
342
  - { name: conformance_violations, from: "$.violations" }
343
+ - { name: model_notes, from: "$.model_notes" }
262
344
  - id: conformance_break
263
345
  action: break
264
346
  condition: "{{conformance_ok}} == true"
@@ -268,9 +350,15 @@ workflow:
268
350
  permission_mode: acceptEdits
269
351
  task: |
270
352
  Fix these domain-conformance violations in the feature's code (in context),
271
- following the gate checklist rules (in the standards) and keeping the design's
272
- behaviour intact. Do NOT weaken or delete a test to hide a violation — fix the
273
- code. After fixing, re-run the tests (Bash) to confirm they stay green.
353
+ keeping the design's behaviour intact. A violation whose `rule` is an overlay
354
+ rule is fixed against the gate checklist in the standards; one whose `rule` is a
355
+ BR-nnn is fixed against the text domain-model.md states for it under
356
+ `## Aggregate: {{feature}}` — enforce what the model says, where the code
357
+ already owns that decision, and name entities and fields the way the model names
358
+ them. Do NOT weaken or delete a test to hide a violation, and do NOT edit
359
+ domain-model.md or the spec to match the code: this is a gate on the code, and a
360
+ model that is genuinely wrong is corrected upstream through /appweaver specify.
361
+ After fixing, re-run the tests (Bash) to confirm they stay green.
274
362
  Violations: {{conformance_violations}}
275
363
  outputs:
276
364
  - { name: conformance_fixes, from: "$.files" }
@@ -279,7 +367,7 @@ workflow:
279
367
  action: verify_context
280
368
  require_true: conformance_ok
281
369
  on_false: stop
282
- on_false_message: "Code violates the domain gate checklist after 2 fix attempts (violations: {{conformance_violations}}). These are the domain constraints plan/implement must satisfy — fix the code, or if the constraint itself is wrong, correct it upstream (/appweaver specify → plan)."
370
+ on_false_message: "Code fails domain conformance after 2 fix attempts (violations: {{conformance_violations}}). Each entry is either a gate-checklist rule or an invariant (BR-nnn) the aggregate's own section of {{domain_model}} states and this code does not enforce as written. Both are constraints plan/implement must satisfy — fix the code, or if the constraint itself is wrong, correct it upstream (/appweaver specify → plan) rather than editing the model to match what was built. Invariants owned by other aggregates in this context were reported, not enforced here: {{model_notes}}"
283
371
 
284
372
  # ═══════ INTEGRITY — did this feature's change break any aggregate that USES it? ═══════
285
373
  # The slice is green + domain-conformant on its own. But a change to a feature can break
@@ -325,6 +413,7 @@ workflow:
325
413
  integrity_log: "{{integrity_log}}"
326
414
  context:
327
415
  - { path: "{{spec_file}}" }
416
+ - { path: "{{domain_model}}", optional: true }
328
417
  context_from: all_paths
329
418
  - id: write_broken_referrers
330
419
  action: agent_author
@@ -436,6 +525,18 @@ workflow:
436
525
 
437
526
  outputs:
438
527
  feature_dir: "{{feature_dir}}"
528
+ bounded_context: "{{context}}"
529
+ # The second master this gate scored against — the RESOLVED path, which context-paths
530
+ # reports whether or not the file is there. `model_scored` is the one that says whether
531
+ # it actually reached the passes: false means the conformance pass checked the gate
532
+ # overlays alone, either because the feature is outside every bounded context or because
533
+ # its context has not been modelled yet.
534
+ domain_model: "{{domain_model}}"
535
+ model_scored: "{{domain_model_exists}}"
536
+ # Invariants another aggregate in this context owns that this slice appears to touch.
537
+ # Reported by the conformance pass and never gated — surfaced so a real cross-aggregate
538
+ # leak is visible rather than only living in one query's answer.
539
+ model_notes: "{{model_notes}}"
439
540
  test_tool: "{{test_tool}}"
440
541
  referrer_count: "{{referrer_count}}"
441
542
  referrer_blast_radius: "{{referrer_names}}"
@@ -22,12 +22,19 @@
22
22
  # interactive "continue or exit?" gate on every build to load rules that could never
23
23
  # have been found. Removing it makes greenfield `build` non-interactive.
24
24
  #
25
+ # 1.3.0 — the lane gate requires A LANE, not a BACKEND lane. Every declared lane is
26
+ # compiled by its own build command, so what this stage needs is a declaration to compile
27
+ # from, not a particular kind of one; requiring the service lane blocked a frontend-only
28
+ # project that `plan` and `implement` both accept. The rest of the gate is unchanged: no
29
+ # lane declared, a lane with no source path, a source path that is not on disk, and two
30
+ # lanes claiming overlapping subtrees all still stop the run.
31
+ #
25
32
  # Launch: /appweaver build [strategy]
26
33
  # =============================================================================
27
34
 
28
35
  name: aw-spec-build
29
- description: Compile the feature, fix compile errors against the design intent, then record the touched files in the slice's knowledge concept. Loads TECHNICAL rules only (tech-rule-bindings) — no domain rules reach this stage.
30
- version: 1.2.0
36
+ description: Compile the feature by running EVERY declared lane's own build command, fix compile errors against the design intent, then record the touched files in the slice's knowledge concept. Backend-only, frontend-only and full-stack projects all compile here — the gate requires a lane declaration, not a lane of a particular kind. Loads TECHNICAL rules only (tech-rule-bindings) — no domain rules reach this stage.
37
+ version: 1.3.0
31
38
  agent_agnostic: true
32
39
  dialect: aw-spec.orchestrator/v1
33
40
 
@@ -77,14 +84,18 @@ workflow:
77
84
  on_empty: stop
78
85
  on_empty_message: "No active feature. Run /appweaver select-feature first (pick or create the aggregate, e.g. Order), then re-run build."
79
86
 
80
- # Require the SERVICE LANE and its source path. With a declaration, build-compile
81
- # runs EVERY declared lane's own build command, so a two-lane project compiles
82
- # both — the case the old first-match-wins sniffer got wrong by reporting green
83
- # for a frontend it never touched.
87
+ # Require A LANE — not a KIND of lane. build-compile runs EVERY declared lane's own
88
+ # build command, so a two-lane project compiles both (the case the old first-match-wins
89
+ # sniffer got wrong by reporting green for a frontend it never touched) and a one-lane
90
+ # project compiles the one it has, whichever kind that is. A frontend-only project is a
91
+ # legal project: `plan` and `implement` both accept one, so refusing it here stopped it
92
+ # at the first stage that had no reason to care. Passing no required kind leaves every
93
+ # other check armed — no lane declared at all, a lane with no source path, a source path
94
+ # that is not on disk, and two lanes claiming overlapping subtrees.
84
95
  - id: lane_gate
85
96
  action: include
86
97
  module: .appweaver/orchestrator/modules/lane-gate.orch.yaml
87
- with: { require: backend, command: "/appweaver build" }
98
+ with: { require: "", command: "/appweaver build" }
88
99
 
89
100
  # The slice's source files, from its OKF concept — what the fix loop below is handed.
90
101
  # This replaces the retired `design-paths` fence scan: the tech-agnostic backend-plan.md