Skip to content

Testing plan: six inert gates, and the assertion that will keep them honest

Phase 3 of 4. Derived from the locked requirements.md and design.md, before tasks.md. Ticket: issue #167.

This file is executable content. It names commands an agent will run, so review it like code. No credentials are involved — this work item touches no network and no secret store.

Test matrix

The proof this work item needs is a negative one: the six gates must stop skipping, and must not be able to start skipping again. Three rows carry that; the rest are n/a for a pure library-and-YAML change with no runtime surface.

#TypeApplies?Scope / what it provesWhere it runs
T1Unityesvalidate-artifacts with validates:: an absent target blocks, a satisfied one passes, alternation and ambiguity behave as they do for produces, and every pre-existing branch (missing-artifact message, optional: skip, no-checks skip) is unchangeduv run --directory cli pytest tests/test_graph_hooks.py
T2Integration (scenario)yesthe real security-review node, driven through run_chain against a temp spec directory: blocks without its execution-log section, passes with it. Gherkin-documenteduv run --directory cli pytest tests/test_graph_verification_integration.py
T3Contract (OpenAPI / GraphQL SDL)n/a — the change adds no API surface; docs/api-specs/openapi is untouched
T4End-to-endn/a — the CLI end-to-end path is the-loop graph, covered by T2's chain-level drive; no shell-level behaviour changes
T5UI / visualn/a — the-loop has no product UI (design.uiArtifacts.format: html, unused here)
T6Snapshotn/a — no rendered output; the one message string that must stay stable is pinned by an equality assertion in T1 instead
T7Performance / loadn/a — two extra Path.is_file() calls per node boundary
T8Security / abuse caseyesthe fail-closed branch: a section gate that resolves no target blocks and is not retriable; and the parity assertion (P5) that fails when a node gates sections with nothing to read them fromuv run --directory cli pytest tests/test_graph_hooks.py tests/test_graph_parity.py
T9Accessibilityn/a — no user-facing surface
T10Migration / upgraden/a — no persisted state changes. validates is a hook parameter inside the shipped graph, so no run state, manifest entry or project file is migrated
T11Manual exploratoryyesthe ticket's own reproduction script prints nothing after the change — the defect's own definition, re-runuv run --directory cli python - <<'PY' … (§ Verification results)

Scenarios & requirement trace

RowRequirement(s)Scenario / case
T1R1.1, R1.2, R1.3, R1.4, R1.5a validated target that is absent blocks; present and satisfied passes; produces + validates findings arrive in one result; a.md|b.md alternation; no checks and no target still skips
T1R2.3, R2.4optional: still skips; the missing-artifact message is byte-identical
T2R3.1, R3.2Scenario: the security-review gate blocks a work item whose execution log has no security section
T2R3.1, R3.2Scenario: the security-review gate passes once the section is written
T8R2.1, R2.2a section gate with no resolvable target blocks, not retriable
T8R4.1, R4.2, R4.3P5 over the shipped graph: every section gate resolves a target; every validated name is manifest-tracked; every demanded section exists in the bundled template
T11R3.3, R5.1–R5.3the reproduction script is silent; the template offers ## Capability docs; the capability doc and decision record are present

Verification environment

Nothing beyond this repository and its own toolchain — the whole change is a Python package plus checked-in markdown and YAML.

  • Repositories: this repo only.
  • Services / containers: none.
  • Fixtures & data: none. The unit tests build spec directories under pytest's tmp_path; the parity test reads the shipped pdlc.yaml, .the-loop/manifest.yaml and skills/the-loop/templates/ from the checkout.
  • Credentials: none. This work item reads and writes no secret, and makes no network call.
  • Bring-up: uv sync --directory cli · Tear-down: none.
  • If bring-up fails: record it under Verification results, leave the dependent activities unticked, and escalate.

Evidence plan

RowEvidencePath under evidence/
T1, T2, T8full pytest run — counts, duration, the new tests namedtests.md
T8ruff + pyright output over the changed fileslint-and-types.md
T11the ticket's reproduction script, before and after — the before output is the defect, the after output is its absencereproduction.md

No capture in this work item can contain a token, a cookie, personal data or an internal hostname: the outputs are pytest summaries, linter findings and a list of node ids. They are committed as markdown, as the rule requires.

Verification activities

  • [x] T1 — uv run --directory cli pytest tests/test_graph_hooks.py -q
  • [x] T2 — uv run --directory cli pytest tests/test_graph_review_chain_integration.py -q
  • [x] T8 — uv run --directory cli pytest tests/test_graph_parity.py -q
  • [x] T8 — P5 fails against the unfixed graph and the unfixed template (the check that the checks check something)
  • [x] T11 — the behavioural reproduction: every review node blocks where it used to skip
  • [x] Full suite — uv run --directory cli pytest -q
  • [x] Lint + types — ruff check, ruff format --check, pyright, markdownlint

Verification results

Every activity ran; nothing was left unexecuted. One activity's shape changed during execution and is recorded as it happened rather than as it was planned — see T11.

ActivityCommand / procedureOutcomeEvidence
T1pytest tests/test_graph_hooks.py -qpass — 42 tests, 12 of them newevidence/tests.md
T2pytest tests/test_graph_review_chain_integration.py -qpass — 24: four Gherkin scenarios × all six review nodesevidence/tests.md
T8pytest tests/test_graph_parity.py -qpass — 8, including P5a/P5b/P5cevidence/tests.md
T8P5a against the pre-fix graph (the six validates: lines removed)fails, naming all six nodesevidence/tests.md
T8P5c against the pre-fix template (## Capability docs removed)fails, naming the section and the nodeevidence/tests.md
T11every review node's exit chain driven over the shipped graph against an empty spec folderall six block (required artifact is missing … execution-log.md); all six were skip beforeevidence/reproduction.md
Full suitepytest -qpass — 1380 passed, 1 skipped (pre-existing, unrelated)evidence/tests.md
Lint + typesruff check ., ruff format --check ., pyright, markdownlint-cli2cleanevidence/lint-and-types.md

Planned differently from how it ran: T11 was planned as "the ticket's reproduction script prints nothing". It does not — that script tests for sections: without produces:, a question that predates the validates: vocabulary, so it still prints six lines after the fix. Rather than quietly swapping the activity, the evidence file records the script's stale output, the corrected structural check (which is what P5a asserts in CI), and the behavioural check that is the real proof: what each node's chain actually returns. The integration file test_graph_verification_integration.py named in the plan was likewise the wrong home — the scenarios landed in a new test_graph_review_chain_integration.py, since they are the review chain's, not issue-163's.

Not executed: none.

Review comments

Appended by the-loop's record-feedback hook when a human gate approves with comments. Append-only and attributed.

Released under the MIT License.