Tasks: the work item's record owns its pull requests
Phase 4 of 4. Derived from the locked
design.mdandtesting-plan.md— each task's_Test:_names a row of the matrix. Revised with the design after owner review on PR #173; T1–T8 below are the rebuild's tasks (the link-record version's tasks are in this file's git history).
Task list
flowchart LR
T1["T1 registry: pullRequests[] endpoints"] --> T2["T2 router: pr_work_item"]
T1 --> T3["T3 dispatcher: match by record,<br/>deliver to endpoint, lazy spawn"]
T2 --> T3
T3 --> T4["T4 close: endpoint ends,<br/>record survives"]
T1 --> T5["T5 config: sessionPerPr<br/>schema + docs"]
T3 --> T6["T6 regression scenarios"]
T4 --> T6
T5 --> T7["T7 spec/decision/capability docs"]
T3 --> T9["T9 the two loops:<br/>pdlc-work-item-loop + pdlc-pr-loop"]
T6 --> T8["T8 verification + evidence"]
T7 --> T8
T9 --> T8[x] T1 — the registry learns endpoints
cli/the_loop/sessions/registry.py:Session.pull_requests(+endpoint_for/owns, one-level nesting and per-entry degradation infrom_dict, absent-key round-trip into_dict), and the verbsrecord_owning,session_for(ref, session_per_pr),link_pull_request,save_endpoint,close_endpoint, per-endpointtouch. Eventssession.pr_linked/session.pr_spawned/session.pr_closed/session.link_failedineventlog.EVENT_TYPES. Requirements: R1.1–R1.6, R2.3, R2.4, R4.3Test: T1 (unit), T8 (abuse cases)[x] T2 — the router names a PR's own ref
cli/the_loop/webhook/router.py:pr_work_item(event, payload), composed from the same helpersextract_work_itemsuses so the recorded ref cannot drift from the routed one. Requirements: R1.1, R1.5Test: T1 (unit)[x] T3 — the dispatcher matches by record and delivers to the endpoint
cli/the_loop/webhook/dispatcher.py:handle()matches viarecord_owning;_record_pr_bindingrecords the PR on each matched record (never on close);_endpoint_forpicks the conversation persessionPerPr;_spawn_endpointgives a recorded PR its session lazily (no graph entry — R2.9) with fallback to the record;_dispatch_onedelivers per endpoint with per-endpoint dedup and touch; respawn goes throughsave_endpoint. Poll path:delivery_statusandhas_sessionresolve through the record. Requirements: R1.1, R1.2, R2.1–R2.9Test: T2 (integration), T2b (poll path), T1 (resolver ordering)[x] T4 — a PR closing ends its endpoint, not the record The close branch: a closed object that is a recorded PR of a still-open record →
close_endpoint+ tmux teardown for that endpoint (session.pr_closed), record kept (session.kept_open). A PR with its own record still auto-closes. Requirements: R3.1, R3.2Test: T2[x] T5 —
routing.tmux.sessionPerPr, declared and documented.the-loop/cli-config.schema.json(+TmuxConfig.session_per_pr, default true) anddocs/config/cli/routing-options.md. Sensitive path (**/*schema*): flagged for the owner's sign-off on the PR. Requirements: R2.1, R2.2Test: T10 (docs parity P3/P4,validate_config.py)[x] T6 — the ticket's reproduction, as tests that fail without T3 Seven Gherkin-documented scenarios across
test_webhook_routing_integration.py/test_poller.py: the linkage-removed sequence, both recording paths, the re-link both-records case, control-command resolution, both close cases, and the poll-path pair. All seven fail against a pre-fix resolver. Requirements: R5.1, R5.2Test: T2, T2b[x] T7 — the paper trail, and the two-loop definition
decision-064rewritten (the reversal recorded, not erased); spec chain revised;docs/capabilities/webhook-triggers.mdclause + history row;docs/cli/state.mdsession-record section; decision-065, the process-graph capability doc's two-loops behaviour, and the--prsection of the graph command page. (T7 originally scoped the loops as follow-up; the owner's second review round pulled them into this PR — they are T9.) Requirements: R4.1, R4.2, and the loop's same-PR capability-docs ruleTest: T11[x] T9 — the two loops (decision-065)
pdlc.yaml→pdlc-work-item-loop.yaml(+name:, +await-inner-loopsonimplementation); newpdlc-pr-loop.yaml;Graph.name+load_graph(name=…); theawait-inner-loopshook (graph/hooks/loops.py) overpr-loops/*/graph-state.json;Runtime.state_subpath; graphlinkon_pr_spawn/on_pr_event/pr_context/on_pr_close(merge → audited force tocomplete); dispatcher wiring (endpoint spawn enters the loop, endpoint events advance it, outer loop never advanced by PR events);--prthrough CLI → core → API → the authored OpenAPI contract; P5 parity over both graphs. Requirements: R6.1–R6.6, R2.9Test: T12 (tests/test_graph_loops.py), T2 (wiring), parity P5[x] T10 — the graph assigns:
deliver-assignment(decision-065 D8)graph/hooks/assignment.py(render + push through a daemon-injected channel; skip without one; a failure never gates); on every non-terminal agent node's entry in both loops;GraphLink.assignment_sinkbinding the channel per loop;Dispatcher._deliver_assignmentresolving the record or the PR's endpoint and pasting via tmux;graph.assignment_delivered/graph.assignment_failedinEVENT_TYPES. Requirements: R6.7Test: T12[x] T8 — execute the testing plan and commit the evidence Every activity in
testing-plan.md§ Verification activities, including the seven-test negative run and the re-captured reproduction. Requirements: R5.1Test: T1, T2, T2b, T8, T10, T11