aw-spec-cli 0.1.20 → 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/ORCH-DIALECT.md +8 -3
- package/assets/.appweaver/STACK-PROFILES.md +87 -8
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +30 -0
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +22 -1
- package/assets/.appweaver/orchestrator/orchestrator.py +65 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +6 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +144 -52
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +78 -10
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +283 -25
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +202 -40
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +140 -36
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +557 -57
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +3 -3
- 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-select-stack.orch.yaml +47 -23
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +101 -1
- 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 +772 -216
- package/assets/.appweaver/scripts/bash/aw-xref.sh +4 -1
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +708 -209
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +4 -1
- package/assets/.appweaver/scripts/python/aw-crstate.py +319 -0
- package/assets/.appweaver/scripts/python/aw-foundation.py +151 -0
- package/assets/.appweaver/scripts/python/aw-gaps.py +119 -0
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +19 -0
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +503 -0
- 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 +257 -28
- package/assets/.appweaver/scripts/python/aw-testlog.py +148 -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 +39 -0
- package/assets/.appweaver/stack-profiles/java-spring.json +20 -0
- package/assets/.appweaver/stack-profiles/nextjs.json +26 -0
- package/assets/.appweaver/stack-profiles/node-express.json +27 -0
- package/assets/.appweaver/stack-profiles/python-fastapi.json +16 -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/constitution-domain-template.md +17 -2
- package/assets/.appweaver/templates/ui-model-template.md +67 -0
- package/assets/.appweaver/templates/workflow-model-template.md +62 -0
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +10 -6
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +4 -0
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +3 -1
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +5 -3
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +5 -1
- package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +13 -5
- package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +2 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +6 -2
- package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +114 -0
- package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +3 -0
- package/assets/guardrails/technology/dotnet-webapi/testing.md +10 -0
- package/assets/guardrails/technology/nextjs/ui-constitution.md +8 -1
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/.state/smoke.state.json +0 -32
|
@@ -11,9 +11,18 @@
|
|
|
11
11
|
# "Slice" is the unit under analysis: the aggregate's code, its paired tests, and
|
|
12
12
|
# its OKF knowledge concept. Three passes over it —
|
|
13
13
|
# 1. BEHAVIOUR run the tests (paired to the spec's acceptance criteria by the
|
|
14
|
-
# design docs); on red, fix up to 3
|
|
14
|
+
# design docs); on red, fix up to 3×, then stop — the passes below would
|
|
15
|
+
# be scoring code already known to be wrong. A slice with NOTHING to run
|
|
16
|
+
# (no test project, or a filter that selected zero tests) is a different
|
|
17
|
+
# state: no fix attempt can help, so it warns, every other pass runs, and
|
|
18
|
+
# the gate fails at the END with what they found.
|
|
15
19
|
# 2. CONFORMANCE check the code against the domain gate overlays AND the bounded
|
|
16
20
|
# context's domain-model.md (validate-only).
|
|
21
|
+
# 2b. COMPOSITION is every dependency the slice injects actually BOUND in the service
|
|
22
|
+
# lane's composition root? The one failure class the other passes cannot
|
|
23
|
+
# see: a container graph is not compile-checked, so an unregistered port
|
|
24
|
+
# builds green, passes unit tests that construct their subject directly,
|
|
25
|
+
# and fails on the first request. Fixed up to 2×.
|
|
17
26
|
# 3. KNOWLEDGE refresh + review the slice's OKF concept (advisory).
|
|
18
27
|
# Every fix made here is recorded in that concept, which is also where the slice's
|
|
19
28
|
# file list is READ from (2.3.0 — `aw-xref concept-paths`, replacing the retired
|
|
@@ -62,7 +71,7 @@
|
|
|
62
71
|
|
|
63
72
|
name: aw-spec-analyze-slice
|
|
64
73
|
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.
|
|
74
|
+
version: 2.6.0
|
|
66
75
|
agent_agnostic: true
|
|
67
76
|
dialect: aw-spec.orchestrator/v1
|
|
68
77
|
|
|
@@ -87,7 +96,33 @@ standards:
|
|
|
87
96
|
|
|
88
97
|
vars:
|
|
89
98
|
test_ok: "false"
|
|
99
|
+
# TWO WAYS PASS 1 IS NOT GREEN, and they are not the same failure. `tests_red` is a suite
|
|
100
|
+
# that RAN and failed — the fix loop's business, and a stop. `tests_missing` is a slice
|
|
101
|
+
# with nothing to run (no test project, or a slice filter that selected zero tests):
|
|
102
|
+
# nothing to repair, so the loop leaves at once, the run carries on through every other
|
|
103
|
+
# pass, and the gate fails at the end with what they found. See PASS 1.
|
|
104
|
+
tests_red: "false"
|
|
105
|
+
tests_missing: "false"
|
|
106
|
+
tests_present: "true"
|
|
107
|
+
slice_testable: "true"
|
|
108
|
+
not_green: "false"
|
|
109
|
+
test_excerpt: ""
|
|
110
|
+
test_failing_lanes: ""
|
|
111
|
+
test_no_setup: "false"
|
|
112
|
+
test_setup_reason: ""
|
|
113
|
+
zero_match: "false"
|
|
90
114
|
conformance_ok: "false"
|
|
115
|
+
# Composition pass (PASS 2b). Defaults to PASSED, because a project with no service lane
|
|
116
|
+
# has no container to check — the flag is lowered only where a backend lane is declared.
|
|
117
|
+
wiring_ok: "true"
|
|
118
|
+
unbound: ""
|
|
119
|
+
be_declared: "false"
|
|
120
|
+
be_lane_id: ""
|
|
121
|
+
be_source_path: ""
|
|
122
|
+
wiring_notes: ""
|
|
123
|
+
# The integrity fence blocks through this, not through `integrity_ok` — a fence skipped
|
|
124
|
+
# because there were no tests to run must not read as a broken referrer.
|
|
125
|
+
integrity_gate_ok: "true"
|
|
91
126
|
# Invariants the model states under ANOTHER aggregate's section that this slice appears
|
|
92
127
|
# to touch — reported, never blocking, since this slice does not own them. Defaulted so
|
|
93
128
|
# the gate message and the outputs read a real value on a run that skipped the pass.
|
|
@@ -101,9 +136,11 @@ vars:
|
|
|
101
136
|
# Integrity pass (referrer blast radius) defaults — safe values if xref/test-run is
|
|
102
137
|
# skipped or errors, so the gate reads a real verdict rather than an empty var.
|
|
103
138
|
integrity_ok: "false"
|
|
104
|
-
referrer_csv: ""
|
|
139
|
+
referrer_csv: "" # referrers THAT HAVE CODE — what the fence can actually run
|
|
105
140
|
referrer_count: "0"
|
|
106
141
|
referrer_names: ""
|
|
142
|
+
unbuilt_referrers: "" # referrers that are planned and not implemented: nothing to run
|
|
143
|
+
unbuilt_count: "0"
|
|
107
144
|
# Pass-3 (OKF) reporting defaults — the knowledge pass is advisory, so these stay
|
|
108
145
|
# readable in the outputs even when a step is skipped or errors out.
|
|
109
146
|
okf_ok: "unknown"
|
|
@@ -128,9 +165,10 @@ workflow:
|
|
|
128
165
|
script: { ref: actions }
|
|
129
166
|
subcommand: feature-paths
|
|
130
167
|
outputs:
|
|
131
|
-
- { name: feature,
|
|
132
|
-
- { name: feature_dir,
|
|
133
|
-
- { name: spec_file,
|
|
168
|
+
- { name: feature, from: "$.feature" }
|
|
169
|
+
- { name: feature_dir, from: "$.feature_dir" }
|
|
170
|
+
- { name: spec_file, from: "$.spec_file" }
|
|
171
|
+
- { name: backend_plan, from: "$.backend_plan" } # the ports + §4.3 wiring the composition pass reads
|
|
134
172
|
|
|
135
173
|
- id: require_feature
|
|
136
174
|
action: verify_context
|
|
@@ -256,13 +294,55 @@ workflow:
|
|
|
256
294
|
- { name: test_log, from: "$.log_tail" }
|
|
257
295
|
- { name: test_matched, from: "$.matched" }
|
|
258
296
|
- { name: test_scoped, from: "$.scoped" }
|
|
297
|
+
# The lines that NAME the failure, per failing lane, with the command and exit
|
|
298
|
+
# code that produced them — what the fix step reads. A positional tail can be
|
|
299
|
+
# entirely green while the failure sits above it.
|
|
300
|
+
- { name: test_excerpt, from: "$.failing_excerpt" }
|
|
301
|
+
- { name: test_failing_lanes, from: "$.failing_lanes" }
|
|
302
|
+
# "No suite ran, and here is why" — no test script, no runner, no test project.
|
|
303
|
+
# Distinct from a red suite, and from a scoped filter that matched nothing.
|
|
304
|
+
- { name: test_no_setup, from: "$.no_setup" }
|
|
305
|
+
- { name: test_setup_reason, from: "$.setup_reason" }
|
|
259
306
|
- id: green_break
|
|
260
307
|
action: break
|
|
261
308
|
condition: "{{test_ok}} == true"
|
|
309
|
+
# WAS A SUITE ACTUALLY RUN? Two ways it was not, and both are STRUCTURAL: there is no
|
|
310
|
+
# failing test to repair, and three fix attempts against one only spend model passes
|
|
311
|
+
# to re-discover the same emptiness.
|
|
312
|
+
# • `no_setup` — test-run read the runner's own words: no test script wired up, no
|
|
313
|
+
# runner installed, no test project. It carries the reason.
|
|
314
|
+
# • `matched=false` on a SCOPED run — a .NET slice filter that selected zero tests.
|
|
315
|
+
# Only meaningful when scoped, since `matched` reads a .NET-shaped summary line;
|
|
316
|
+
# a failing jest run says "Tests: 3 failed" and would otherwise read as empty.
|
|
317
|
+
# The engine has no compound conditions, so each AND is an ordered assignment —
|
|
318
|
+
# `not_green` carries "the run was not green" into both tests.
|
|
319
|
+
- id: flag_not_green
|
|
320
|
+
action: set
|
|
321
|
+
vars: { not_green: "true" }
|
|
322
|
+
- id: flag_no_setup
|
|
323
|
+
condition: "{{test_no_setup}} == true"
|
|
324
|
+
action: set
|
|
325
|
+
vars: { tests_missing: "{{not_green}}" }
|
|
326
|
+
- id: flag_zero_match
|
|
327
|
+
condition: "{{test_scoped}} == true"
|
|
328
|
+
action: set
|
|
329
|
+
vars: { zero_match: "true" }
|
|
330
|
+
- id: unflag_zero_match
|
|
331
|
+
condition: "{{test_matched}} == true"
|
|
332
|
+
action: set
|
|
333
|
+
vars: { zero_match: "false" }
|
|
334
|
+
- id: flag_tests_missing
|
|
335
|
+
condition: "{{zero_match}} == true"
|
|
336
|
+
action: set
|
|
337
|
+
vars: { tests_missing: "{{not_green}}" }
|
|
338
|
+
- id: missing_break
|
|
339
|
+
action: break
|
|
340
|
+
condition: "{{tests_missing}} == true"
|
|
262
341
|
- id: ctx_fix
|
|
263
342
|
action: build_context
|
|
264
343
|
vars:
|
|
265
344
|
test_log: "{{test_log}}"
|
|
345
|
+
test_excerpt: "{{test_excerpt}}"
|
|
266
346
|
context:
|
|
267
347
|
- { path: "{{spec_file}}" }
|
|
268
348
|
- { path: "{{domain_model}}", optional: true }
|
|
@@ -272,29 +352,64 @@ workflow:
|
|
|
272
352
|
tools: "Read,Write,Edit,Bash"
|
|
273
353
|
permission_mode: acceptEdits
|
|
274
354
|
task: |
|
|
275
|
-
Tests failed
|
|
355
|
+
Tests failed (tool {{test_tool}}, failing lane(s) {{test_failing_lanes}}; each
|
|
356
|
+
excerpt below is labelled with the lane, its command and its exit code).
|
|
357
|
+
Make them pass by fixing the code (or a genuinely wrong
|
|
276
358
|
test) in the files in context, keeping behaviour aligned with the spec's
|
|
277
359
|
acceptance criteria (spec in context). Where the spec cites an invariant
|
|
278
360
|
by id (BR-nnn), the bounded context's domain-model.md in context carries
|
|
279
361
|
its text and the entity names it is stated over — read the rule there
|
|
280
|
-
rather than inferring it from the id.
|
|
362
|
+
rather than inferring it from the id.
|
|
363
|
+
FIX WHAT THE LOG ACTUALLY SAYS. The failing lines are below; if they do not
|
|
364
|
+
show a real failure, say so and change nothing rather than inventing a repair.
|
|
365
|
+
Failing lines:
|
|
366
|
+
{{test_excerpt}}
|
|
367
|
+
Log tail (context, may be from a lane that passed):
|
|
281
368
|
{{test_log}}
|
|
282
369
|
outputs:
|
|
283
370
|
- { name: fixed_files, from: "$.files" }
|
|
284
371
|
|
|
285
|
-
|
|
372
|
+
# THE RED GATE — a suite that RAN and failed, after 3 fix attempts. It stops here, and
|
|
373
|
+
# deliberately: the passes below score the code against the model, the container and the
|
|
374
|
+
# referrers, and running them over code whose own tests fail reports findings nobody can
|
|
375
|
+
# act on until the tests are green. The structural case does NOT come here (see below).
|
|
376
|
+
- id: flag_tests_red
|
|
377
|
+
condition: "{{test_ok}} != true"
|
|
378
|
+
action: set
|
|
379
|
+
vars: { tests_red: "true" }
|
|
380
|
+
- id: unflag_tests_red
|
|
381
|
+
condition: "{{tests_missing}} == true"
|
|
382
|
+
action: set
|
|
383
|
+
vars: { tests_red: "false", slice_testable: "false", tests_present: "false" }
|
|
384
|
+
- id: red_gate
|
|
286
385
|
action: verify_context
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
386
|
+
require_value: tests_red
|
|
387
|
+
equals: "false"
|
|
388
|
+
on_mismatch: stop
|
|
389
|
+
on_mismatch_message: >
|
|
290
390
|
Slice tests not green after 3 fix attempts (tool={{test_tool}}, scoped={{test_scoped}},
|
|
291
|
-
matched={{test_matched}}).
|
|
292
|
-
per-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
391
|
+
matched={{test_matched}}). scoped=true means a per-feature name filter ran; scoped=false
|
|
392
|
+
means this stack has no per-test selector, so the lane's whole declared suite ran and
|
|
393
|
+
every failure in it counts here, including ones this feature did not cause.
|
|
394
|
+
Failing lines (lane(s) {{test_failing_lanes}}):
|
|
395
|
+
{{test_excerpt}}
|
|
396
|
+
Last log: {{test_log}}
|
|
397
|
+
|
|
398
|
+
# THE STRUCTURAL CASE — nothing to run. It WARNS and carries on, so the passes below
|
|
399
|
+
# still report what they find; the gate fails at the end, once they have. Before this,
|
|
400
|
+
# a slice with no tests stopped here and the conformance, composition, integrity and
|
|
401
|
+
# knowledge passes never executed on any run.
|
|
402
|
+
- id: warn_no_tests
|
|
403
|
+
action: verify_context
|
|
404
|
+
require_true: tests_present
|
|
405
|
+
on_false: continue
|
|
406
|
+
on_false_message: >
|
|
407
|
+
WARNING — this slice has NO TESTS TO RUN (tool={{test_tool}}, scoped={{test_scoped}},
|
|
408
|
+
matched={{test_matched}}). Reason: {{test_setup_reason}} — or, where that is blank,
|
|
409
|
+
the slice filter selected zero tests because no test name carries the aggregate's
|
|
410
|
+
name. Nothing failed and nothing passed. PASS 1 proves nothing on this run, and the integrity fence below
|
|
411
|
+
is skipped for the same reason — every other pass still runs, and this gate fails at
|
|
412
|
+
the end with what they found.
|
|
298
413
|
|
|
299
414
|
# ═══════ PASS 2 — DOMAIN CONFORMANCE: code vs the gate overlay checklist (validation) ═══════
|
|
300
415
|
# With tests green, scan the feature's CODE against the domain gate overlays (the
|
|
@@ -402,6 +517,99 @@ workflow:
|
|
|
402
517
|
on_false: stop
|
|
403
518
|
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}}"
|
|
404
519
|
|
|
520
|
+
# ═══════ PASS 2b — COMPOSITION: is everything the slice injects actually BOUND? ═══════
|
|
521
|
+
# The one failure class both other passes are blind to. A dependency-injection graph is
|
|
522
|
+
# not compile-checked, so a port that is injected and never registered builds green, runs
|
|
523
|
+
# every unit test green (they construct their subject directly), and fails on the first
|
|
524
|
+
# request that needs it. The mock run shipped exactly that: six of seven handlers took
|
|
525
|
+
# ports the composition root never bound.
|
|
526
|
+
#
|
|
527
|
+
# It reads FILES, so the query declares tools — the composition root is shared
|
|
528
|
+
# infrastructure and is often not among the slice's own concept paths. Registration by
|
|
529
|
+
# assembly scan or convention counts; assuming one without finding it does not.
|
|
530
|
+
- id: lane_resolve
|
|
531
|
+
action: execute_script
|
|
532
|
+
on_error: continue
|
|
533
|
+
script: { ref: actions }
|
|
534
|
+
subcommand: lane-resolve
|
|
535
|
+
outputs:
|
|
536
|
+
- { name: be_declared, from: "$.backend_declared" }
|
|
537
|
+
- { name: be_lane_id, from: "$.backend_lane_id" }
|
|
538
|
+
- { name: be_source_path, from: "$.backend_source_path" }
|
|
539
|
+
- id: wiring_arm
|
|
540
|
+
condition: "{{be_declared}} == true"
|
|
541
|
+
action: set
|
|
542
|
+
vars: { wiring_ok: "false" }
|
|
543
|
+
|
|
544
|
+
- id: wiring_loop
|
|
545
|
+
condition: "{{be_declared}} == true"
|
|
546
|
+
action: loop
|
|
547
|
+
while: "{{wiring_ok}} != true"
|
|
548
|
+
max_iterations: 2
|
|
549
|
+
steps:
|
|
550
|
+
- id: ctx_wiring
|
|
551
|
+
action: build_context
|
|
552
|
+
context:
|
|
553
|
+
- { path: "{{backend_plan}}", optional: true } # §2.1 ports + §4.1 adapters + §4.3 wiring
|
|
554
|
+
context_from: all_paths
|
|
555
|
+
- id: check_wiring
|
|
556
|
+
action: agent_query
|
|
557
|
+
tools: "Read,Grep"
|
|
558
|
+
schema: { ok: true, unbound: [ { dependency: "<injected type>", injected_by: "<file:type>", checked: "<the registration file(s) read>" } ], notes: [ "<a binding worth a second look>" ] }
|
|
559
|
+
mock: { ok: true, unbound: [], notes: [] }
|
|
560
|
+
task: |
|
|
561
|
+
WOULD THIS SLICE RUN? Check the service lane's composition against its code.
|
|
562
|
+
The lane is `{{be_lane_id}}` and its source path is `{{be_source_path}}`; the
|
|
563
|
+
backend plan is in context, and its §2.1 Ports, §4.1 Persistence and §4.3 Wiring
|
|
564
|
+
say what has to be bound.
|
|
565
|
+
|
|
566
|
+
1. Collect every dependency this slice's own types take by INJECTION — handler,
|
|
567
|
+
endpoint, adapter and service constructors, and the ports the plan names.
|
|
568
|
+
2. Read the lane's composition root: wherever this stack registers services (a
|
|
569
|
+
Program/Startup file, a registration/module/configuration class, a container
|
|
570
|
+
module, an assembly scan). Open the files; do not infer from names.
|
|
571
|
+
3. For each dependency, say whether the container binds it — an explicit
|
|
572
|
+
registration, an assembly scan or convention you have CONFIRMED covers that
|
|
573
|
+
type, or the framework itself providing it (a logger, a clock, an options
|
|
574
|
+
object, the HTTP context). A scan you assumed rather than found is not a
|
|
575
|
+
binding.
|
|
576
|
+
|
|
577
|
+
Every dependency with no binding is an `unbound` entry, naming where it is
|
|
578
|
+
injected and which registration file(s) you read. A binding that exists but whose
|
|
579
|
+
LIFETIME contradicts what it holds — a singleton capturing a scoped unit of work,
|
|
580
|
+
a scoped dependency in a singleton — goes in `notes`, which never affects ok.
|
|
581
|
+
Judge only this slice's types; another feature's unbound port is not this gate's.
|
|
582
|
+
ok=true only when every dependency this slice injects is bound.
|
|
583
|
+
outputs:
|
|
584
|
+
- { name: wiring_ok, from: "$.ok" }
|
|
585
|
+
- { name: unbound, from: "$.unbound" }
|
|
586
|
+
- { name: wiring_notes, from: "$.notes" }
|
|
587
|
+
- id: wiring_break
|
|
588
|
+
action: break
|
|
589
|
+
condition: "{{wiring_ok}} == true"
|
|
590
|
+
- id: fix_wiring
|
|
591
|
+
action: agent_generate
|
|
592
|
+
tools: "Read,Write,Edit,Bash"
|
|
593
|
+
permission_mode: acceptEdits
|
|
594
|
+
task: |
|
|
595
|
+
Bind these dependencies in the `{{be_lane_id}}` lane's composition root, under
|
|
596
|
+
`{{be_source_path}}`: {{unbound}}
|
|
597
|
+
Register each one the way this project already registers its neighbours — same
|
|
598
|
+
file, same helper, same lifetime convention — and bind the implementation the
|
|
599
|
+
plan's §4.1 / §4.3 names, never a new one you invent. Change no behaviour: this
|
|
600
|
+
is wiring, not design. If a dependency has NO implementation to bind because
|
|
601
|
+
nothing was ever written for it, do not stub one — leave it and say so, since
|
|
602
|
+
that is a generation gap /appweaver implement closes.
|
|
603
|
+
Then re-run the slice's tests (Bash) to confirm they stay green.
|
|
604
|
+
outputs:
|
|
605
|
+
- { name: wiring_fixes, from: "$.files" }
|
|
606
|
+
|
|
607
|
+
- id: wiring_gate
|
|
608
|
+
action: verify_context
|
|
609
|
+
require_true: wiring_ok
|
|
610
|
+
on_false: stop
|
|
611
|
+
on_false_message: "The service lane's container does not bind everything this slice injects, after 2 fix attempts: {{unbound}}. A dependency-injection graph is not compile-checked, so this builds green and fails on the first request that needs it. Bind each one in the lane's composition root under {{be_source_path}} — or, if nothing was ever written to bind, run /appweaver implement to generate it. Lifetime notes (not blocking): {{wiring_notes}}"
|
|
612
|
+
|
|
405
613
|
# ═══════ INTEGRITY — did this feature's change break any aggregate that USES it? ═══════
|
|
406
614
|
# The slice is green + domain-conformant on its own. But a change to a feature can break
|
|
407
615
|
# any OTHER aggregate that references it. Ask the OKF Cross-References index "who refers to
|
|
@@ -411,6 +619,12 @@ workflow:
|
|
|
411
619
|
# broken referrer STOPS the gate for a human to resolve, it is not auto-fixed here.
|
|
412
620
|
# Empty referrer set (a leaf feature, or the only aggregate in the repo) → the fence is just
|
|
413
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.
|
|
414
628
|
- id: xref_referrers
|
|
415
629
|
action: execute_script
|
|
416
630
|
on_error: continue
|
|
@@ -418,11 +632,18 @@ workflow:
|
|
|
418
632
|
subcommand: list
|
|
419
633
|
args: ["--feature-dir", "{{feature_dir}}"]
|
|
420
634
|
outputs:
|
|
421
|
-
- { name: referrer_csv,
|
|
422
|
-
- { name: referrer_count,
|
|
423
|
-
- { 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" }
|
|
424
640
|
|
|
641
|
+
# Skipped when PASS 1 found nothing to run: this is the same test command over a wider
|
|
642
|
+
# scope, so with no tests it would report the same emptiness a second time and fail a
|
|
643
|
+
# fence over it. `integrity_gate_ok` is what blocks, and it stays true for a fence that
|
|
644
|
+
# never ran — `integrity_ok` keeps reporting what actually happened.
|
|
425
645
|
- id: run_integrity
|
|
646
|
+
condition: "{{tests_missing}} == false"
|
|
426
647
|
action: execute_script
|
|
427
648
|
on_error: continue
|
|
428
649
|
script: { ref: actions }
|
|
@@ -431,6 +652,12 @@ workflow:
|
|
|
431
652
|
outputs:
|
|
432
653
|
- { name: integrity_ok, from: "$.fence_green" }
|
|
433
654
|
- { name: integrity_log, from: "$.log_tail" }
|
|
655
|
+
# Ordered assignment again: a fence that is not green blocks UNLESS it was skipped, and
|
|
656
|
+
# `tests_missing` carries exactly that (it reads "true" only when the fence did not run).
|
|
657
|
+
- id: integrity_verdict
|
|
658
|
+
condition: "{{integrity_ok}} != true"
|
|
659
|
+
action: set
|
|
660
|
+
vars: { integrity_gate_ok: "{{tests_missing}}" }
|
|
434
661
|
|
|
435
662
|
# On a RED fence, author a durable triage report BEFORE stopping — a broken referrer
|
|
436
663
|
# means someone must go fix ANOTHER slice, so this is a record, not scrollback (mirrors
|
|
@@ -450,7 +677,7 @@ workflow:
|
|
|
450
677
|
context_from: all_paths
|
|
451
678
|
- id: write_broken_referrers
|
|
452
679
|
action: agent_author
|
|
453
|
-
condition: "{{
|
|
680
|
+
condition: "{{integrity_gate_ok}} == false"
|
|
454
681
|
include_standards: false # a triage report, not generated code
|
|
455
682
|
output_target: { type: file, path: "{{feature_dir}}/broken-referrers.md" }
|
|
456
683
|
task: |
|
|
@@ -458,7 +685,10 @@ workflow:
|
|
|
458
685
|
REFER to this feature whose tests went red when this feature's change was run
|
|
459
686
|
against them. This is a triage record for a human — analyze-slice does NOT fix a
|
|
460
687
|
referrer (that is the referrer's own slice's job). Referrer blast radius
|
|
461
|
-
({{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:
|
|
462
692
|
{{integrity_log}}
|
|
463
693
|
Write markdown, in this order:
|
|
464
694
|
- `# Broken Referrers — <feature>` and a one-line statement of what this file is:
|
|
@@ -488,7 +718,7 @@ workflow:
|
|
|
488
718
|
|
|
489
719
|
- id: integrity_gate
|
|
490
720
|
action: verify_context
|
|
491
|
-
require_true:
|
|
721
|
+
require_true: integrity_gate_ok
|
|
492
722
|
on_false: stop
|
|
493
723
|
on_false_message: "Integrity check failed — this feature's change breaks a test in an aggregate that REFERS to it. Referrer blast radius ({{referrer_count}}): {{referrer_names}}. Triage written to {{feature_dir}}/broken-referrers.md — resolve each in the referrer's own slice (or reconsider this feature's contract change), then re-run. Last log: {{integrity_log}}"
|
|
494
724
|
|
|
@@ -545,6 +775,26 @@ workflow:
|
|
|
545
775
|
- { name: okf_ok, from: "$.ok" }
|
|
546
776
|
- { name: okf_orphans, from: "$.orphan_count" }
|
|
547
777
|
|
|
778
|
+
# THE LAST GATE — a slice with no tests fails HERE, not at PASS 1, so everything the
|
|
779
|
+
# other passes found has already been reported and any fix they made is on disk. It sits
|
|
780
|
+
# before the after-hooks, because an extension wired to `after_analyze_slice` runs on a
|
|
781
|
+
# slice this gate has passed.
|
|
782
|
+
- id: untested_gate
|
|
783
|
+
action: verify_context
|
|
784
|
+
require_true: tests_present
|
|
785
|
+
on_false: stop
|
|
786
|
+
on_false_message: >
|
|
787
|
+
The slice was analysed but NOT tested — it has no tests to run (tool={{test_tool}},
|
|
788
|
+
scoped={{test_scoped}}, matched={{test_matched}}). Reason: {{test_setup_reason}}.
|
|
789
|
+
So PASS 1 and the integrity fence proved nothing. Everything above ran: domain
|
|
790
|
+
conformance={{conformance_ok}}, composition={{wiring_ok}}, OKF index={{okf_ok}}.
|
|
791
|
+
A lane whose test command is not wired up is fixed in that lane — add the script or
|
|
792
|
+
the runner its declared command expects, or correct the command in
|
|
793
|
+
.appweaver/stacks.json. Otherwise, generate the missing tests —
|
|
794
|
+
/appweaver implement writes a paired test per plan element, under the test path the
|
|
795
|
+
lane declares — or, if the tests exist, name them so each carries its aggregate's
|
|
796
|
+
name, which is what the slice filter selects on. Then re-run /appweaver analyze-slice.
|
|
797
|
+
|
|
548
798
|
- id: resolve_post_hooks
|
|
549
799
|
action: execute_script
|
|
550
800
|
script: { ref: actions }
|
|
@@ -570,7 +820,15 @@ outputs:
|
|
|
570
820
|
# Reported by the conformance pass and never gated — surfaced so a real cross-aggregate
|
|
571
821
|
# leak is visible rather than only living in one query's answer.
|
|
572
822
|
model_notes: "{{model_notes}}"
|
|
823
|
+
# The composition pass. `true` with no backend lane declared — there is no container.
|
|
824
|
+
# `wiring_notes` are lifetime observations, reported and never gated.
|
|
825
|
+
wiring_ok: "{{wiring_ok}}"
|
|
826
|
+
wiring_notes: "{{wiring_notes}}"
|
|
573
827
|
test_tool: "{{test_tool}}"
|
|
828
|
+
# false when the slice had no tests to run — PASS 1 and the integrity fence proved
|
|
829
|
+
# nothing on this run, and the gate failed at the end saying so.
|
|
830
|
+
tests_present: "{{tests_present}}"
|
|
831
|
+
tests_matched: "{{test_matched}}"
|
|
574
832
|
referrer_count: "{{referrer_count}}"
|
|
575
833
|
referrer_blast_radius: "{{referrer_names}}"
|
|
576
834
|
okf_concept: "{{okf_concept}}"
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
|
|
81
81
|
name: aw-spec-constitution
|
|
82
82
|
description: Create or update the split project constitution — constitution.technical.md (technology base) + constitution.ui.md (the frontend lane's base, only where a lane exists) + constitution.domain.md (active domain addenda), each version-bumped with a Sync Impact Report, then composed into constitution.md. Project-level, run-once/amend — not a per-feature phase.
|
|
83
|
-
version: 2.
|
|
83
|
+
version: 2.5.0
|
|
84
84
|
agent_agnostic: true
|
|
85
85
|
dialect: aw-spec.orchestrator/v1
|
|
86
86
|
|
|
@@ -561,9 +561,9 @@ workflow:
|
|
|
561
561
|
invent principles, and do NOT reach for a rule file the map did not supply. If NO
|
|
562
562
|
domain rule file was loaded, say so in the Sync Impact Report and keep the principles
|
|
563
563
|
to what the user input actually supports.
|
|
564
|
-
- Author ONLY the domain layer: the
|
|
565
|
-
principles, domain folder additions, regulatory posture &
|
|
566
|
-
domain governance overrides. For each domain principle, state which technical
|
|
564
|
+
- Author ONLY the domain layer: the project identity, the adopted modelling
|
|
565
|
+
standard, the domain principles, domain folder additions, regulatory posture &
|
|
566
|
+
security addenda, and domain governance overrides. For each domain principle, state which technical
|
|
567
567
|
principle it EXTENDS. Do NOT restate the technical half's principles or the base
|
|
568
568
|
governance — reference them. Where constitution.technical.md is absent from
|
|
569
569
|
context the project declared no service lane and there is no technical half to
|