Skip to content

<!-- Authored per the the-loop:writing skill. -->

Testing plan: the mention is the address, and three acts each end in the session ​

Derived from the approved requirements.md and design.md, before tasks.md — each task's _Test:_ names a row of the matrix below. Authored at the test-planning node and completed at the verification node. Reviewed together with the design at design-approval.

This file is executable content. It names commands an agent will run, so review it like code. Credentials appear by reference only.

Test matrix ​

#TypeApplies?Scope / what it provesWhere it runs
T1Unityesverbs.parse_verb / compose_keyword / strip_mention; the §1 input table as a pure decision; the snapshot renderer, its link composition and its cap; parse_subjects; the listen guard and CollaboratorRecord.from_dict; mirror_body for the two kinds; the two delivery frames; records' envelope filtercd cli && uv run python -m pytest -q tests/test_channels_mentions.py tests/test_channels_verbs.py tests/test_collaborators.py tests/test_workchannels.py tests/test_channels.py
T2Integration (scenario)yesthe listener's handle and handle_socket_event end to end with the fake Slack client and the fake ledger writer, Gherkin-documented per requirement (scenarios below); the CLI forms (add-collaborator --slack, add-channel --listen, channels records) through their commandscd cli && uv run python -m pytest -q tests/test_channels_mentions_integration.py tests/test_channels_shortcuts_integration.py tests/test_collaborators_cli.py tests/test_workchannels_cli.py tests/test_channels_records_integration.py
T3Contract (OpenAPI / GraphQL SDL)n/a — no HTTP API changes; the control plane's OpenAPI under docs/api-specs/openapi is untouched
T4End-to-endn/a — a live Slack workspace cannot run in CI; T11 covers the real-workspace path by hand, and T2 drives the same code with Slack's payload shapes recorded as fixtures
T5UI / visualn/a — no product UI; the decision modal is a Block Kit JSON literal pinned by T6, and its rendering is Slack's
T6Snapshotyesthe decision modal view, the shipped manifest (scopes, event, shortcuts) and the guide's copy of it, the help text, the channels status lines for mentions: / shortcuts:cd cli && uv run python -m pytest -q tests/test_channels_commands.py tests/test_channels_dm.py tests/test_channels_shortcuts.py
T7Performance / loadn/a — the mention gate adds no call and drops before any lookup; a snapshot is one paged conversations.replies bounded by the cap
T8Security / abuse caseyesone negative test per abuse case A1–A12, named in design.md § Security designcd cli && uv run python -m pytest -q tests/test_channels_mentions_security.py
T9Accessibilityn/a — no rendered surface of the-loop's own; the modal's labels are plain text and Slack owns its accessibility
T10Migration / upgradeyesa declaration without listen reads as mentions; a roster entry without slack keeps working and one without either id is skipped; a channel state file without snapshots loads; an app whose granted scopes lack app_mentions:read is reported by status --probe and at connect; both schema copies stay byte-equal; the docs pins passcd cli && uv run python -m pytest -q tests/test_channels_upgrade.py tests/test_config_schema_parity.py tests/test_docs_parity.py tests/test_graph_parity.py tests/test_bus.py
T11Manual exploratoryyesagainst a real workspace: reinstall with the new manifest; a plain message in a mentions room leaves no trace; @the-loop help; @the-loop record-context in a thread → ticket record, context.md entry, thread reply, ✅; the Add as context shortcut; the decision shortcut, modal and decision-<nnn>.md; @the-loop add-collaborator slack:U… then that member adds context; --listen all on a room; a DM still works; read.mode: poll says nothing addressed can arriveprocedure in evidence/manual.md
T12Whole suite and the repository's own checksyesnothing else regressed; lint, format, types, markdownmake check (from the repository root: ruff, ruff format check, pyright, the schema validation, pytest, markdownlint)

Scenarios & requirement trace ​

RowRequirement(s)Scenario / case
T1R1.1, R3.1, R3.4parse_verb on each verb, help, a keyword's last word, prose; strip_mention removes the bot's token anywhere and leaves other mentions
T1R1.1–R1.3, R1.6, R2.2the §1 input table: (DM, message) → input; (all room, message) → input; (all room, app_mention) → duplicate; (mentions room / central / bound thread, message) → not-addressed; (…, app_mention) → input
T1R4.1, R4.5, A5, A9the snapshot renderer: names, links, the 150-message and 40,000-character caps, the scrub order, the size refusal
T1R5.1, R5.4mirror_body for decision.recorded and context.added carries the marker and the envelope and defangs keywords
T1R3.7the context and decision frames are composed from fixed words, ref, counts and URLs, and name the text untrusted
T1R7.1, A11, A12parse_subjects, CollaboratorRecord.from_dict, the listen guard
T2R1.1–R1.5Scenario: a message without the mention leaves no trace · Scenario: a mention in a mentions room is input · Scenario: a mention under the-loop's own question is input and a plain reply there is not · Scenario: a mention with a keyword composes the configured keyword · Scenario: a mentioned top-level message in the central channel is a kickoff · Scenario: a redelivered mention is processed once
T2R1.6, R1.7Scenario: a DM hears a plain message · Scenario: poll mode reads no mention-gated conversation and moves no cursor
T2R2.1–R2.4Scenario: an all room hears a plain message · Scenario: add-channel --listen all is recorded with provenance and shown by channels threads · Scenario: a collaborator cannot switch a room
T2R3.2, R3.3, R3.6, R3.7Scenario: help answers ephemerally and records nothing · Scenario: a room's opening message carries the hint · Scenario: an accepted act is acknowledged and answered with the record's link · Scenario: a refused delivery leaves the record standing
T2R4.1–R4.4, R4.6Scenario: record-context snapshots a thread onto the ticket and into the session · Scenario: record-context on a top-level message snapshots that message · Scenario: a second record-context records only what is new · Scenario: record-context without the grant is dropped as unpublishable
T2R5.1–R5.3, R5.5Scenario: record-decision lands as a marked record and a decision frame · Scenario: an empty record-decision is refused with the grammar · Scenario: a collaborator may add context but not a decision
T2R6.1–R6.5Scenario: the context shortcut is the typed mention · Scenario: the decision shortcut opens the modal and the submission is the typed mention · Scenario: a shortcut acts once per trigger
T2R7.1, R7.2, R7.4, R7.5Scenario: add-collaborator by mention writes a Slack id · Scenario: a collaborator's record names the roster's ids · Scenario: a roster on one work item widens nothing on another
T2R4.7, R5.6Scenario: channels records lists the enveloped records of a work item
T6R6.1, R1.8, R3.2, R6.6the manifest (scope, event, shortcuts) equals the guide's block; the modal view; the help text; the status lines
T8A1–A12the twelve negative tests named in design.md § Security design
T10R1.8, R2.1, R7.1, R8.1the upgrade cases in the matrix row
T11R1–R7the manual procedure, one step per requirement, in evidence/manual.md
T12R8make check green, which includes the docs pins

Verification environment ​

  • Repositories: this repository only, at the PR's head.
  • Services / containers: none for T1–T10, T12: every Slack call goes through the fake client the channel tests already inject (client_factory), every ledger write through the fake writer (post_comment), and every session delivery through the injected deliver. For T11 a Slack workspace with the app re-imported from the shipped manifest and re-installed, one public channel declared as a room, and a running the-loop start with read.mode: socket.
  • Fixtures & data: Slack payload shapes for app_mention, message_action and view_submission checked in as fixtures under cli/tests/fixtures/slack/; a pre-change declaration, roster entry and channel state file for T10.
  • Credentials: by reference only — THE_LOOP_SLACK_BOT_TOKEN and THE_LOOP_SLACK_APP_TOKEN for T11 (the names channels.slack.botTokenEnv / appTokenEnv default to), and the operator's gh login for the ledger. None is written anywhere in this spec or in evidence.
  • Bring-up: cd cli && uv sync · T11: the-loop start in a scratch config · Tear-down: the-loop stop; the scratch room and issue are deleted after the evidence is captured.
  • If bring-up fails: record it under Verification results, leave the dependent activities unticked, and escalate — do not pass the gate on an environment that never came up.

Evidence plan ​

RowEvidencePath under evidence/
T1test summary (counts, duration)unit.md
T2the-loop scenarios --glob 'cli/tests/test_*_integration.py' --format markdown table + run outputintegration.md
T6run output; the modal view JSON as committedsnapshot.md
T8run output, one section per abuse casesecurity.md
T10run output; the pre-change fixtures namedupgrade.md
T11the procedure with one section per step and its outcome; redacted screenshots of the room message, the ticket record, context.md, the modal and the decision file; an animated capture of the shortcut → modal → record flowmanual.md, ui/*.png, ui/decision-shortcut.gif
T12make check outputchecks.md

Redaction before commit: member ids, workspace URLs and channel ids in screenshots and captured payloads are masked; a capture that cannot be redacted is described in the results row instead of committed.

Verification activities ​

  • [x] T1 — cd cli && uv run python -m pytest -q tests/test_channels_mentions.py tests/test_channels_verbs.py tests/test_collaborators.py tests/test_workchannels.py tests/test_channels.py
  • [x] T2 — cd cli && uv run python -m pytest -q tests/test_channels_mentions_integration.py tests/test_channels_shortcuts_integration.py tests/test_collaborators_cli.py tests/test_workchannels_cli.py tests/test_channels_records_integration.py
  • [x] T6 — cd cli && uv run python -m pytest -q tests/test_channels_commands.py tests/test_channels_dm.py tests/test_channels_shortcuts.py
  • [x] T8 — cd cli && uv run python -m pytest -q tests/test_channels_mentions_security.py
  • [x] T10 — cd cli && uv run python -m pytest -q tests/test_channels_upgrade.py tests/test_config_schema_parity.py tests/test_docs_parity.py tests/test_graph_parity.py tests/test_bus.py
  • [ ] T11 — the manual procedure in evidence/manual.md, against the workspace named by the credentials above
  • [x] T12 — make check

Verification results ​

Executed 2026-09-19 at the PR's head, in a cloud checkout of this repository (cd cli && uv sync); T11 needs a Slack workspace and was not run here.

ActivityCommand / procedureOutcomeEvidence
T1cd cli && uv run python -m pytest -q tests/test_channels_mentions.py tests/test_channels_verbs.py tests/test_collaborators.py tests/test_workchannels.py tests/test_channels.pypass — 234 passedevidence/unit.md
T2cd cli && uv run python -m pytest -q tests/test_channels_mentions_integration.py tests/test_channels_shortcuts_integration.py tests/test_collaborators_cli.py tests/test_workchannels_cli.py tests/test_channels_records_integration.pypass — 69 passed before the self-review rounds, 88 after their tests were added; 22 Gherkin scenariosevidence/integration.md
T6cd cli && uv run python -m pytest -q tests/test_channels_commands.py tests/test_channels_dm.py tests/test_channels_shortcuts.pypass — 101 passed; the modal view committedevidence/snapshot.md
T8cd cli && uv run python -m pytest -q tests/test_channels_mentions_security.pypass — 12 passed, one per abuse caseevidence/security.md
T10cd cli && uv run python -m pytest -q tests/test_channels_upgrade.py tests/test_config_schema_parity.py tests/test_docs_parity.py tests/test_graph_parity.py tests/test_bus.pypass — 64 passedevidence/upgrade.md
T11the procedure in evidence/manual.mdnot executed — no workspace, no credentials in this environmentevidence/manual.md
T12make checkpass — ruff, format, pyright, markdownlint 0 errors; 4127 passed, 1 skipped (4149 after the three self-review rounds)evidence/checks.md

Not executed: T11 — the manual walk-through against a real workspace; the procedure is written for the owner to run, and its captures are not committed.

Review comments ​

Appended by the-loop's record-feedback hook when a human gate approves with comments (issue-109).

  • 2026-09-19 · MadaraUchiha-314 (owner) · approved — the review on PR #390 that approved the requirements said "go ahead with design and implementation", and the owner repeated the instruction to implement in the session. Recorded by the session because no daemon runs the design-approval gate here; the two refinements called out under Open questions / the PR body (a marked decision record; grants, not subscriptions) stand unless the owner objects on the PR.

Released under the MIT License.