Skip to content

Tasks: one rule for where a work item's attributes live

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. Declare the rule as data

    • cli/the_loop/state.py: an ATTRIBUTES table beside GENERATED_PATHS — one entry per top-level key of the three per-work-item files, with its kind (pointer, human decision, remote entity, operator ledger, machine handle, derived) and the file the rule allows it in
    • Depends on: none
    • Requirements: R1.1, R1.5, R6.1
    • Test: T1 — test_state_portability.py (red→green)
  • [x] 2. work-item-state.json v2

    • cli/the_loop/graph/state.py: drop session; add sessionPerPr, model, effort and pullRequests[] (ref, repository, number, url, stateDir, state, linkedAt, linkedBy); link_pr idempotent by ref, set_pr_state; a v1 file's session is ignored on load and absent on the next save; repository validated through repo_state_key
    • Depends on: 1
    • Requirements: R2.1, R2.2, R3.1, R3.3, R4.1
    • Test: T2, T3 (red→green)
  • [x] 3. session: inherit resolves through the session registry

    • cli/the_loop/graph/runtime.py: resolve_session asks the registry for the live endpoint of this state's ref (outer: the work item; inner: the PR) and falls back to fresh-with-artifacts; cli/the_loop/graphlink.py: _bind_session and on_close stop writing into the state file
    • Depends on: 2
    • Requirements: R3.1, R3.2, R3.3
    • Test: T3, T4 (red→green)
  • [x] 4. The frozen choices move into the work item's file

    • cli/the_loop/graph/hooks/selection.py: the freeze writes sessionPerPr, model and effort into WorkItemState beside skips/optIns/surface; it stops emitting frozenGraph
    • cli/the_loop/webhook/dispatcher.py: _tmux_for and _resolved_choice read the state file in the checkout the session record names, falling back to the portable graph section for a work item frozen before this change, then to the operator's defaults; _record_frozen_graph is removed
    • Depends on: 2
    • Requirements: R4.1–R4.5, abuse cases 1–2
    • Test: T9, T10 (red→green)
  • [x] 5. The portable record's sections

    • cli/the_loop/workitem.py: SECTIONS drops GRAPH, gains CHANNELS and PULL_REQUESTS; owner_of(ref) scans the pull-request maps; index.json lists one entry per work item naming its PR refs
    • cli/the_loop/control.py: record_frozen_graph removed, frozen_graph kept as the legacy reader only
    • Depends on: 4
    • Requirements: R4.2, R8.3, R10.1, R10.7
    • Test: T11 (red→green)
  • [x] 6. One portable record per work item

    • cli/the_loop/poller/poller.py: PollState takes (owner, ref)owner == ref writes poll, otherwise pullRequests[ref]; the owner is resolved before any write (session registry → portable maps → the router's linkage on the listed item), and a PR that resolves to nothing is a work item of its own
    • cli/the_loop/webhook/dispatcher.py: a PR with an owner records its upstream state through GraphLink and drops its nested ledger; no ended on a PR record
    • Depends on: 5
    • Requirements: R10.1–R10.6
    • Test: T23, T6 (red→green)
  • [x] 7. The local record is a map of sessions keyed by ref

    • cli/the_loop/sessions/registry.py: {workItem, channels?, sessions{ref → handles}}; record_owning is a key lookup; session_for, link_pull_request, save_endpoint, close_endpoint and touch keep their signatures; a v1 record (top-level handles + pullRequests[]) loads into the map and is rewritten as v2; nothing about a pull request but its ref is kept
    • Depends on: 1
    • Requirements: R5.1, R5.2, R5.5, R8.1
    • Test: T24, T12, T5 (red→green)
  • [x] 8. The channel binding is the operator's; the cursor is the machine's

    • cli/the_loop/channels/state.py: threads and conversations are no longer written, cursors keeps only channel:*; the old maps are exposed read-only as legacy_bindings()
    • cli/the_loop/channels/slack.py: thread_for/bind go through the portable record's channels.slack; cursor/advance through the work item's local record; the thread → work item index is built in memory from the portable records
    • Depends on: 5, 7
    • Requirements: R2.3, R5.1, R5.3, R5.4, R7.3, abuse case 6
    • Test: T7, T8, T19 (red→green)
  • [x] 9. The control plane stops joining two identities — amended: the join stays

    • GET /api/v1/work-items now serves one record per work item, pull requests included, so the issue-302 reconciliation has nothing to do in the ordinary case
    • It is kept (design D9, amended in implementation): a pull request given its own portable record before this change still has one, and R8.1 deletes no record — removing the join would draw those twice. Inert when there is nothing to reconcile
    • nodes[] comes from the-loop check, which the client already calls
    • Depends on: 6
    • Requirements: R4.5, R10.1
    • Test: T13, T22
  • [x] 10. Lifecycle: cleanup, reset, upgrade

    • cli/the_loop/cleanup.py, reset.py: cleanup deletes the local record (cursors with it) and keeps channels; reset clears every portable section including channels and pullRequests
    • commands/upgrade-the-loop.md: report the three retired locations and the per-PR portable records as no longer read; delete nothing
    • Depends on: 6, 7, 8
    • Requirements: R7.2, R8.1, R8.2, R10.6
    • Test: T14, T23
  • [x] 11. The hand-off scenario

    • cli/tests/test_state_root_integration.py: the design's worked example on two state roots — the three pull requests and the thread survive; the new machine routes a PR event with no provider call
    • Depends on: 6, 7, 8
    • Requirements: R2.1–R2.3
    • Test: T13 (red→green)
  • [x] 12. Docs, decision record and capability docs

    • docs/cli/state.md rewritten by the classification (attribute tables); the decision record superseding decision-046's file-level rule; capability docs cli, process-graph, interactive-sessions, channels, control-plane, webhook-triggers, spec-workflow
    • Depends on: 9, 10
    • Requirements: R9.1–R9.4
    • Test: T15, T22
  • [x] 13. Verification

    • Execute the testing plan; record results, evidence and the security round
    • Depends on: 11, 12
    • Requirements: all
    • Test: T22

Released under the MIT License.