Skip to content

Testing plan: a first-class PR review workflow

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

Test matrix

#TypeApplies?Scope / what it provesWhere it runs
T1Unityesthe graph compiles with the specified shape and gates nothing; parse_brief's accepted and refused shapes; post-review-brief's idempotence and fast path; classify-review-brief's waiting / briefed / short-circuit; the review keyword's parsing, arming and spawn-arming; resolve_outer_loop and LOOP_FOR_CONTROL_COMMAND extended fail-closeduv run --project cli pytest cli/tests/test_graph_review.py
T2Integration (scenario)yesthe whole walk against the stub GitHub integration — review-brief → (briefed) → review → follow-up → (more-work) → review → follow-up → (done) → complete — with Gherkin docstrings; PR-first targeting through the dispatcher (the-loop review on a PR with a linked issue binds to the PR)uv run --project cli pytest cli/tests/test_graph_review.py -k "Walk or target"
T3Contract (OpenAPI / GraphQL SDL)yesthe control-plane contract is unchanged by this work item — no route, request or response shape is added; test_api_contract_parity proves ituv run --project cli pytest cli/tests/test_api_contract_parity.py
T4End-to-endn/a — the e2e harness (cli/tests/test_pdlc_e2e/) drives the outer loop's phase chain against a mocked agent; the review loop has no phase chain, and T2 already exercises every node and edge it has
T5UI / visualyesthe Sessions screen renders a pdlc-review-loop item treeless, like the other guest/ad-hoc loopscd ui && bun run test
T6Snapshotn/a — no snapshot-tested output in this change
T7Performance / loadn/a — one more YAML compiled at load, one more tuple membership test per resolution; no hot path
T8Security / abuse caseyesone negative test per abuse case in requirements.md §Security considerations: unauthorized arming, two-command refusal, unauthorized brief, self-authored brief/"done", invented loop name, unauthorized reply at the follow-up gateuv run --project cli pytest cli/tests/test_graph_review.py -k "unauthorized or refused or invented or self_authored or cannot or empty_allowlist or prose"
T9Accessibilityn/a — no new UI surface beyond one existing list rendering path
T10Migration / upgradeyesa pre-issue-279 graph-state.json still resolves exactly as before; the two generalized adoption call sites (GUEST_LOOPS) are behaviour-preserving for the existing loops; the full suite proves nothing else moveduv run --project cli pytest cli/tests/test_graph_contribution.py cli/tests/test_graph_adhoc.py cli/tests/test_graphlink.py cli/tests/test_core_graphs.py, then uv run --project cli pytest -q cli
T11Manual exploratoryn/a — every surface is a library call or a config leaf, and the parity tests cover the docs/schema pairing mechanically

Scenarios & requirement trace

RowRequirement(s)Scenario / case
T1R1.1–R1.6the shipped graph compiles; four walkable nodes plus two terminals; no produces; no validate-artifacts; no skipSets; no skippable; existing phase vocabulary only; a repo-supplied override is warned about
T1R2.1, R2.3review is in COMMANDS, SPAWN_COMMANDS and the arming set; DEFAULT_KEYWORDS[review] == "the-loop review"; the keyword is configurable and disablable
T1R2.4resolve_outer_loop accepts pdlc-review-loop and still returns "" for an invented name, for pdlc-pr-loop, and for the default
T1R4.2–R4.6parse_brief accepts one/two/three filled sections and refuses an empty form; post-review-brief skips when a brief or its marker already exists; classify-review-brief waits on silence, freezes the newest brief with provenance, and short-circuits once decided
T1R8.1–R8.5a work-item review's template asks for (and pre-fills) the pull requests in scope, deduped across the pr-loops/ state and the provider's links; nothing detected still asks; a PR thread (and an unknown kind) is not asked; stated entries freeze as composed refs with junk dropped; a PR list alone is not a brief
T2R4.1, R5.1–R5.3, R6.1Scenario: a review walks brief → review → follow-up rounds → complete on the reviewer's replies
T2R3.1–R3.2Scenario: the-loop review on a pull request binds the review to the pull request itself (linked issue present) and the plain-issue fallback
T2R2.5Scenario: the core verbs address a review item through its recorded loop
T3R1.1the control-plane API contract is unchanged
T5design §10a review item renders treeless on the Sessions screen
T8abuse 1Scenario: an unauthorized "the-loop review" arms nothing
T8abuse 2Scenario: a comment carrying two control keywords is refused
T8abuse 3Scenario: an unauthorized brief leaves the brief gate waiting
T8abuse 4Scenario: the harness can neither brief nor end its own review
T8abuse 5Scenario: an invented loop name in agent-writable state selects no graph
T8abuse 6Scenario: an unauthorized reply leaves the follow-up gate open
T10R7.1, R7.3the contribution, ad-hoc and outer loops behave identically after the GUEST_LOOPS generalization

Verification environment

  • Repositories: this repository only.
  • Services / containers: none. Every test is an in-process filesystem test against tmp_path; the GitHub integration is the suite's existing stub.
  • Fixtures & data: cli/tests/conftest.py and the fakes in test_graph_review.py (mirrored from test_graph_adhoc.py).
  • Credentials: none — no test touches the network.
  • Bring-up: uv sync (and cd ui && bun install for T5) · 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, T8the new suite's run output (counts, duration, scenario names)unit-and-integration.md
T3, T10the full suite's run output, proving nothing else movedfull-suite.md
T5the UI suite's run outputui-suite.md
ruff, pyright, markdownlint and validate_config outputlint-and-types.md

Verification activities

Run from cli/, so pytest's configured testpaths apply.

  • [x] T1 — uv run pytest tests/test_graph_review.py
  • [x] T2 — uv run pytest tests/test_graph_review.py -k "Walk or target"
  • [x] T3 — uv run pytest tests/test_api_contract_parity.py
  • [x] T5 — cd ui && bun run test
  • [x] T8 — uv run pytest tests/test_graph_review.py -k "unauthorized or refused or invented or self_authored or cannot or empty_allowlist or prose"
  • [x] T10 — uv run pytest tests/test_graph_contribution.py tests/test_graph_adhoc.py tests/test_graphlink.py tests/test_core_graphs.py, plus uv run pytest for the whole suite and its parity tests
  • [x] lint / types — uv run ruff check cli hooks, uv run ruff format --check cli hooks, uv run pyright cli, markdownlint-cli2, scripts/validate_config.py

Verification results

ActivityCommand / procedureOutcomeEvidence
T1 + T2 + T8uv run pytest tests/test_graph_review.py (from cli/)pass — 62 passed (55 at first verification, +1 after the security-review fix, +6 after the work-item-level reviews of the owner's PR #280 ruling)evidence/unit-and-integration.md
T2uv run pytest tests/test_graph_review.py -k "Walk or target"pass — 5 passedevidence/unit-and-integration.md
T3uv run pytest tests/test_api_contract_parity.pypass — 2 passedevidence/full-suite.md
T5cd ui && bun run lint && bun run test && bun run buildpass — 157 passed (12 files), lint and build cleanevidence/ui-suite.md
T8uv run pytest tests/test_graph_review.py -k "unauthorized or refused or invented or self_authored or cannot or empty_allowlist or prose"pass — 9 passedevidence/unit-and-integration.md
T10uv run pytest tests/test_graph_contribution.py tests/test_graph_adhoc.py tests/test_graphlink.py tests/test_core_graphs.pypass — 162 passedevidence/full-suite.md
whole suiteuv run pytest (from cli/)pass — 2667 passed, 1 skipped (+67 over the 2600 issue-277 recorded on main at b6bfda1)evidence/full-suite.md
lint / typesuv run ruff check cli hooks · uv run ruff format --check cli hooks · uv run pyright cli · markdownlint-cli2 (870 files, 0 errors) · scripts/validate_config.py (7 VALID)passevidence/lint-and-types.md

Not executed: none.

Review comments

Released under the MIT License.