Skip to content

Testing plan: per-work-item collaborators

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

Test matrix

#TypeApplies?Scope / what it provesWhere it runs
T1Unit — the login grammaryesnormalize_login accepts GitHub's shape and rejects everything else (leading/trailing/double hyphen, 40 chars, dots, slashes, spaces, paths, argv fragments, empty); parse_logins takes the @login run and stops at prosecd cli && uv run pytest tests/test_collaborators.py
T2Unit — the storeyesadd/remove/list round-trip; idempotent add and no-op remove report themselves; case-insensitive identity; provenance fields recorded; permits is per-ref and answers "any of these refs"; clear; the section survives beside control/poll/graph and a record with only a roster is not deletedcd cli && uv run pytest tests/test_collaborators.py
T3Unit — the parseryesboth keywords parse with their logins; several logins and a repeated keyword; a keyword with no login yields the command and no subjects; ambiguity with an existing command; disabling by empty keyword; whole-token boundaries (the-loop add-collaborators, xthe-loop add-collaborator)cd cli && uv run pytest tests/test_control.py
T4Unit — the dispatcher's control seamyesan authorized user's add-collaborator writes the roster, emits control.command naming the login and settles the delivery; remove-collaborator revokes; a body with no login is refused missing-collaborator and writes nothing; no ControlStore record is written by eithercd cli && uv run pytest tests/test_dispatcher_control.py
T5Unit — the dispatcher's spawn seamyesa named actor outside authorizedUsers cannot spawn (collaborator-no-spawn, settled, not retried); an actor-less presence event still spawns on a recorded start (decision-074 regression)cd cli && uv run pytest tests/test_dispatcher_spawn.py
T6Integration — webhook ingressyesa collaborator's comment on their work item is delivered to that item's session; the same comment from a stranger is dropped; the same collaborator on another work item is dropped; a collaborator's the-loop stop is refused, not executed and not forwardedcd cli && uv run pytest tests/test_webhook_routing_integration.py
T7Integration — poll ingressyesthe poller forwards a collaborator's comment; it does not arm a spawn from one; a collaborator's control keyword is not treated as a pending commandcd cli && uv run pytest tests/test_poller_integration.py
T8Regression — the human gatesyesclassify-feedback, classify-phase-selection, goal-definition and the review brief ignore a work-item collaborator's comment exactly as they ignore any other non-authorized author (A5)cd cli && uv run pytest tests/test_graph_review.py tests/test_graph_hooks*.py
T9Unit — lifecycleyesclosing the work item clears the roster with the control record; sessions reset drops the section; a cleared roster stops permittingcd cli && uv run pytest tests/test_collaborators.py tests/test_reset.py
T10Unit / integration — the CLIyesadd-collaborator/remove-collaborator apply the grant, post the keyword with the login carrying the self-marker, report an unchanged roster honestly, survive a failing gh without failing the grant, and exit 2 on a malformed login or refcd cli && uv run pytest tests/test_collaborators_cli.py
T11Config / schemayesboth copies of the CLI-config schema accept the two new keywords and still reject an unknown one; the shipped template and this repo's own config validate; ControlConfig.from_mapping honours a configured and an emptied keywordcd cli && uv run pytest tests/test_configschema.py tests/test_control.py
T12Contract (OpenAPI)n/a — no route, request or response shape is added or changed (R5.5, design §5)
T13Security / abuse caseyesA1 self-grant refused; A2 collaborator's control keyword refused and not forwarded; A3 injection through the login argument (T1's rejections, plus a body whose "login" is a path/argv fragment reaching nothing); A4 cross-item grant refused; A5 gates unchanged; A6 no spawn; A8 a removal takes effect on the next commentT1, T4, T5, T6, T7, T8
T14Lint / typecheck / testsyesthe commands CI runs, at the pinned versionscd cli && uv run ruff check . && uv run pyright && uv run pytest; markdownlint over the changed docs
T15UI / visualn/a — no dashboard surface is added (design §5)
T16Performancen/a — one extra JSON read per event, on the path that already reads that record for the control section

Scenarios & requirement trace

RowRequirement(s)Scenario / case
T1R4.2, A3@dana parses; @-dana, @dana-, @da--na, a 40-character login, @dana/../etc, @dan a, --flag do not
T1R4.3the-loop add-collaborator @a @b please help yields a, b and ignores the prose
T2R1.1, R1.3, R1.7a grant round-trips through the portable record with login, grantor, timestamp, surface and comment URL
T2R1.4adding twice reports "already"; removing an absent login reports "not on the roster"
T2R1.5@Dana, dana, @dana are one entry and one identity
T2R1.2, R3.7, A4permits is true for the granted ref and for an event naming it beside a linked PR; false for any other ref
T3R4.1, R4.5, R4.6both keywords; add-collaborator + stop in one body is ambiguous; "" disables
T4R4.4, R4.7, R6.1, R6.2the refusal, the settle, and an event log naming the login and nothing else from the body
T4R1.6the two verbs write no ControlStore record
T5R3.2, A6a named unauthorized actor's comment on an armed, unstarted item does not spawn
T5R3.3decision-074's authorized-start-on-an-unauthorized-author's-item still spawns
T6R3.1delivery of a collaborator's comment to the work item's session
T6R2.2, R3.4, A1, A2a collaborator's the-loop add-collaborator @self and the-loop stop are refused and not forwarded
T7R3.1, R3.3the poller forwards, and does not arm
T8R3.5, A5every human gate ignores a collaborator
T9R1.6, A8, A9closure, reset, and a revocation taking effect
T10R5.1–R5.4the CLI's four behaviours and its two exit-2 cases
T11R4.1, R4.6schema parity and keyword configuration
T14lint, types, the full suite, markdownlint

Verification results

Recorded at verification in evidence/verification.md.

Released under the MIT License.