Skip to content

Execution Log: a closed work item is recorded as ended, and the board stops asking for a human on it

Append-only log of progress for the user's visibility.

Phase transitions

PhaseEnteredReviewed/approved byNotes
phase-selection2026-09-09Tier 3 (human-approves-pr; below humanSignOffMinTier: 4): one portable section, written on the shared close path, read by two attention surfaces; no new grant, route or sensitive path. Brainstorming skipped: the ticket's root-cause analysis and fix direction are the requirement. No authorized the-loop execute reaches this cloud session, so the selection is recorded here and every phase is walked
requirements-definition2026-09-09requirements.md — six requirements, six abuse cases
design2026-09-09design.md — the fact, the writer, more closures, the readers; decision-113
test-planning2026-09-09testing-plan.md — fourteen rows, eight applicable
tasks-breakdown2026-09-09tasks.md — six tasks
implementation2026-09-09On claude/github-issue-329-fide3i — tasks 1–5
verification2026-09-09evidence/verification.md — rows T1, T2, T3, T9, T11, T13; evidence/security-review.md — six abuse cases, six closed
needs-review2026-09-09PR raised; awaiting the owner (tier 3: human-approves-pr)
complete

Pull requests

PRScope / tasksStatus
#330tasks 1–6: the whole work itemopen

Progress entries

2026-09-09 — implemented, verified, ready for review

  • Phase: implementation → verification → needs-review
  • Did: tasks 1–6, red first (the 20 new service tests and the 7 dashboard tests were run against the unchanged source and fail there — the red block in evidence/verification.md). ENDED as the fifth portable section with record_ended / ended / clear_ended on ControlStore (the stamp gets its at there); reset clears it; the dispatcher's closed branch stamps and disarms every closing ref the machine tracks — the matched session's, and any ref with a session record of any status or a portable record — and a reopened action clears the stamp; the poller reconciles every session record plus every armed, frozen or rostered portable record, skips a stamped record unless a live session sits beside it, and clears the stamp of any item it lists; GhItemState.closed_byClosure.actor → the synthesized event's sender; the shared POLL_CLOSURE_DELIVERY_PREFIX on the router so the stamp can say which ingress saw the close; core/attention.py skips awaiting-input and armed-without-session for a stamped record; the dashboard's join carries ended, nulls the question and the gate, keeps the item out of needs-you, groups it Shipped/Idle with a muted chip and drops it from the inbox; the demo's shipped item carries the stamp; the docs.
  • Checkpoint/tests: make check and the dashboard's three commands — see evidence/verification.md. Existing assertions changed: one (test_an_already_closed_session_is_not_reconciled_again inverted into test_a_closed_session_is_asked_once_and_not_again_once_stamped, because the rule it pinned was the gap). Four existing test files gained a tmp portable directory (test_routing.py's factory, test_workspace.py's two builders, test_tmux_runner_integration.py's layout): a close now writes a record, and RoutingConfig's default directory is the checkout's own.
  • Self-review: three passes over the diff. Pass one found a real gap — a stamp arriving through the tracked directory from another machine would have stopped this machine's poller from ever closing its still-live session; the skip now applies only when no live session sits beside the stamp, with a test — plus a side-effecting list comprehension on the close path (a plain loop now), a ControlStore rebuilt per candidate per cycle (cached), and _closing_refs recomputed per ref in the reopen path. Pass two found two doc gaps (the store's module docstring example; the cleanup table in docs/cli/state.md). Pass three found nothing new.
  • Next: the owner's review.
  • Blockers: none.

2026-09-09 — spec chain drafted

  • Phase: requirements-definition → tasks-breakdown
  • Did: read the close path (Dispatcher.handle's closed branch, close_session, _cleanup_after_close), the poller's _reconcile_closures and _process_item, the GitHub provider's fetch_item_state / closure / closure_event, the store (WorkItemStore.write_section, SECTIONS), reset.py, cleanup.py, core/attention.py, and the dashboard's model.ts / grouping.ts / Sidebar.tsx / WorkItemDetail.tsx at cab21a6; confirmed the ticket's five causes, and found a sixth fact that decides the fix direction — the closed session record keeps the row on the board whatever happens to the portable record, so deletion retires nothing. Wrote the four artifacts and the decision.
  • Checkpoint/tests: baseline — test_routing.py, test_poller.py, test_control.py, test_core_attention.py, test_workitem.py, test_reset.py green; cd ui && bun run test green.
  • Next: task 1 (the fact), red first.
  • Blockers: none.

Verification results

Only when this work item declared test-planning away. It did not: results live in testing-plan.md.

What was verifiedCommandOutcomeEvidence
see testing-plan.md

Design critic review

Not selected for this work item.

RoundCritic (<harness>/<model>)OutcomeFindings → dispositionLink

Review cycles

CycleType (self/critic/security)ReviewerOutcomeLink
1self (diff)the-loop (this session)new findings — a cross-machine stamp would strand a live session (fixed, tested); a side-effecting comprehension, an uncached store, a recomputed set: cleanedthis log
2self (diff + docs)the-loop (this session)new findings — two doc gaps: fixedthis log
3self (diff)the-loop (this session)zero (converged)this log
criticunavailable — reviews.critics is empty in this repository's config; does not count toward criticReviewCount
4securitythe-loop checklistpass — six abuse cases, six closedevidence/security-review.md

Security review (gate)

  • Mechanism: the-loop checklist (security.review.mechanism: auto; no security-review skill is invocable from this session's plugin set)
  • Outcome: pass — evidence/security-review.md, six abuse cases closed
  • Human sign-off: not required (tier 3 < humanSignOffMinTier: 4); the owner's PR approval is the gate

Final validation evidence

RequirementProof
R1.1, R1.6test_routing.py::test_an_issue_close_stamps_the_work_item_ended, test_the_stamp_is_logged_as_work_item_ended; test_control.py::test_record_ended_and_clear_ended
R1.2test_a_pr_merge_stamps_the_prs_own_record_and_not_the_linked_issue, test_a_close_for_an_untracked_ref_creates_no_record, test_a_closed_session_record_counts_as_tracked
R1.3test_a_close_with_no_session_still_stamps_a_tracked_record; Scenario: A closed issue with no session is stamped ended
R1.4test_workitem.py::test_a_record_with_only_ended_is_kept_and_indexed; graph untouched by the close path (diff)
R1.5test_reset.py::test_reset_clears_the_ended_section; cleanup.py imports no store (existing assertion)
R2.1, R2.3test_a_reopen_clears_the_ended_stamp, test_a_reopen_without_a_stamp_writes_nothing; Scenario: Reopening an issue clears the stamp
R2.2test_poller.py::test_a_listed_item_clears_its_ended_stamp
R3.1test_a_paused_sessions_closed_item_is_reconciled, test_a_record_without_a_session_is_reconciled[control / graph / collaborators], test_a_closed_session_is_asked_once_and_not_again_once_stamped, test_a_live_session_beside_a_stamp_is_still_reconciled; Scenario: A paused session's closed item is detected by the poller
R3.2test_a_poll_only_record_is_not_reconciled
R3.3, R3.4the existing issue-94 / issue-159 / issue-315 reconciliation tests, unchanged and green
R4.1, R4.2test_provider_closure_carries_the_closer, test_provider_closure_event_names_the_closer_as_sender
R4.3Scenario: A polled closure by an authorized closer releases the item, Scenario: A polled closure by an unlisted closer is deferred
R5.1–R5.3model.test.ts › an ended work item (issue-329) (four cases), grouping.test.ts › sidebarGroup for an ended item (three cases)
R5.4test_core_attention.py::test_an_ended_item_asks_for_no_attention
R5.5grouping.test.ts › reads the demo's shipped item from its stamp; model.test.ts › treats a record without the field … as open
R6.1, R6.2docs/cli/state.md, the two capability docs, decision-113 (below); make lint (markdownlint) clean

Capability docs

Capability docWhat changedHistory row
docs/capabilities/webhook-triggers.mdtwo behaviour bullets — the closure stamp (with or without a session; reopened clears it) and the widened reconciliation; the deferred-cleanup paragraph now says both ingresses name the closerissue-329 row added at the top
docs/capabilities/control-plane.mdthe sidebar grouping bullet gains the ended rule; the awaiting-input bullet gains the service-side ruleissue-329 row added at the top
docs/capabilities/cli.mdthe issue-315 bullet's "that scope's sessions" became "that scope's tracked items", pointing at webhook-triggersnone (one clause)

Documentation

DocumentWhat changed
docs/cli/state.mdthe record's section list; a new ended section (fields, when written, when cleared, what deleting it does); the index's sections; the reset and cleanup tables; the security paragraph's disclosure sentence
docs/decisions/decision-113.md + index rowwhy the record is stamped and kept, why graph stays, why attribution rather than a relaxed gate
ui/README.mdthe sidebar sentence names where an ended item goes
README.md, skills/the-loop/**, the docs site's other pagesunchanged — none of them describes closure or the board's groups

Released under the MIT License.