Tasks: one top-level repositories, read by every ingress
Derived mechanically from the locked
design.mdandtesting-plan.md. No approval gate (issue-281) — it advances on shape alone. Each_Test:_names a row of the testing plan.
flowchart LR
T1["1 · repos.py moves up"] --> T2["2 · receiver bound"]
T1 --> T3["3 · poller reads it"]
T1 --> T4["4 · control surfaces"]
T1 --> T5["5 · schema + migration"]
T2 --> T6["6 · tests: receiver + abuse"]
T3 --> T7["7 · tests: poller"]
T5 --> T8["8 · tests: migration + schema"]
T1 --> T9["9 · tests: the builder"]
T4 --> T10["10 · docs, capability docs, decision-121"]
T6 --> T11["11 · verification + evidence"]
T7 --> T11
T8 --> T11
T9 --> T11
T10 --> T11Tasks
[x] 1. Move the builder to
the_loop/repos.pyand point it at the new key. Movechannels/repos.py→repos.py; drop thechannels.slackimport and the_polled_pathswalk; read the top-levelrepositories; addrepository_boundsreturningOptional[Set[str]];DeclaredRepo.sourcebecomes"repositories". Requirements: R1.1–R1.6 · Test: T1[x] 2. Bound the webhook receiver.
Router.repositories: Optional[Set[str]]; the delivery-repository check and the ref filter inroute(), above the actor guard, emittingrouting.droppedwith reasonundeclared-repository;webhook/daemon.pybuilds it fromrepository_bounds, hot-swaps it inapply(), and warns once at start when it isNone. Register the new reason ineventlog.py's catalog. Requirements: R2.1–R2.6 · Test: T2, T5, T11[x] 3. Take the poller's repositories from the top level.
PollProvider.from_source/build_providergainrepositories;GitHubPollProviderbuildsRepoSpecs from it, refuses a source still carryingrepos, and names the top-level key in its empty-set error;poller/daemon.pypassesdeclared_repositories(data). Requirements: R3.1–R3.5 · Test: T3, T5[x] 4. Point the control surfaces at the moved builder.
channels/commands.py,channels/kickoff.py,commands/channels_cmd.py; the kickoff fallback is checked against the declared set; the refusal and status wordings namerepositories. Requirements: R4.1–R4.4 · Test: T1, T13[x] 5. Break the config, and migrate it. Both schema copies: add top-level
repositories, removepolling.sources[].repos.migrations.py:CURRENT_CONFIG_VERSION = "0.8.0", the move (sources +kickoff.repo, ordered and deduplicated),needs_migration, and theassert_currentrefusal. Updateskills/the-loop/templates/cli-config.yamland.the-loop/cli-config.yaml. Requirements: R5.1–R5.6 · Test: T4, T13[x] 6. Tests — the receiver bound and its abuse cases.
test_routing.pyunit cases;test_webhook_routing_integration.pyscenarios with Gherkin docstrings. Requirements: R2.1–R2.6, A1–A4, A9 · Test: T2, T5, T11[x] 7. Tests — the poller.
test_poller.pyandtest_poll_daemon_integration.py. Requirements: R3.1–R3.5 · Test: T3, T5[x] 8. Tests — the migration and the schema.
test_cli_config.pyboth directions; the schema, manifest, docs-parity and event-catalog suites green. Requirements: R5.1–R5.6, A5 · Test: T4, T13[x] 9. Tests — the builder. New
cli/tests/test_repositories.py. Requirements: R1.1–R1.6, A6–A8 · Test: T1, T11[x] 10. Docs, capability docs and the decision record. New
docs/config/cli/repositories-options.md;polling-options.md,channels-options.md,webhook-options.mdand the config index updated; the guide's configuration page; capability docswebhook-triggers.md,channels.mdandcli.mdwith history rows;docs/decisions/decision-121.md;README.mdif it names the old key. Requirements: R1.1, R2.3, R3.1, R5.1 · Test: T13[x] 11. Verification and evidence. Run T1–T5, T11, T13, T15; write
evidence/verification.mdandevidence/security-review.md; fill the testing plan's results section; updateexecution-log.md. Requirements: all · Test: T15, T16