Skip to content

Testing plan: prove the gate runs, and that nothing else moved

Derived from the approved bugfix.md and design.md, before tasks.md. Authored at test-planning, completed at verification.

Test matrix

#TypeApplies?Scope / what it provesWhere it runs
T1Unityesthe exempt actions (start, context) and the gated ones (advance, clean); no graph.skipped for an exempt action; the skip record still names a gated one; declared-specDir parity across both; the pending context and its rendered blockmake test (uv run pytest cli/tests)
T2Integration (scenario)yesthe ticket's reproduction end to end against a real Dispatcher and a real graph.Runtime over the shipped pdlc-work-item-loop: a plain ticket is held at phase-selection, labelled, and asked. Gherkin-documentedmake test
T3Contract (OpenAPI / GraphQL SDL)n/a — the control-plane API is untouched: no path, schema or response shape changes, and GraphContext is not exposed through it
T4End-to-endn/a — a real run needs a GitHub repository, a gh credential and a real tmux/harness. T2 drives the real dispatcher and the real runtime against injected fakes, which is how this seam has always been proved in this repo
T5UI / visualn/a — no user-facing surface
T6Snapshotn/a — no rendered artefact changes. The two prompt templates are deliberately untouched (design § Trade-offs), so the byte-identical parity test in test_interaction.py is a guard that they stayed that way, not a test of this change
T7Performance / loadn/a — one membership test removed from a per-delivery path, and one extra graph read on a path that already builds the runtime. Nothing measurable above noise
T8Security / abuse caseyesthe authorization tail: an unauthorized the-loop execute on the freshly started graph moves nothing; an unarmed work item is still refused before the predicate is reached; a foreign checkout is still refusedmake test
T9Accessibilityn/a — no user interface
T10Migration / upgradeyesa deployment already carrying work items with spec folders is unchanged: their graphs start, advance and report exactly as before, and a started graph never reports pendingmake test
T11Manual exploratoryn/a — the reproduction is a spawn against a real dispatcher; mechanised as T2, which is stricter (it asserts the graph state and the posted checklist, not the observed symptom)
T12Static analysis (lint + types)yesruff, pyright, markdownlint over the changed modules and docsmake lint

Scenarios & requirement trace

RowRequirement(s)Scenario / case
T1R1.1test_a_work_item_with_no_spec_directory_is_still_started — the start reaches the runtime
T1R1.1test_a_start_with_no_spec_directory_records_no_skip — neither context nor start records graph.skipped
T1R1.4test_an_advance_still_requires_the_spec_directory — the runtime is never reached
T1R1.4test_a_skipped_work_item_is_recorded_in_the_event_log / test_the_skip_record_names_the_action_that_was_refused — the record survives, naming advance
T1R1.4test_link_a_work_item_with_no_spec_directory_is_a_no_op (issue-186's, unchanged) — clean still skips
T1R1.5test_the_gate_reads_the_same_directory_the_runtime_will — the stale docs/specs does not satisfy the gated action, and the exempt one builds its runtime on the declared specs
T1R2.1, R2.3test_a_fresh_item_reports_the_node_it_is_about_to_stand_on — the start node, pending, and not a human gate
T1R1.2test_a_repository_is_adopted_even_when_its_graph_is_skipped — adoption still precedes the start, and graph-state.json now lands
T2R1.1, R1.2Scenario: A work item minted as a plain ticket starts at the human gate — pointer at phase-selection, loop:phase-selection set, one checklist posted asking for the-loop execute, no graph.skipped
T2R2.1, R2.2, R2.5Scenario: The prompt is rendered before the graph is entered — the block names the node, says NOT ENTERED YET, names the human gate, and carries no claim line
T8R1.3Scenario: Starting the graph is not the same as walking it — an unauthorized the-loop execute leaves the pointer at phase-selection
T8R1.6the existing refusal suite in test_graphlink.py (disabled link, non-GitHub ref, unstarted item, foreign checkout, escaping specDir) — all unchanged and still green
T10R2.3Scenario: pending never masks a work item in flight — a started graph reports its real node and a status that is not pending
T10R2.6test_a_disabled_link_does_nothing and the foreign-checkout tests — no context, so an empty block, so an unchanged prompt
T12R4lint and type checks pass over the changed modules and docs

Verification environment

  • Repositories: this repository only.
  • Services / containers: none. No tmux, no harness, no network: the graph's outbound integration is replaced at the_loop.graph.integrations.resolve (the seam issue-194 established), and the checkouts are real git init trees under tmp_path.
  • Fixtures & data: the existing test_graphlink*.py fixtures plus a _bare_checkout helper (a real checkout with no spec folder — the shape of the bug) and a _FakeGitHub recording the label and the checklist.
  • Credentials: none.
  • Bring-up: make test · Tear-down: none (pytest tmp_path).
  • If bring-up fails: record it under Verification results, leave the dependent activities unticked, and escalate.

Evidence plan

RowEvidencePath under evidence/
T1, T2, T8, T10red run — the new and rewritten tests, run before any production code changedred.md
T1, T2, T8, T10green run — full suite summary and the per-file runsunit-and-integration.md
T12make lint / type-check outputlint-and-typecheck.md
security review record (checklist per reference/security.md)security-review.md

Verification activities

  • [x] T1 — uv run pytest -q cli/tests/test_graphlink.py cli/tests/test_graph_drive.py
  • [x] T2, T8, T10 — uv run pytest -q cli/tests/test_graphlink_integration.py cli/tests/test_harness_config_scaffold_integration.py
  • [x] T12 — make lint (ruff check, ruff format --check, markdownlint-cli2) and make typecheck
  • [x] Full suite — make test
  • [x] Red run captured before the fix — evidence/red.md
  • [x] Security review — the checklist in reference/security.md, against the diff

Verification results

ActivityCommand / procedureOutcomeEvidence
Red runthe 8 new/rewritten tests, run with the production change stashed8 failed, 79 passed — every one of them failing on the behaviour, not on a missing symbolevidence/red.md
T1pytest -q cli/tests/test_graphlink.py cli/tests/test_graph_drive.py63 passedevidence/unit-and-integration.md
T2, T8, T10pytest -q cli/tests/test_graphlink_integration.py cli/tests/test_harness_config_scaffold_integration.py24 passedevidence/unit-and-integration.md
Full suitemake test2477 passed, 1 skippedevidence/unit-and-integration.md
T12uv run ruff check, uv run ruff format --check, uv run pyright cli, markdownlint-cli2cleanevidence/lint-and-typecheck.md
Security reviewchecklist (reference/security.md), effective risk tier 3pass, no unresolved findingsevidence/security-review.md

Every planned activity ran. Nothing was replanned, and nothing is left unticked.

Released under the MIT License.