<!-- Authored per the the-loop:writing skill. -->
Tasks: a work item names the room it is worked in
The last spec artifact (requirements → design → testing plan → tasks). A DAG of implementation tasks derived from the approved design and testing plan.
Task list
[x] 1. The grammar and the store
cli/the_loop/workchannels.py:ChannelRef,CHANNEL_TYPESwith the Slack target validator,parse_channel_ref/parse_channel_refs/describe_refusal,CollaborationChannel,ChannelTakenErrorandCollaborationChannelStore(list,for_type,declared_by,targets,add,remove,clear)cli/the_loop/workitem.py: thecollaborationChannelssection constant, inSECTIONSand in the module's record sketch- Depends on: none
- Requirements: R1.1–R1.7, R3.4
- Test: T1, T2, T3 (red→green)
[x] 2. The section travels with the work item's life cycle
cli/the_loop/state.py: anATTRIBUTESentry classifying it as an operator ledgercli/the_loop/reset.py: the section inPIECESand in both clearing listscli/the_loop/poller/poller.py: the section in the tracked-item scandocs/cli/state.md: the section's own heading, fields and deletion note- Depends on: 1
- Requirements: R1.10, R5.2
- Test: T16, T17
[x] 3. The control vocabulary
cli/the_loop/control.py:ADD_CHANNEL/REMOVE_CHANNEL,CHANNEL_COMMANDS,ARGUMENT_COMMANDS, the per-command argument parser inparse_command, andcommand_commentspelling a channel without the@prefix- Depends on: 1
- Requirements: R1.1, R1.3
- Test: T12 (red→green)
[x] 4. The dispatcher applies it
cli/the_loop/webhook/dispatcher.py:channel_store, theCHANNEL_COMMANDSbranch,_apply_channel(refusingmissing-channel/channel-taken, settlingcontrol-executed), and clearing the section when the work item ends- Depends on: 3
- Requirements: R1.1, R1.2, R1.4–R1.7, R1.10, R3.4
- Test: T13, T14 (red→green)
[x] 5. The CLI verbs
cli/the_loop/core/workchannels.py:manage_channels/list_channels, validate-all then apply, the keyword posted back best-effortcli/the_loop/commands/workchannels_cmd.py+commands/__init__.py:add-channelandremove-channel- Depends on: 3
- Requirements: R1.9
- Test: T4 (red→green)
[x] 6. Outbound: the conversation lives in the declared room
cli/the_loop/channels/state.py:ChannelStores.declared/declared_work_item/declared_targets, anddeclaredinCONVERSATION_ORIGINScli/the_loop/channels/slack.py:home_for,_no_channel,_conversation,_say_moved,_open_thread(channel_id=…), andpost/openrouted through them- Depends on: 1
- Requirements: R2.1–R2.4
- Test: T5, T6, T11 (red→green)
[x] 7. Inbound: every message in the room is about the work item
cli/the_loop/channels/slack.py:fetch_channel_messageswith first-sight baselining, andfetch_kickoffsstanding down in a declared channelcli/the_loop/channels/inbound.py: the room lookup before the kickoff branch, the binding-wins order, the room cursor for a message that is its own root, and the declared-channel loop inpoll_once- Depends on: 6
- Requirements: R3.1–R3.3, R3.5, R3.6
- Test: T7, T8, T9, T10, T11 (red→green)
[x] 8. The gate asks the question
cli/the_loop/graph/bootstrap.py: seedportableDircli/the_loop/graph/hooks/selection.py:_declared_channels,_channel_linesin the checklist, and the confirmation's channel line- Depends on: 1
- Requirements: R4.1–R4.3
- Test: T15 (red→green)
[x] 9. Configuration and its documentation
.the-loop/cli-config.schema.json+ the packaged copy: the two keyword entries.the-loop/cli-config.yaml,skills/the-loop/templates/cli-config.yaml: the keywords with the comment that says what a declaration is and is notdocs/config/cli/routing-options.md: an option entry for eachdocs/cli/commands/add-channel.md,remove-channel.md- Depends on: 3, 5
- Requirements: R5.2
- Test: T17
[x] 10. Capability and skill documentation
docs/capabilities/channels.md,docs/capabilities/webhook-triggers.md: current behaviour plus a history rowskills/the-loop/reference/collaboration.md,reference/automation.md: the rule as an agent reads itevidence/documentation.md: what changed and why- Depends on: 9
- Requirements: R5.1
- Test: T17
[x] 12. Names, not ids — the author's review of PR #376
cli/the_loop/channels/directory.py:SlackDirectory(two cached maps, TTL, fail-closed, thefollowflag for handles),is_conversation_id/is_member_id/normalize_namecli/the_loop/workchannels.py: the Slack target accepts a name,ChannelRef.is_id/.name,resolve_channel_ref, the record'snamefield, and the store refusing to hold anything but an idcli/the_loop/webhook/dispatcher.py,core/workchannels.py: resolve at the declaration boundary, refuse what will not resolvecli/the_loop/channels/slack.py:central_channel()/directory(), every outbound read routed through the resolved id,probe_subscriptionresolving a namecli/the_loop/channels/inbound.py,channels/commands.py:_authorized, shared by every inbound surface, resolving a handle and failing closedcli/the_loop/channels/slack-app-manifest.yaml:channels:read,groups:read,users:read- the schema, both config files,
channels-options.md,routing-options.md,add-channel.md - Depends on: 1–11
- Requirements: R1.5, R1.11, R5.1–R5.6, R6.1–R6.5
- Test: T21, T22, T23, T24, T25, T26 (red→green)
[x] 11. Verification
- the full suite,
make check, and the evidence files - Depends on: 1–10
- Requirements: all
- Test: T1–T17
- the full suite,