Tasks: the item's author gates spawning, and nothing else
Derived from the approved
bugfix.md,design.mdandtesting-plan.md. A DAG, not a list: tasks with no edge between them are independent. Each_Test:_names a row of the testing plan.
flowchart TD
T1["T1 — spawn_authorized:<br/>item author OR recorded arming"] --> T2["T2 — first sight always reads<br/>the thread for control commands"]
T1 --> T3["T3 — forwarding stops asking<br/>who opened the item"]
T1 --> T4["T4 — poll.unauthorized says<br/>what is withheld, and stops when it isn't"]
T2 --> T6["T6 — unit + abuse tests"]
T3 --> T6
T4 --> T6
T5["T5 — spawn prompt frames the<br/>work item as untrusted (both copies)"] --> T7["T7 — template parity tests"]
T6 --> T8["T8 — integration scenario<br/>through the real dispatcher"]
T7 --> T9["T9 — docs: poll page, capability doc,<br/>decision-074"]
T8 --> T9
T9 --> T10["T10 — verification:<br/>run the plan, commit evidence"]Tasks
[x] T1 —
_process_itemcomputesspawn_authorized.item_authorized or self.control_store.start_requested(ref), replacingitem_authorizedat both presence seams (first sight and known item). No other behaviour moves. Requirements: R2.1, R2.2, R2.3, R2.4 · Test: T1, T4[x] T2 — first sight always asks which control comments are pending. Drop the
if item_authorized else set()conditional on_pending_control_ids. The method's own guards (authorized comment author, not self-marked, unambiguous, no existing control record) are the whole gate — none of them changes. Requirements: R1.3 · Test: T1[x] T3 — forwarding stops asking who opened the item. Remove the
if item_authorized:wrapper around the candidate loop. Candidates are already filtered by their own author and by the self-marker. Requirements: R1.1, R1.2 · Test: T1, T4[x] T4 — the withheld-spawn warning tells the truth. Emit
poll.unauthorized(and log) only when the item's author being unauthorized actually withholds a spawn — i.e. whenspawn_authorizedis false — and name the remedy in the log line. Requirements: R3.1, R3.2 · Test: T1[x] T5 — the spawn prompt frames the work item itself as untrusted. One constant paragraph, added identically to
skills/the-loop/templates/webhook-autoexecute-prompt.mdandDEFAULT_SPAWN_TEMPLATEinwebhook/dispatcher.py, above$payload_excerpt. Requirements: R4.1, R4.2, R4.3 · Test: T6[x] T6 — unit and abuse-case tests. In
cli/tests/test_poller.py: the R1/R2/R3 cases from the trace table, including the four abuse cases. Rewritetest_first_sight_ignores_the_thread_of_an_unauthorized_items_author(it asserted the bug) and keeptest_poller_does_not_spawn_for_unauthorized_item_author(it asserts R2.1, which does not change). Requirements: R1.1–R1.5, R2.1–R2.5, R3.1, R3.2 · Test: T1, T4[x] T7 — template parity holds.
cli/tests/test_interaction.py— assert the new paragraph exists in both copies and still precedes the untrusted payload block. Requirements: R4.1, R4.2, R4.3 · Test: T6[x] T8 — integration scenario. In
cli/tests/test_poller_integration.py: a Gherkin-docstringed scenario driving a realDispatcher— a maintainer'sthe-loop contributeon a stranger's item records the command and spawns; the same command from the stranger does neither. Requirements: R1.1, R1.2, R1.3, R2.2 · Test: T2[x] T9 — documentation.
docs/cli/commands/poll.md(the Guards block states the old rule verbatim),docs/capabilities/webhook-triggers.md(behaviour + history row), anddocs/decisions/decision-074.mdwith its index row. Requirements: all · Test: T13[x] T10 — verification. Execute the testing plan, fill in its results table, commit the evidence. Requirements: all · Test: T5, T13
Unplanned work, recorded
eventlog.EVENT_TYPES["poll.unauthorized"]reworded. T4 changed when the event fires; its catalogue description said the item "was ignored", which is now the opposite of what happens to its comments. The catalogue is the-loop's own documentation of its event vocabulary (the-loop events --types), so leaving it would have shipped a wrong answer to an operator's question.- Two more documentation surfaces than T9 named:
docs/cli/concepts.md§ Guards anddocs/config/cli/routing-options.md§authorizedUsersboth stated the old rule in their own words, andskills/the-loop/templates/cli-config.yaml's security comment did too. Found by grepping for the claim rather than for the filename. uv.lockversion line.9.5.0→9.5.1: the 9.5.1 release commit bumped the package without re-locking, so the firstuv runin this branch regenerated it. Not this work item's change, but leaving a dirty lock in the tree would push the drift onto the next work item.