Skip to content

Testing plan: a closed poll-only work item leaves the board by itself

Derived from requirements.md and design.md, before tasks.md. Authored at test-planning; the results section is filled at verification.

This file is executable content. Commands below are what the agent runs; credentials appear by reference only.

Test matrix

#TypeApplies?Scope / what it provesWhere it runs
T1Unit (service)yesPollState.absent_since (the later timestamp; "" for none or unparsable) and note_closure_check (written through at once); _ledger_candidates (a poll-only record absent for the window is asked once; one seen this window is not; one dated by a check this window is not; a listed one, a stamped one, one beside a session record, one beside control / graph / collaborators are not in the set; an unparsable timestamp is due; longest-absent first; the cap admits twenty and leaves the rest); the outcomes (still open → closureCheckedAt written, no close; ProviderErrorclosureCheckedAt written, error recorded; closed → poll.closure_detected, the close event, poll forgotten, closures counted, and the record not asked again); the counter on the summary and on poll.cycle; the unowned and degraded skips do not consume the cap; the tracked set is asked with no window and no capuv run --project cli python -m pytest -q cli/tests/test_poller.py -k "ledger or poll_only or closure_check or absent_since"
T2Unit (dashboard)n/a — no dashboard change: the record a closure leaves behind (ended only) is the shape issue-329 already renders
T3Integration (scenario)yesthrough poll_once and the gh double: a labelled issue by an unlisted author is listed once (a poll-only record, no session), leaves the listing, is asked once after the window and dated as still open, is not asked again inside the window, and once closed upstream and due again is stamped ended, source poll, with its ledger forgottenuv run --project cli python -m pytest -q cli/tests/test_poller_integration.py -k ledger
T4Contract (OpenAPI / GraphQL SDL)n/a — no route changes
T5End-to-endn/a — the poller and the dispatcher's close path are exercised together by T3 over the real Dispatcher
T6UI / visualn/a — no new element
T7Snapshotn/a — assertions on record contents, events and counters
T8Performance / loadn/a — the bound is the design: at most twenty questions per provider per cycle, one per record per window; T1 asserts the cap and the window rather than timing them
T9Security / abuse caseyesone negative test per abuse case A1–A4 (requirements.md § Security considerations)uv run --project cli python -m pytest -q cli/tests/test_poller.py cli/tests/test_poller_integration.py -k "ledger or poll_only or closure_check or absent_since"
T10Accessibilityn/a — no new element
T11Migration / upgradeyesa ledger without closureCheckedAt (every 13.7.1 record) is due once lastPolledAt is a window old, and otherwise not; poll.cycle without the counter is byte-identical to today's; both event descriptions are in the cataloguv run --project cli python -m pytest -q cli/tests/test_eventlog.py cli/tests/test_docs_parity.py
T12Manual exploratoryn/a — no GitHub deployment is reachable from this session; the reviewer's walk-through is the PR briefing's "what to check"
T13Lint / format / typecheck / config validation / full suiteyesthe repository's own gates, as pre-commit and CI run themmake check
T14Security review (gate)yesthe-loop checklist against A1–A4, recorded as evidence; tier 3 needs no human sign-off (humanSignOffMinTier: 4)evidence/security-review.md

Scenarios & requirement trace

RowRequirement(s)Scenario / case
T1R1.2, R1.5test_poller.py::test_poll_state_absent_since_is_the_later_timestamp, test_poll_state_note_closure_check_writes_through
T1R1.1, R1.2test_a_closed_ledger_only_item_is_forgotten_and_never_asked_again, test_a_poll_only_record_seen_this_window_is_not_asked, test_a_poll_only_record_checked_this_window_is_not_asked, test_a_poll_only_record_with_an_unparsable_timestamp_is_due
T1R1.1, R2.3test_a_listed_poll_only_record_is_not_asked, test_a_stamped_poll_only_record_is_not_asked, test_a_poll_only_record_beside_a_session_record_is_tracked_not_ledger_only, test_a_record_with_poll_beside_another_section_is_asked_without_a_window
T1R1.3, R1.7test_ledger_only_records_are_asked_longest_absent_first_up_to_the_cap, test_unowned_and_degraded_ledger_only_records_do_not_spend_the_cap
T1R1.4test_a_closed_ledger_only_item_is_forgotten_and_never_asked_again (the unit double stamps nothing; the stamp is T3's assertion)
T1R1.5test_a_still_open_ledger_only_item_is_dated_not_closed, test_an_unanswerable_ledger_only_item_is_dated_not_retried_next_cycle
T1R1.6test_the_cycle_counts_ledger_checks
T1R1.7, R2.1, R2.2the existing issue-94 / issue-159 / issue-315 / issue-329 reconciliation tests, unchanged and green (test_a_poll_only_record_is_not_reconciled is the one rewritten, into the pair R2.2 names)
T3R1.1–R1.5test_poller_integration.py::test_a_closed_ledger_only_item_is_stamped_after_the_window
T9A1test_ledger_only_records_are_asked_longest_absent_first_up_to_the_cap, test_an_unanswerable_ledger_only_item_is_dated_not_retried_next_cycle
T9A2test_a_poll_only_record_with_a_future_timestamp_is_not_asked
T9A3test_a_still_open_ledger_only_item_is_dated_not_closed
T9A4test_a_closed_ledger_only_item_is_stamped_after_the_window (the record carries ended, not nothing)
T11R1.6, R3.3test_eventlog.py::test_every_emitted_event_type_is_documented, test_docs_parity.py

Verification environment

  • Repositories: this repo only.
  • Services / containers: none. GitHub is faked at its injection points (FakeProvider, the gh runner double).
  • Fixtures & data: temp directories per test; timestamps written directly into the ledger (2020-… for absent for the window, _utcnow() for seen this window).
  • Credentials: none.
  • Bring-up: uv sync · Tear-down: none.
  • If bring-up fails: record it under Verification results and escalate.

Evidence plan

RowEvidencePath under evidence/
T1, T3, T9, T11, T13command, counts, duration, raw tail of the output; red → green per taskverification.md
T14the abuse-case table with verdicts and the tests that close eachsecurity-review.md

Verification activities

  • [x] T1 — the poller unit selection
  • [x] T3 — the integration scenario
  • [x] T9 — the abuse-case selection
  • [x] T11 — test_eventlog.py, test_docs_parity.py
  • [x] T13 — make check
  • [x] T14 — evidence/security-review.md

Verification results

ActivityCommand / procedureOutcomeEvidence
T1uv run --project cli python -m pytest -q cli/tests/test_poller.py -k "ledger or poll_only or closure_check or absent_since"pass — 21 passed (17 new, one rewritten; the selection also matches four pre-existing ledger tests)evidence/verification.md
T3uv run --project cli python -m pytest -q cli/tests/test_poller_integration.py -k ledgerpass — 3 passed (1 new scenario, red first)evidence/verification.md
T9… -k "ledger or poll_only or closure_check or absent_since" over both filespass — 24 passed (A1–A4)evidence/verification.md, evidence/security-review.md
T11uv run --project cli python -m pytest -q cli/tests/test_eventlog.py cli/tests/test_docs_parity.pypass — 19 passedevidence/verification.md
T13make checkpass — see the evidence for the countsevidence/verification.md
T14the-loop checklist over A1–A4pass; no human sign-off at tier 3evidence/security-review.md

Not executed: none.

Review comments

Appended by the-loop's record-feedback hook when a human gate approves with comments (issue-109). Append-only and attributed: an approval never silently discards a reviewer's suggestions, and the feedback travels with the document it concerns rather than living in a side-channel tracker.

Released under the MIT License.