qfai 1.9.2 → 1.10.0
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/README.md +48 -2
- package/assets/init/.qfai/assistant/agents/acceptance-test-engineer.md +1 -0
- package/assets/init/.qfai/assistant/agents/backend-engineer.md +1 -0
- package/assets/init/.qfai/assistant/agents/completion-reviewer.md +13 -2
- package/assets/init/.qfai/assistant/agents/delivery-planner.md +9 -0
- package/assets/init/.qfai/assistant/agents/frontend-engineer.md +1 -0
- package/assets/init/.qfai/assistant/agents/implementation-reviewer.md +6 -0
- package/assets/init/.qfai/assistant/agents/orchestrator.md +2 -2
- package/assets/init/.qfai/assistant/agents/qa-gatekeeper.md +96 -3
- package/assets/init/.qfai/assistant/agents/test-design-analyst.md +20 -3
- package/assets/init/.qfai/assistant/catalog/cli-ux-guidelines.md +2 -2
- package/assets/init/.qfai/assistant/catalog/spec_required_files.json +2 -1
- package/assets/init/.qfai/assistant/catalog/test-layers.md +355 -14
- package/assets/init/.qfai/assistant/catalog/worklog-entry.schema.md +165 -0
- package/assets/init/.qfai/assistant/constitution/communication.md +1 -1
- package/assets/init/.qfai/assistant/constitution/drift-protocol.md +304 -10
- package/assets/init/.qfai/assistant/constitution/quality.md +35 -5
- package/assets/init/.qfai/assistant/constitution/requirements-decomposition.md +37 -0
- package/assets/init/.qfai/assistant/constitution/shared-skill-delegation-baseline.md +244 -8
- package/assets/init/.qfai/assistant/constitution/shared-skill-operating-baseline.md +122 -5
- package/assets/init/.qfai/assistant/constitution/workflow.md +53 -7
- package/assets/init/.qfai/assistant/manifest/agent-catalog.yml +316 -945
- package/assets/init/.qfai/assistant/manifest/agent-routing.yml +50 -4
- package/assets/init/.qfai/assistant/manifest/review-profiles.yml +9 -0
- package/assets/init/.qfai/assistant/process/migrations/v1.4.27-atdd-alignment.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-atdd/SKILL.md +61 -21
- package/assets/init/.qfai/assistant/skills/qfai-atdd/references/test-case-depth-checklist.md +23 -4
- package/assets/init/.qfai/assistant/skills/qfai-configure/SKILL.md +15 -7
- package/assets/init/.qfai/assistant/skills/qfai-discussion/SKILL.md +8 -4
- package/assets/init/.qfai/assistant/skills/qfai-discussion/references/design-md-brand-catalog.md +2 -2
- package/assets/init/.qfai/assistant/skills/qfai-discussion/references/discussion-completion-matrix.md +17 -7
- package/assets/init/.qfai/assistant/skills/qfai-discussion/references/rcp_footer.md +10 -4
- package/assets/init/.qfai/assistant/skills/qfai-discussion/references/review-cycle-playbook.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/references/ui-bearing-playbook.md +4 -4
- package/assets/init/.qfai/assistant/skills/qfai-discussion/references/ui_ux/review_audit_playbook.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/references/ui_ux/trend_scan_playbook.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/references/ui_ux_best_practices.md +17 -7
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/01_Context.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/02_Inception-Deck.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/03_Story-Workshop.md +11 -3
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/05_Scope.md +5 -2
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/07_NFR.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/09_Constraints.md +7 -4
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/10_Policy.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/11_OQ-Register.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/12_OQ-Resolution-Log.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/14_Review-Request.md +14 -7
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/99_delta.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/review/Rxx_reviewer.md +16 -7
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/review/review_request.md +9 -6
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/uiux/40_screen_contracts.md +3 -2
- package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/uiux/50_review_input_bundle.md +4 -2
- package/assets/init/.qfai/assistant/skills/qfai-implement/SKILL.md +250 -128
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/change-request-reset.md +93 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/checkpoint-verification.md +106 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/cross-spec-ownership.md +73 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/evidence-revision.md +77 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/execution-ledger.md +303 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/final-checklist.md +19 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/finding-classification.md +49 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/ledger-preconditions.md +55 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/oracle-strength.md +81 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/parallelization-policy.md +235 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/red-admissibility.md +91 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/red-not-observable.md +70 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/relevant-test-suite.md +87 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/review-artifact-layout.md +31 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/round-evidence.md +102 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/selector-granularity.md +24 -0
- package/assets/init/.qfai/assistant/skills/qfai-implement/references/volume-policy.md +149 -0
- package/assets/init/.qfai/assistant/skills/qfai-prototyping/SKILL.md +35 -18
- package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/evidence-requirements.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/generator-prompt.md +106 -7
- package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/handoff.md +40 -11
- package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/iteration-loop.md +48 -1
- package/assets/init/.qfai/assistant/skills/qfai-prototyping/templates/DESIGN.md.sample +6 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/SKILL.md +82 -22
- package/assets/init/.qfai/assistant/skills/qfai-sdd/references/contract-artifact-rules.md +148 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/references/rcp_footer.md +10 -4
- package/assets/init/.qfai/assistant/skills/qfai-sdd/references/review-cycle-playbook.md +1 -1
- package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-execution-playbook.md +4 -2
- package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-phase-checklists.md +18 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-quality-gate.md +44 -3
- package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-triage.md +60 -7
- package/assets/init/.qfai/assistant/skills/qfai-sdd/references/spec-traceability-rules.md +157 -5
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/change-request.md +125 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/contracts/db-contract.sample.sql +6 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/evidence/sdd-spec.md +92 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/01_Objective.md +27 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/02_Initiative.md +30 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/05_Contracts.md +16 -6
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/06_Glossary.md +19 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/07_Constraints.md +25 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/08_Decisions.md +22 -2
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/11_Slice-Policy.md +37 -10
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/02_User-stories.md +20 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/03_Acceptance-Criteria.md +19 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/04_Business-Rules.md +32 -3
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/06_Test-Cases.md +56 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/07_Decisions.md +29 -2
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/10_Plan.md +41 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/16_Traceability-ledger.md +58 -0
- package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/tdd/test-list.md +56 -0
- package/assets/init/.qfai/assistant/skills/qfai-verify/SKILL.md +57 -102
- package/assets/init/.qfai/assistant/skills/qfai-verify/references/articles.md +24 -0
- package/assets/init/.qfai/assistant/skills/qfai-verify/references/context-load.md +22 -0
- package/assets/init/.qfai/assistant/skills/qfai-verify/references/verify-output-contract.md +48 -0
- package/assets/init/.qfai/assistant/skills/qfai-verify/templates/verify-evidence.md +48 -0
- package/assets/init/.qfai/assistant/skills/web-research/SKILL.md +25 -7
- package/assets/init/.qfai/waivers.yml +11 -5
- package/assets/init/root/DESIGN.md +6 -0
- package/assets/init/root/qfai.config.yaml +15 -12
- package/dist/cli/index.cjs +11023 -7139
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +10963 -7080
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +8782 -5257
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +280 -8
- package/dist/index.d.ts +280 -8
- package/dist/index.mjs +11776 -8264
- package/dist/index.mjs.map +1 -1
- package/package.json +18 -19
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Approved Change Request reset (the only sanctioned backward transition)
|
|
2
|
+
|
|
3
|
+
`tdd/test-list.md` has a forward-only lifecycle
|
|
4
|
+
(`todo -> red -> green -> refactor -> done`) and skips `done` rows on
|
|
5
|
+
re-execution. An approved upstream change can invalidate rows that already
|
|
6
|
+
reached `green`, `refactor` or `done`, and the Change Request template
|
|
7
|
+
(`.qfai/assistant/skills/qfai-sdd/templates/change-request.md`) instructs the
|
|
8
|
+
approved actions to "reset the `tdd/test-list.md` rows this change
|
|
9
|
+
invalidates". Without a stated exception that instruction is unperformable:
|
|
10
|
+
the skill would either violate the forward-only rule or leave a `done` row
|
|
11
|
+
holding an implementation result the approved change already invalidated.
|
|
12
|
+
|
|
13
|
+
## The mandatory preflight
|
|
14
|
+
|
|
15
|
+
The reset cannot be reached from the normal loop: `Required Process` starts by
|
|
16
|
+
selecting the first `todo` row, and the skill exits with "nothing to do" when
|
|
17
|
+
every row is `done` — exactly the state an approved reset is meant to reopen.
|
|
18
|
+
So the sweep is a **preflight**, not an opportunistic step:
|
|
19
|
+
|
|
20
|
+
1. On every run, before the ledger is read for any other purpose, enumerate
|
|
21
|
+
`.qfai/decisions/CR-*.md` and keep those in scope for the target spec.
|
|
22
|
+
2. Apply every reset the approved ones authorise (conditions below).
|
|
23
|
+
3. Only then judge the ledger. A run whose preflight reset rows must not report
|
|
24
|
+
"nothing to do", because the all-`done` state it would report is stale.
|
|
25
|
+
|
|
26
|
+
A run with no in-scope approved CR does nothing here and proceeds unchanged.
|
|
27
|
+
|
|
28
|
+
## The exception
|
|
29
|
+
|
|
30
|
+
A `.qfai/decisions/CR-*.md` whose "Approved actions" name a downstream ledger
|
|
31
|
+
sweep may reset the rows that change invalidates back to `todo`. Those rows are
|
|
32
|
+
then re-executed rather than skipped.
|
|
33
|
+
|
|
34
|
+
This is an operator-approved act, not a skill decision. It is legal only when
|
|
35
|
+
all of the following hold:
|
|
36
|
+
|
|
37
|
+
- the CR's `Status` is `approved`, with `Approved by` and `Approved at`
|
|
38
|
+
populated;
|
|
39
|
+
- the CR's "Approved actions" section **enumerates the rows** — the `TDD-ID`s,
|
|
40
|
+
or a verifiable selection rule — so the reset scope is what the operator
|
|
41
|
+
approved and not what the skill later decided was invalidated. A row outside
|
|
42
|
+
that enumeration is not approved; widening the scope needs a new CR;
|
|
43
|
+
- each reset row records that CR's ID in its `DR-ID` column — that column
|
|
44
|
+
carries both `DR-*` and `CR-*` references as a comma-separated list, and the
|
|
45
|
+
ID is **retained** as the row re-runs through `red` / `green` / `refactor` /
|
|
46
|
+
`done`, since clearing it on the next transition erases the only record of the
|
|
47
|
+
approved reopen; and
|
|
48
|
+
- the reset is recorded in the CR's `Resolution` section (which rows, by
|
|
49
|
+
`TDD-ID`).
|
|
50
|
+
|
|
51
|
+
## A retained `CR-*` is not an exception's `DR-*`
|
|
52
|
+
|
|
53
|
+
A reset row that later hits an anomaly still owes a Decision Record. The
|
|
54
|
+
retained `CR-*` records the approved reopen, not the anomaly, so it does not
|
|
55
|
+
satisfy the `exception` requirement on its own: add the `DR-*` alongside it
|
|
56
|
+
(`DR-NNNN, CR-YYYYMMDD-NNNN`). `TDDLIST_EXCEPTION_MISSING_DR` fires on an
|
|
57
|
+
`exception` row whose `DR-ID` cell is empty **or holds `CR-*` references only**.
|
|
58
|
+
|
|
59
|
+
## What stays prohibited
|
|
60
|
+
|
|
61
|
+
- Any backward transition not covered by an approved CR, including the named
|
|
62
|
+
`green -> red` case. The error text is unchanged:
|
|
63
|
+
`"Backward transition prohibited: green -> red"`.
|
|
64
|
+
- Resetting rows before approval. A CR at `Status: open` authorises nothing.
|
|
65
|
+
- Resetting rows the CR's "Approved actions" did not name.
|
|
66
|
+
|
|
67
|
+
## When an in-scope CR counts as resolved
|
|
68
|
+
|
|
69
|
+
The spec completion gate covers only the `.qfai/decisions/CR-*.md` **in scope for
|
|
70
|
+
this spec**: `Impact scope` names this spec or a shared policy it depends on, or
|
|
71
|
+
this spec's artifacts reference it. A CR confined to another spec never blocks
|
|
72
|
+
this one.
|
|
73
|
+
|
|
74
|
+
An in-scope CR is **resolved** only when every one of these holds. A CR failing
|
|
75
|
+
any single one — a half-filled record included — is **unresolved** and blocks
|
|
76
|
+
completion:
|
|
77
|
+
|
|
78
|
+
- `Status` is `approved`, `rejected` or `superseded` (never `open`);
|
|
79
|
+
- `Approved by` / `Approved at` are populated, plus `Approved option` when
|
|
80
|
+
`Status` is `approved` and `Superseded by` when it is `superseded`;
|
|
81
|
+
- `Resolution` records what was actually done; and
|
|
82
|
+
- when `Status` is `approved`, `Applied at` is populated — approval alone does
|
|
83
|
+
not release the gate. It is set only after the owner-skill rerun in "Approved
|
|
84
|
+
actions" completed and upstream artifacts are updated, which is when
|
|
85
|
+
`constitution/drift-protocol.md` resumes downstream work.
|
|
86
|
+
|
|
87
|
+
## Ordering against the drift protocol
|
|
88
|
+
|
|
89
|
+
`.qfai/assistant/constitution/drift-protocol.md` resumes downstream work only
|
|
90
|
+
after the owner skill has been rerun and the upstream artifacts are updated.
|
|
91
|
+
The ledger sweep is downstream work, so it follows the owner-skill rerun; the
|
|
92
|
+
CR's `Applied at` is set once both are done. An `approved` CR without
|
|
93
|
+
`Applied at` is still unresolved and blocks spec completion.
|
package/assets/init/.qfai/assistant/skills/qfai-implement/references/checkpoint-verification.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Checkpoint Verification
|
|
2
|
+
|
|
3
|
+
"Checkpoint verification" is the whole-repository regression check run at a checkpoint boundary. It
|
|
4
|
+
is what item 12 of the 12-point gate refers to and the only thing it refers to.
|
|
5
|
+
|
|
6
|
+
## Checkpoint boundary
|
|
7
|
+
|
|
8
|
+
A checkpoint boundary is reached in exactly two places:
|
|
9
|
+
|
|
10
|
+
- **Per item** — after all routed blocking reviewers have returned PASS for the item and before it
|
|
11
|
+
transitions `refactor` -> `done`. Every item has exactly one.
|
|
12
|
+
- **Per spec** — after the last item in `test-list.md` reaches `done` or a valid `exception`, before
|
|
13
|
+
declaring spec-level completion.
|
|
14
|
+
|
|
15
|
+
No other point is a checkpoint boundary. There is no "every N items" rule.
|
|
16
|
+
|
|
17
|
+
## Verification command set (per item)
|
|
18
|
+
|
|
19
|
+
Run all of the following from the repository root, in order. Substitute the project's own runner for
|
|
20
|
+
the placeholders; record the literal commands actually executed in evidence.
|
|
21
|
+
|
|
22
|
+
1. The item's own test — the ledger's `Test file` **plus** the runner's test-name option applied to
|
|
23
|
+
the ledger's `Selector`. Two runner-specific decisions, and both must be made from the ledger row:
|
|
24
|
+
|
|
25
|
+
**a. The name option.** The `Selector` is a test NAME, and every common runner takes a name
|
|
26
|
+
through a flag, not a positional argument: a positional is a file filter. Vitest and Jest use
|
|
27
|
+
`-t`, pytest uses `-k`, `go test` uses `-run`. Passing the selector positionally
|
|
28
|
+
(`<test runner> '<Selector>'`) exits 1 with "No test files found", so the checkpoint would fail
|
|
29
|
+
on every item and none could leave `refactor`.
|
|
30
|
+
|
|
31
|
+
**b. The unit of selection.** What goes in front of the name option is whatever the runner
|
|
32
|
+
selects by, which is not always a file:
|
|
33
|
+
- **File-selecting runners** (vitest, jest, pytest) take the `Test file` itself:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
<test runner> <Test file> -t '<Selector>'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- **Package-selecting runners** (`go test`) take a package. `go help test` documents the usage
|
|
40
|
+
as `go test [build/test flags] [packages] [build/test flags & test binary flags]`, and it
|
|
41
|
+
compiles the package's sources together with the matching `*_test.go` files. Handing it a lone
|
|
42
|
+
file switches it into file mode and drops the rest of the package from the build, so the item's
|
|
43
|
+
test normally fails on undefined symbols. Derive the package from the `Test file`'s directory:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
go test ./<dir of Test file> -run '<Selector>'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Record the literal command actually run either way. If a project's runner selects by something
|
|
50
|
+
else again, derive that unit from `Test file` in the same manner and say so in the evidence.
|
|
51
|
+
|
|
52
|
+
2. The full test suite — `<test runner>` with no file filter and no test-name option.
|
|
53
|
+
3. The project's static gates, when the repository defines them — formatter check, linter, and type
|
|
54
|
+
check.
|
|
55
|
+
4. `npx qfai validate --profile tdd --fail-on error` — qfai is a project dependency, not a global
|
|
56
|
+
command; a bare `qfai …` is `command not found` (exit 127) on a normal local install, which would
|
|
57
|
+
fail every checkpoint.
|
|
58
|
+
|
|
59
|
+
## Verification command set (per spec)
|
|
60
|
+
|
|
61
|
+
The spec-level boundary has no "item just completed" — a re-run in a later session has none, and
|
|
62
|
+
under parallel slices the ledger order does not identify one either. So step 1 is dropped: the
|
|
63
|
+
spec-level set is steps 2, 3 and 4 only. Everything step 1 would have proved is already covered by
|
|
64
|
+
the full suite.
|
|
65
|
+
|
|
66
|
+
## Pass criteria
|
|
67
|
+
|
|
68
|
+
Checkpoint verification PASSES only when **every** command in the applicable set exits 0, and step 4
|
|
69
|
+
reports zero `QFAI-TEST-001` findings. Any non-zero exit is a FAIL: for a per-item checkpoint the
|
|
70
|
+
item stays at `refactor`, the failure is fixed, and the whole set is re-run. A partial run is not a
|
|
71
|
+
pass.
|
|
72
|
+
|
|
73
|
+
**A fix invalidates the reviewer PASS that preceded it.** The per-item boundary sits after
|
|
74
|
+
`completion-reviewer` and `implementation-reviewer` returned PASS (Phase: Refactor, steps 4-5), so
|
|
75
|
+
those verdicts were given against the pre-fix test and production code. Whenever the fix changes
|
|
76
|
+
either, re-submit to every routed blocking reviewer the change could affect — always the ones whose
|
|
77
|
+
scope it touches — and obtain a fresh PASS **before** re-running the command set. Re-running the
|
|
78
|
+
commands alone would let an item reach `done` carrying code no reviewer ever saw, which the
|
|
79
|
+
skill's own stale-evidence rule forbids. A fix that changes nothing a reviewer judged — re-running
|
|
80
|
+
after a flaky external service, say — needs no re-review; record which case applied.
|
|
81
|
+
|
|
82
|
+
## Spec-level boundary on an already-complete ledger
|
|
83
|
+
|
|
84
|
+
A ledger is **terminal** when every row is `done` or a valid `exception` — the same condition the
|
|
85
|
+
spec completion conditions use. The last row reaching `exception` ends the loop exactly as `done`
|
|
86
|
+
does, so both must reach this boundary.
|
|
87
|
+
|
|
88
|
+
The per-spec boundary is owed once per terminal ledger state, not once per session. Two cases leave
|
|
89
|
+
it unrecorded:
|
|
90
|
+
|
|
91
|
+
- the run was interrupted between the last row reaching its terminal status and the spec-level
|
|
92
|
+
verification;
|
|
93
|
+
- `/qfai-implement` is re-run against a ledger that is already terminal.
|
|
94
|
+
|
|
95
|
+
In both, an unconditional "nothing to do" exit would skip the boundary permanently, and no later
|
|
96
|
+
re-run could repair it — a re-run finds no `todo` rows to process either. So before that exit: read
|
|
97
|
+
`.qfai/evidence/implement-<spec-id>.md` for spec-level `Checkpoint verification command` /
|
|
98
|
+
`Checkpoint verification result` entries covering the current ledger state. Run the **per spec**
|
|
99
|
+
command set above and record them when they are absent, or when they predate the last ledger
|
|
100
|
+
change. Only then report "nothing to do".
|
|
101
|
+
|
|
102
|
+
## Evidence
|
|
103
|
+
|
|
104
|
+
Record the result in `.qfai/evidence/implement-<spec-id>.md` using the two per-item evidence fields
|
|
105
|
+
`Checkpoint verification command` and `Checkpoint verification result`. As with RED/GREEN evidence,
|
|
106
|
+
a status without its command is invalid, and evidence from a previous checkpoint MUST NOT be reused.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Cross-spec Code Ownership
|
|
2
|
+
|
|
3
|
+
What to do when implementing one spec correctly requires changing something
|
|
4
|
+
another spec's `done` rows certify.
|
|
5
|
+
|
|
6
|
+
## Why this needs a rule at all
|
|
7
|
+
|
|
8
|
+
`/qfai-implement` constrains execution to **one spec** and scopes every artifact
|
|
9
|
+
it writes — the ledger, the evidence file — to that spec. The codebase is not
|
|
10
|
+
partitioned at all, qfai defines no module → owning-spec index, and the Refactor
|
|
11
|
+
phase **mandates** duplication removal one line before this rule applies.
|
|
12
|
+
|
|
13
|
+
So the collision is the normal case, not an edge one. And both available
|
|
14
|
+
outcomes used to violate a shipped rule:
|
|
15
|
+
|
|
16
|
+
- change the file, and spec A's `done` rows now certify behaviour that was
|
|
17
|
+
re-tested by nobody — `done` has no outbound edge, so those rows cannot be
|
|
18
|
+
reopened;
|
|
19
|
+
- do not change it, and the Refactor phase's own instruction is unmet, and
|
|
20
|
+
spec B ships a duplicate.
|
|
21
|
+
|
|
22
|
+
Neither left any trace under `.qfai/**`. `drift-protocol.md`'s upstream list
|
|
23
|
+
contains no code or test artifacts, so the STOP → Change Request → owner-rerun
|
|
24
|
+
route never fired for them.
|
|
25
|
+
|
|
26
|
+
## The rule
|
|
27
|
+
|
|
28
|
+
**A file another spec's ledger names in `Test file` is that spec's to certify.**
|
|
29
|
+
Editing it does not require permission — it requires a record and a re-review.
|
|
30
|
+
|
|
31
|
+
1. **Detect.** Before editing a production file in Refactor, search every
|
|
32
|
+
`tdd/test-list.md` for it. A hit means the edit is cross-spec.
|
|
33
|
+
2. **Record.** Add a `## Cross-spec obligations` entry to this spec's evidence
|
|
34
|
+
file (fields below).
|
|
35
|
+
3. **Re-review.** Run `completion-reviewer` against the affected specs'
|
|
36
|
+
obligations as well as this one's. The reviewer is the party that can say
|
|
37
|
+
whether the other spec's assertions still hold.
|
|
38
|
+
4. **Do not close over it.** An open entry is a completion prohibition
|
|
39
|
+
(`qfai-implement/SKILL.md#completion-prohibition-conditions`).
|
|
40
|
+
|
|
41
|
+
## The evidence entry
|
|
42
|
+
|
|
43
|
+
Per affected spec, in `.qfai/evidence/implement-<spec-id>.md`:
|
|
44
|
+
|
|
45
|
+
| Field | Meaning |
|
|
46
|
+
| -------------------- | ------------------------------------------------------------- |
|
|
47
|
+
| `TDD-ID` | the row in **this** spec whose work forced the change |
|
|
48
|
+
| `Blocked spec` | the spec whose `done` rows the file belongs to |
|
|
49
|
+
| `Blocked TDD-IDs` | the rows in that spec that name the file |
|
|
50
|
+
| `File` | the exact path changed |
|
|
51
|
+
| `Change required` | what had to change, in one sentence |
|
|
52
|
+
| `Obligation at risk` | what that spec asserted about the file that is now unverified |
|
|
53
|
+
| `Resolution` | `re-reviewed` (the reviewer confirmed it holds) or `CR-*` |
|
|
54
|
+
|
|
55
|
+
`Obligation at risk` is the load-bearing field. "Changed a shared helper" is not
|
|
56
|
+
a record; "spec-0004 TDD-0012 asserts this helper rejects an empty batch, and
|
|
57
|
+
that path is now routed through the new guard" is.
|
|
58
|
+
|
|
59
|
+
## When re-review is not enough
|
|
60
|
+
|
|
61
|
+
If the other spec's obligation genuinely no longer holds — the behaviour it
|
|
62
|
+
asserted has changed, not merely moved — that is upstream drift, and
|
|
63
|
+
`constitution/drift-protocol.md#when-drift-is-detected` governs from step 1.
|
|
64
|
+
Record the CR id in `Resolution`. Re-review cannot ratify a behaviour change the
|
|
65
|
+
other spec never agreed to; only its owner can.
|
|
66
|
+
|
|
67
|
+
## What this does not do
|
|
68
|
+
|
|
69
|
+
It does not partition the codebase, and it does not give `done` an outbound
|
|
70
|
+
edge. Both are larger changes. What it does is make the collision **visible and
|
|
71
|
+
blocking** instead of silent — the two outcomes above are still the two
|
|
72
|
+
outcomes, but neither can now be taken without a record that names the
|
|
73
|
+
obligation it puts at risk.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Evidence revision (what state the observation describes)
|
|
2
|
+
|
|
3
|
+
Four of the twelve gate items — 3, 5, 7 and 8 — are sub-agent observations, and
|
|
4
|
+
`#evidence-hard-rules` says stale evidence MUST NOT be reused. That rule needs
|
|
5
|
+
something to compare against. This file defines it.
|
|
6
|
+
|
|
7
|
+
## The field
|
|
8
|
+
|
|
9
|
+
Every observation records the revision it was made against.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Revision: <git rev> | working-tree+<porcelain digest>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- **`<git rev>`** — the output of `git rev-parse HEAD` at the moment of the
|
|
16
|
+
observation. Preferred: it is exact and someone else can reproduce from it.
|
|
17
|
+
- **`working-tree+<porcelain digest>`** — for an uncommitted tree, the `HEAD`
|
|
18
|
+
rev plus a short digest of `git status --porcelain`. Not as good, and honest
|
|
19
|
+
about not being as good: it says "this observation was made against a state
|
|
20
|
+
that was never committed".
|
|
21
|
+
|
|
22
|
+
It appears in three places, all carrying the same address:
|
|
23
|
+
|
|
24
|
+
1. **Reviewer responses** — as `Reviewed revision`, per
|
|
25
|
+
`constitution/shared-skill-delegation-baseline.md#reviewer-response-template`.
|
|
26
|
+
2. **The per-item evidence contract** — one `Revision` per round block, beside
|
|
27
|
+
the RED / GREEN commands and results, and one for the refactor-verify pair.
|
|
28
|
+
3. **The review pack** — `summary.json`'s `revision` field, which is what makes
|
|
29
|
+
the fact machine-checkable (`QFAI-REVIEW-009`).
|
|
30
|
+
|
|
31
|
+
## Why an address, not a timestamp
|
|
32
|
+
|
|
33
|
+
A timestamp orders observations; it does not identify what was observed. Two
|
|
34
|
+
runs a second apart against different trees have different meanings and nearly
|
|
35
|
+
identical timestamps. The revision is the only thing that survives the question
|
|
36
|
+
"which code did this verdict actually rule on?".
|
|
37
|
+
|
|
38
|
+
Reviewers and `qa-gatekeeper` are dispatched against the **integrated** tree by
|
|
39
|
+
design — `constitution/workflow.md`'s worktree-separation rule constrains
|
|
40
|
+
implementers, not reviewers — so the tree a reviewer reads is legitimately
|
|
41
|
+
allowed to move under it. A fully independent reviewer reading a tree that is
|
|
42
|
+
being edited mid-review produces a verdict that is honest, independent and
|
|
43
|
+
unattributable. That is the failure this field exists for, and it is not fixed
|
|
44
|
+
by choosing a better reviewer.
|
|
45
|
+
|
|
46
|
+
## What makes evidence stale
|
|
47
|
+
|
|
48
|
+
Mechanically, so it can be checked rather than judged:
|
|
49
|
+
|
|
50
|
+
> Evidence is **stale** when the revision it names differs from the revision the
|
|
51
|
+
> item's work finally landed at.
|
|
52
|
+
|
|
53
|
+
Consequences:
|
|
54
|
+
|
|
55
|
+
- An item's four verdicts (gate items 3, 5, 7, 8) MUST all name the **same**
|
|
56
|
+
revision. Verdicts from different revisions do not compose into a ruling about
|
|
57
|
+
one state — the earlier ones ruled on code that no longer exists.
|
|
58
|
+
- A commit that changes any file the observation covered invalidates it. Re-run
|
|
59
|
+
the observation; do not carry the verdict forward because "the change was
|
|
60
|
+
unrelated". Whether it was unrelated is exactly the judgement the evidence
|
|
61
|
+
exists to remove.
|
|
62
|
+
- A reviewer that noticed the tree moving mid-review says so and pins its ruling
|
|
63
|
+
to a named revision. A pinned ruling is a valid verdict for **that** revision
|
|
64
|
+
and a stale one for any later revision.
|
|
65
|
+
|
|
66
|
+
## When the tree moved under a reviewer
|
|
67
|
+
|
|
68
|
+
State it, in the response, with the revision. Two forms are acceptable:
|
|
69
|
+
|
|
70
|
+
- **Pinned** — the ruling stands for the named revision. The orchestrator then
|
|
71
|
+
decides whether that revision is still the item's final state; if it is not,
|
|
72
|
+
the review is re-run rather than reinterpreted.
|
|
73
|
+
- **Aborted** — the reviewer could not obtain a coherent read at all (for
|
|
74
|
+
example, a suite whose collected count changed between two runs of the same
|
|
75
|
+
command). That is `REVISE` with the reason, not `PASS` with a caveat: a
|
|
76
|
+
mandated back-to-back identical run that could not be produced is a gate that
|
|
77
|
+
did not pass.
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Execution Ledger: test-list.md
|
|
2
|
+
|
|
3
|
+
The execution ledger at `.qfai/specs/<spec-id>/tdd/test-list.md` is the single record of what
|
|
4
|
+
`/qfai-implement` has done and may still do. This file holds its schema and its status rules.
|
|
5
|
+
|
|
6
|
+
## Required columns
|
|
7
|
+
|
|
8
|
+
| Column | Description |
|
|
9
|
+
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
10
|
+
| TDD-ID | Unique identifier for the TDD item (e.g., TDD-0001) |
|
|
11
|
+
| TC-Refs | References to test cases from `06_Test-Cases.md`. Belongs on `Layer = Unit` / `Component` / `Integration` rows |
|
|
12
|
+
| Layer | Test layer. Legal values: `Unit`, `Component`, `Integration`, `API`, `E2E` |
|
|
13
|
+
| Test file | Path to the test file |
|
|
14
|
+
| Selector | Test selector(s) for targeted execution — one entry, a comma-separated list, or a glob pattern |
|
|
15
|
+
| Status | Current lifecycle status |
|
|
16
|
+
| DR-ID | Decision Record / Change Request IDs, comma-separated: a `DR-*` is required for `exception` rows, a `CR-*` for a row reset by an approved Change Request and is retained through that row's later statuses; blank otherwise |
|
|
17
|
+
| Evidence | The RED/GREEN outcome in one word each, plus an anchor into `.qfai/evidence/implement-<spec-id>.md` for this TDD-ID. **Not** the commands and output themselves — see "Evidence cell contract" below |
|
|
18
|
+
|
|
19
|
+
## Declared seam column (optional, required for parallel dispatch)
|
|
20
|
+
|
|
21
|
+
| Column | Description |
|
|
22
|
+
| ------------- | ------------------------------------------------------------------------------------------ |
|
|
23
|
+
| Owning module | The production module this row will write, as a repo-relative path or a dotted module path |
|
|
24
|
+
|
|
25
|
+
`Owning module` is a **declaration, not an observation**, and that is the whole
|
|
26
|
+
point: it exists before the code does.
|
|
27
|
+
|
|
28
|
+
The parallel-dispatch gate asks whether two items write the same source module.
|
|
29
|
+
Under RED-first the production module does not exist when `delivery-planner`
|
|
30
|
+
has to answer, and the only path-valued required column is `Test file` — two
|
|
31
|
+
items trivially have independent test files and land on the same production
|
|
32
|
+
module. So the planner had nothing to evaluate against, and
|
|
33
|
+
`parallelization-policy.md`'s "cannot be explained with concrete file/module
|
|
34
|
+
evidence" asked for exactly what test-first withholds.
|
|
35
|
+
|
|
36
|
+
- Fill it at ledger-authoring time (`/qfai-sdd` Phase 2b) from the TC's parent
|
|
37
|
+
`BR`, which already names the behaviour's home.
|
|
38
|
+
- One module per row. A row that would honestly need two is a row that should
|
|
39
|
+
be split — that is the same signal `selector-granularity.md` describes.
|
|
40
|
+
- `-` is legal and means "not declared". A row carrying `-` is **not eligible
|
|
41
|
+
for parallel dispatch**; it may still be executed serially.
|
|
42
|
+
- It is a claim the row is later measured against, not a lock. See
|
|
43
|
+
`parallelization-policy.md#seam-reconciliation-after-a-parallel-run`.
|
|
44
|
+
|
|
45
|
+
## Obligation columns (optional, required by layer)
|
|
46
|
+
|
|
47
|
+
A row's obligation lives in the column its `Layer` selects. `TC-Refs` is the one
|
|
48
|
+
every row has; the other two are optional columns that become required when the
|
|
49
|
+
row's layer cannot host a `TC-*`.
|
|
50
|
+
|
|
51
|
+
| Column | Description |
|
|
52
|
+
| ------------ | --------------------------------------------------------------------------------- |
|
|
53
|
+
| US-Refs | `US-*` obligations this row implements. Legal **only** on `Layer = E2E` rows |
|
|
54
|
+
| CON-API-Refs | `CON-API-*` obligations this row implements. Legal **only** on `Layer = API` rows |
|
|
55
|
+
| Blocked-By | What a `blocked` row is waiting on. Required on `blocked` rows, blank otherwise |
|
|
56
|
+
|
|
57
|
+
`Blocked-By` takes a Change Request ID (`CR-YYYYMMDD-NNNN`), a contract path
|
|
58
|
+
with line (`.qfai/contracts/db/CON-DB-0005.sql:2715`), or a cross-spec row
|
|
59
|
+
(`spec-0006:TDD-0034`). `DR-ID` is **not** widened to carry it: that column is
|
|
60
|
+
what distinguishes a parked `exception` from a row that never started, and
|
|
61
|
+
overloading it would merge the two states the `blocked` status exists to
|
|
62
|
+
separate.
|
|
63
|
+
|
|
64
|
+
`test-layers.md` forbids `TC-*` annotations in `tests/e2e/**` and `tests/api/**`,
|
|
65
|
+
so an E2E or API row has no legal `TC-Refs` value. Those rows carry `-` in
|
|
66
|
+
`TC-Refs` and record their obligation in `US-Refs` / `CON-API-Refs` instead.
|
|
67
|
+
|
|
68
|
+
The binding is enforced in both directions: a `TC-*` on an E2E/API row raises
|
|
69
|
+
`TDDLIST_OBLIGATION_LAYER_MISMATCH` and is **not** counted towards TC coverage,
|
|
70
|
+
so a forbidden placement cannot close a coverage-target TC.
|
|
71
|
+
`TDDLIST_OBLIGATION_LAYER_MISMATCH` likewise rejects a `US-Refs` /
|
|
72
|
+
`CON-API-Refs` value on a layer that does not own it.
|
|
73
|
+
|
|
74
|
+
A `Layer` outside the legal values raises `TDDLIST_UNKNOWN_LAYER` (warning) —
|
|
75
|
+
without a legal `Layer` the row has no obligation column. Coverage counting
|
|
76
|
+
excludes `API` and `E2E` specifically rather than allowlisting the other three,
|
|
77
|
+
so a mistyped layer keeps counting and is reported by that warning, which names
|
|
78
|
+
the real cause; an allowlist would instead drop the row silently and resurface
|
|
79
|
+
as a coverage error about a TC the author did cover.
|
|
80
|
+
|
|
81
|
+
Coverage measurement is otherwise unaffected: it reads `TC-*` tokens only, so
|
|
82
|
+
non-TC obligation IDs are inert to it by design.
|
|
83
|
+
|
|
84
|
+
## Evidence cell contract
|
|
85
|
+
|
|
86
|
+
The `Evidence` cell is a **pointer**, not the payload.
|
|
87
|
+
|
|
88
|
+
`.qfai/evidence/implement-<spec-id>.md` is the single home for the per-item
|
|
89
|
+
evidence contract — the RED/GREEN commands, their output, and the reviewer
|
|
90
|
+
verdicts. The ledger cell records the outcome and says where to read the proof:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
RED fail / GREEN pass — evidence at `.qfai/evidence/implement-spec-0001.md#tdd-0027`
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Why it cannot hold the payload
|
|
97
|
+
|
|
98
|
+
A GFM table row is **one physical line**, and `splitMarkdownRow` ends a cell at
|
|
99
|
+
every unescaped `|`. Pasting a command and its output into the cell has two
|
|
100
|
+
failure modes, both silent:
|
|
101
|
+
|
|
102
|
+
- a newline ends the row — `parseAllMarkdownTables` stops the table at the first
|
|
103
|
+
line that does not start with `|`, so every row below it disappears from the
|
|
104
|
+
ledger the validators read;
|
|
105
|
+
- a bare `|` — a shell pipe, a table in the output, a regex alternation — splits
|
|
106
|
+
the row into extra cells and misaligns every column after `Evidence`.
|
|
107
|
+
|
|
108
|
+
The column description used to read "RED/GREEN command+result pairs proving the
|
|
109
|
+
TDD cycle", so following it literally corrupted the evidence or destroyed the
|
|
110
|
+
ledger. None of the Evidence hard rules change: they now bind the evidence file,
|
|
111
|
+
which can hold what they ask for.
|
|
112
|
+
|
|
113
|
+
### Evidence cell encoding
|
|
114
|
+
|
|
115
|
+
When a cell must contain either character, encode it with
|
|
116
|
+
`specPackParsers.ts#escapeTableCell` — the exported encoder the parser inverts.
|
|
117
|
+
The left column is the character **in the value you want the cell to hold**;
|
|
118
|
+
the right column is what is **written into the row**. Both are shown as HTML
|
|
119
|
+
entities, because a literal pipe in this table would split its own row.
|
|
120
|
+
|
|
121
|
+
Exactly two rules:
|
|
122
|
+
|
|
123
|
+
| Character in the value | Written into the cell |
|
|
124
|
+
| ---------------------- | --------------------- |
|
|
125
|
+
| a pipe (|) | \| |
|
|
126
|
+
| CR / LF / CRLF | a single space |
|
|
127
|
+
|
|
128
|
+
A literal `\` is **not** escaped: the parser passes it through unchanged, so
|
|
129
|
+
doubling it would corrupt Windows paths and regex literals while keeping the
|
|
130
|
+
column count valid — a corruption no validator can see.
|
|
131
|
+
|
|
132
|
+
## Evidence cell rules (enforced)
|
|
133
|
+
|
|
134
|
+
`Evidence` is checked as content, not only as a header name. On a row whose
|
|
135
|
+
`Status` is `green`, `refactor`, `review-fix` or `done` — the statuses that
|
|
136
|
+
assert a cycle has run:
|
|
137
|
+
|
|
138
|
+
| Finding | Fires when | Severity |
|
|
139
|
+
| ------------------------------ | ------------------------------------------------------------------- | -------- |
|
|
140
|
+
| `TDDLIST_EVIDENCE_EMPTY` | the cell is empty or holds only dash placeholders (`-`, `–`, `—`) | error |
|
|
141
|
+
| `TDDLIST_EVIDENCE_STATUS_ONLY` | the cell claims a verdict (`PASS`, `looks good`, …) with no command | warning |
|
|
142
|
+
|
|
143
|
+
A command is recognised by shape, not from a list of known runners, so the rule
|
|
144
|
+
holds on any stack: a program name followed by an argument carrying a flag, a
|
|
145
|
+
path, a selector or an assignment. Backticked commands and the common runners
|
|
146
|
+
are accepted directly.
|
|
147
|
+
|
|
148
|
+
`TDDLIST_EVIDENCE_STATUS_ONLY` is a warning, waivable under `TDDLIST-004`: a
|
|
149
|
+
ledger written before the check exists carries prose verdicts, and failing a
|
|
150
|
+
build on them is a migration rather than a gate. An empty cell is unambiguous,
|
|
151
|
+
so `TDDLIST_EVIDENCE_EMPTY` stays at `error`.
|
|
152
|
+
|
|
153
|
+
Rows at `todo`, `red` and `exception` are not checked — the first two have
|
|
154
|
+
nothing to show yet, and a parked row records its reason in `DR-ID`, which
|
|
155
|
+
`TDDLIST_EXCEPTION_MISSING_DR` gates.
|
|
156
|
+
|
|
157
|
+
Freshness is **not** gated: the ledger records no run identity, so no validator
|
|
158
|
+
can distinguish a fresh command+result pair from a copied one. That rule stays
|
|
159
|
+
with the routed reviewer (`qfai-implement/SKILL.md` "Evidence hard rules").
|
|
160
|
+
|
|
161
|
+
A pointer cell satisfies these rules: the `evidence at <path>` form carries a
|
|
162
|
+
path, which is one of the command shapes the gate accepts. The rules reject a
|
|
163
|
+
bare verdict, not a pointer.
|
|
164
|
+
|
|
165
|
+
## Selector granularity (MUST)
|
|
166
|
+
|
|
167
|
+
`Selector` is **not** restricted to a single test function: a row may own several entries, written
|
|
168
|
+
as a comma-separated list or a glob. What is restricted is what a row may _conflate_ — **one
|
|
169
|
+
independently observable boundary per selector entry**, with RED observed per entry, and a
|
|
170
|
+
matrix-shaped `TC-*` decomposed across rows before RED begins (`TC-Refs` is many-to-many with
|
|
171
|
+
`TDD-ID`). A selector that accumulates unrelated boundaries invalidates the RED observation. Rules
|
|
172
|
+
and examples: `selector-granularity.md`.
|
|
173
|
+
|
|
174
|
+
## Status Lifecycle
|
|
175
|
+
|
|
176
|
+
Valid status values: `todo`, `blocked`, `red`, `green`, `refactor`, `review-fix`, `done`, `exception`.
|
|
177
|
+
|
|
178
|
+
Allowed transitions:
|
|
179
|
+
|
|
180
|
+
- `todo` -> `blocked` (the row cannot be started: an upstream defect, an
|
|
181
|
+
unresolved Change Request, or an unfinished row in another spec). Name the
|
|
182
|
+
blocker in `Blocked-By`; `TDDLIST_BLOCKED_MISSING_REF` errors without it.
|
|
183
|
+
- `blocked` -> `todo` (the blocker cleared). This is a **resumption, not a
|
|
184
|
+
backward transition**: the row never started, so nothing is being undone.
|
|
185
|
+
- `todo` -> `red` (write a failing test)
|
|
186
|
+
- `red` -> `green` (make the test pass with minimal code)
|
|
187
|
+
- `green` -> `refactor` (improve code quality while keeping tests green)
|
|
188
|
+
- `refactor` -> `done` (item complete)
|
|
189
|
+
- `refactor` -> `review-fix` (a blocking reviewer returned `REVISE`)
|
|
190
|
+
- `review-fix` -> `refactor` (rework complete; re-submit to the reviewer)
|
|
191
|
+
- Any active status -> `exception` (anomaly detected; record DR-ID in DR-ID column)
|
|
192
|
+
- `red` | `green` | `refactor` | `done` | `exception` -> `todo` — **upstream
|
|
193
|
+
reset**, the only legal reopen, available from every status a row can hold.
|
|
194
|
+
Permitted **only** when an approved upstream change (Drift Protocol step 4
|
|
195
|
+
rerun) invalidated the row's obligation. The invalidating CR/DR ID MUST be
|
|
196
|
+
recorded in the `DR-ID` column, and the reset MUST cite it in `Evidence`.
|
|
197
|
+
That ID MUST be retained as the row moves on through `red`, `green`,
|
|
198
|
+
`refactor` and `done` — clearing it on the next transition erases the only
|
|
199
|
+
record of why a completed row was reopened. A row swept out of `exception`
|
|
200
|
+
keeps the anomaly's DR-ID alongside the reset ID. A reset without a recorded
|
|
201
|
+
approval is a backward transition and is prohibited.
|
|
202
|
+
- `exception` -> `todo` — **anomaly resolved**, the item re-enters the cycle
|
|
203
|
+
from the start. This is the exit `exception` previously lacked; without it a
|
|
204
|
+
parked item could never be un-parked without a lifecycle violation. Distinct
|
|
205
|
+
from the upstream reset above: nothing upstream changed, so it needs no CR/DR
|
|
206
|
+
approval — the anomaly's own DR-ID stays in place.
|
|
207
|
+
- A reset row is at `todo`, so it owes no test file until it reaches `green`.
|
|
208
|
+
The `Test file` existence check is unchanged for `green` / `refactor` /
|
|
209
|
+
`done`: those statuses assert a test that ran.
|
|
210
|
+
|
|
211
|
+
- `refactor` -> `red` (**QA rejection recovery — the only re-entry**): a routed
|
|
212
|
+
`qa-gatekeeper` returned `REVISE` on this row's RED/GREEN evidence because the
|
|
213
|
+
cycle itself was wrong. Batched (T1) review defers that confirmation until
|
|
214
|
+
after the row has left `red`, so without this edge a rejected row could never
|
|
215
|
+
redo the RED it was faulted for and could never reach `done`. Cite the verdict
|
|
216
|
+
in `Evidence`, re-run the micro-cycle; rules:
|
|
217
|
+
`volume-policy.md#group-formation-states-and-transitions`.
|
|
218
|
+
|
|
219
|
+
Backward transitions are otherwise prohibited and nothing but that QA rejection
|
|
220
|
+
re-opens a row. Attempting `green` -> `red` must produce:
|
|
221
|
+
`"Backward transition prohibited: green -> red"`. The upstream reset above is
|
|
222
|
+
not a backward transition: it is an owner-approved re-entry, and the row starts
|
|
223
|
+
its cycle again from `todo`.
|
|
224
|
+
|
|
225
|
+
The one exception is an approved Change Request reset — the only sanctioned
|
|
226
|
+
backward transition. Preconditions and the reset procedure:
|
|
227
|
+
`references/change-request-reset.md`.
|
|
228
|
+
|
|
229
|
+
### Reviewer rework is not a backward transition
|
|
230
|
+
|
|
231
|
+
A blocking reviewer's `REVISE` moves the item `refactor -> review-fix`. While at
|
|
232
|
+
`review-fix` the item MAY re-enter the RED/GREEN cycle as many times as the
|
|
233
|
+
rework needs — write the new failing test, watch it fail, make it pass — without
|
|
234
|
+
any of those runs counting as a backward transition. The row's status does not
|
|
235
|
+
change during that cycle: `review-fix -> red` and `review-fix -> green` are not
|
|
236
|
+
allowed transitions. When the rework is done the item returns to `refactor` and
|
|
237
|
+
is re-submitted.
|
|
238
|
+
|
|
239
|
+
`review-fix` is not a completion state and appears in the completion-prohibition
|
|
240
|
+
list. Round-by-round evidence rules: `round-evidence.md`.
|
|
241
|
+
|
|
242
|
+
## Blocked rows
|
|
243
|
+
|
|
244
|
+
`blocked` means **cannot be started**, not "not started yet" and not "anomaly".
|
|
245
|
+
|
|
246
|
+
- It is **completion-prohibiting**, exactly like `todo`. A spec must not close
|
|
247
|
+
over an unimplemented obligation, and naming the blocker does not discharge it.
|
|
248
|
+
- It is **not** selectable. Phase Red picks the first `todo` row and skips
|
|
249
|
+
`blocked` ones, so the loop head stops re-issuing rows that cannot proceed.
|
|
250
|
+
- It is **not** `exception`. `exception` is scoped to an anomaly, requires a
|
|
251
|
+
`DR-*`, and satisfies spec completion — filing a blocked row there would
|
|
252
|
+
silently close the obligation.
|
|
253
|
+
- `npx qfai report` counts it inside `open` but prints it separately
|
|
254
|
+
(`open: N (blocked: M)`), so "not started" and "cannot start" are readable
|
|
255
|
+
apart without changing what completion means.
|
|
256
|
+
|
|
257
|
+
## Exception Handling
|
|
258
|
+
|
|
259
|
+
`exception` means **anomaly, work paused** — not "accepted risk, closed". The
|
|
260
|
+
two are different states sharing one status today; the `DR-ID` distinguishes
|
|
261
|
+
them and only the accepted-risk form is completion-satisfying. Resolve a paused
|
|
262
|
+
item via `exception` -> `todo`.
|
|
263
|
+
|
|
264
|
+
When transitioning to `exception`:
|
|
265
|
+
|
|
266
|
+
- A DR-ID (Decision Record ID) must be recorded in the DR-ID column.
|
|
267
|
+
- A retained `CR-*` does not satisfy this: it records the approved reopen, not the anomaly. Add the `DR-*` alongside it (`DR-NNNN, CR-YYYYMMDD-NNNN`).
|
|
268
|
+
- If the DR-ID column is empty, or holds `CR-*` references only, emit error: `"exception status requires DR-ID in DR-ID column"`.
|
|
269
|
+
|
|
270
|
+
### Where the Decision Record is written
|
|
271
|
+
|
|
272
|
+
Write it to `.qfai/decisions/DR-<id>-<slug>.md`, beside the Change
|
|
273
|
+
Requests, using the same `DR-*` ID scheme those files declare. Do **not** write
|
|
274
|
+
`07_Decisions.md` or `09_delta.md`.
|
|
275
|
+
|
|
276
|
+
Those two are upstream SSOT (`constitution/drift-protocol.md#core-rule`) and
|
|
277
|
+
this skill carries `[DRIFT-PROTOCOL:MANDATORY]`, so a downstream write to either
|
|
278
|
+
is a protocol violation — while the `exception` transition itself is an ordinary
|
|
279
|
+
inline step of Phase Red that `TDDLIST_EXCEPTION_MISSING_DR` blocks at `error`
|
|
280
|
+
without a `DR-*`. `.qfai/decisions/` is the one home that satisfies both: the
|
|
281
|
+
protocol whitelists **creating** a record there
|
|
282
|
+
(`drift-protocol.md#allowed-exceptions-minimal-whitelist`), and the managed `.gitignore` block
|
|
283
|
+
already tracks it as a governance record.
|
|
284
|
+
|
|
285
|
+
The upstream cross-reference is a separate, later write. If the anomaly turns
|
|
286
|
+
out to change an approved obligation, that is drift: raise a Change Request per
|
|
287
|
+
`drift-protocol.md#when-drift-is-detected`, and the owner skill's rerun is what
|
|
288
|
+
records the reference in `07_Decisions.md` / `09_delta.md`. Parking the row does
|
|
289
|
+
not require that to have happened.
|
|
290
|
+
|
|
291
|
+
### Parked items and the `TDDLIST-001` waiver
|
|
292
|
+
|
|
293
|
+
Every `exception` row raises `TDDLIST_EXCEPTION_PARKED` (warning, rule
|
|
294
|
+
`TDDLIST-001`). The validator cannot read the DR to tell a paused defect from an
|
|
295
|
+
approved accepted risk, so the approval is recorded where QFAI already checks
|
|
296
|
+
approvals: a `TDDLIST-001` waiver in `.qfai/waivers.yml`.
|
|
297
|
+
|
|
298
|
+
The waiver carries `id` / `reason` / `expires` / `evidence` / `scope.paths`,
|
|
299
|
+
plus `match.dl_ids` listing the approved rows' `TDD-ID`s. `match.dl_ids` is not
|
|
300
|
+
optional — omit it and `QFAI-WAIVER-005` rejects the waiver, because a single
|
|
301
|
+
path-scoped entry would otherwise clear every parked row in the same ledger.
|
|
302
|
+
|
|
303
|
+
Without a matching waiver the warning stands, which is the intended signal.
|