Skip to content

Execution Log: GitHub events trigger a harness session programmatically

Append-only log of progress. Issue #15 asks for the detailed design and the task breakdown; implementation follows once the spec is approved.

Phase transitions

PhaseEnteredReviewed/approved byNotes
requirements-definition2026-07-02@MadaraUchiha-314 (PR #16)Distilled from issue #15
design2026-07-02@MadaraUchiha-314 (PR #16)MCP vs webhook decision → decision-016
tasks-breakdown2026-07-02@MadaraUchiha-314 (PR #16)10-task DAG
implementation2026-07-02approved via "Let's implement it now." on PR #16
needs-review2026-07-02all 10 tasks done; PR #16 updated
complete

Progress entries

2026-07-02 — Spec drafted (requirements → design → tasks)

  • Phase: tasks-breakdown
  • Did: Researched the two candidate approaches. Confirmed the official github/github-mcp-server has no event-subscription capability (MCP transports are client-initiated; Claude.ai/code's PR-watching is Anthropic-hosted infrastructure) — recorded as decision-016. Confirmed the official programmatic surfaces: claude -p --resume <session-id> --output-format json (session id in the JSON output; resume scoped to the project directory) and cursor-agent -p --resume <chat-id> --output-format json (Cursor's official SDK is TypeScript-only — answers the issue's TODO). Drafted requirements.md (R1–R5 + NFRs), design.md (registry / router / dispatcher / adapters on top of the existing gh-webhook receiver) and the 10-task DAG in tasks.md.
  • Checkpoint/tests: make lint (markdownlint) on the new docs.
  • Next: Human review of the three phases; then execute the DAG.
  • Blockers: Phase approvals (workflow.requireHumanReviewPerPhase: true); open questions raised on the ticket (label-gating for spawnOnUnmatched).

2026-07-02 — Design review feedback addressed (MCP notifications, SDK adapters)

  • Phase: tasks-breakdown
  • Did: Addressed @MadaraUchiha-314's PR #16 review: (1) sharpened decision-016 — MCP does define in-session notifications (resources/subscribe, listChanged), but they only reach a connected client, the GitHub MCP server declares no event surface, and an event-pushing MCP server still needs a webhook receiver; (2) promoted the Claude Python-SDK adapter from "future work" to an explicit opt-in extra (the-loop[claude-sdk], new optional task 10) — verified claude-agent-sdk carries runtime deps (anyio, mcp, sniffio) and drives the claude CLI over stdio, so the stdlib CLI adapter stays default; Cursor's TypeScript SDK would need a Node sidecar, so its adapter stays CLI-only.
  • Checkpoint/tests: make check green.
  • Next: Phase approvals.
  • Blockers: none new.

2026-07-02 — Owner decision: CLI-only triggering for both harnesses

  • Phase: tasks-breakdown
  • Did: Applied @MadaraUchiha-314's decision on PR #16 ("let's use cli to trigger the harnesses not the sdks, for both cursor and claude"): removed the optional Claude Python-SDK adapter (former task 10) from the design and DAG; recorded the CLI-only decision in design.md trade-offs and decision-016. The HarnessAdapter contract still permits SDK implementations later (R4.5).
  • Checkpoint/tests: make check green.
  • Next: Phase approvals.
  • Blockers: none.

2026-07-02 — Implementation: tasks 1–10 executed

  • Phase: implementation → needs-review
  • Did: Executed the DAG in order 1 → {2,4,5,6,7} → 3 → 8 → 9 → 10. Red→green evidence per task:
    • Task 1: validate_config.py INVALID ('routing' was unexpected) → schema extended → VALID.
    • Tasks 2/4/5/6/7: pytest cli/tests/test_routing.py collection error (No module named 'the_loop.harness') → modules implemented → 32 passed.
    • Task 3: pytest -k sessions_command 5 failed (command missing) → 5 passed.
    • Task 8: integration tests 3 failed (--route unknown; on_event lacked the delivery id) → wired → 57 passed total; scenarios queryable via the-loop scenarios (4 rows, Requirement-linked).
    • Tasks 9/10: docs (CLI README, automation.md, architecture.md), records (decision-016 accepted, roadmap item shipped), markdownlint 0 errors.
  • Checkpoint/tests: make check green (ruff · pyright · validate · 57 pytest · markdownlint).
  • Next: Human review of PR #16 (autonomy tier 3: human-approves-pr).
  • Blockers: none.

2026-07-02 — CI red: ruff-format drift; gate parity fixed

  • Phase: needs-review
  • Did: CI (pre-commit) failed on ruff format for the new files — make check did not include a format check, so local was green while CI was red (exactly the tooling drift the-loop forbids). Applied the formatter, and added a format-check target to make check so the gap cannot recur.
  • Checkpoint/tests: uv run pre-commit run --all-files all hooks Passed; make check (now incl. format-check) green.
  • Next: CI green on the re-push; human review.
  • Blockers: none.

2026-07-02 — Expanded integration scenarios (reviewer request, PR #16)

  • Phase: needs-review
  • Did: @MadaraUchiha-314 asked for integration tests that prove the harness is actually triggered, covering specific lifecycle scenarios. Refactored the integration suite around a ServerFactory (live receiver + stub harness with configurable delay/exit + timing capture) and grew it from 4 to 10 end-to-end scenarios, one per question: idle-resume, unmatched-drop, unmatched-spawn, busy-session queueing (asserts the 2nd run starts after the 1st ends), parallel across different items (asserts overlap), duplicate-once, harness-error isolation + redelivery-retry, bad-HMAC→401, disabled-event-type ignored. All assert on the stub CLI's real argv/cwd/timing. Ran the timing-sensitive ones 3× for flake safety.
  • Checkpoint/tests: make check green (63 pytest, incl. 10 Gherkin integration scenarios queryable via the-loop scenarios).
  • Next: Review.
  • Blockers: none.

2026-07-02 — Auto-close session on PR merge/close (reviewer request, PR #16)

  • Phase: needs-review
  • Did: @MadaraUchiha-314 confirmed the receiver should auto-close a session when its PR closes. Added dispatcher handling: a pull_request closed event closes the matched session(s) in the registry instead of resuming, and never spawns on a close. Updated the prompt templates (self-close guidance → "the receiver auto-closes"), design (Dispatcher §4 + Open→Resolved), CLI README, and automation.md. New tests: 3 unit (auto_closes_on_pr_close, pr_close_never_spawns, non-close PR still resumes) + 1 integration scenario (A session auto-closes when its PR is merged/closed).
  • Checkpoint/tests: make check green (67 pytest).
  • Next: Review.
  • Blockers: none.

2026-07-02 — Label-gated auto-execution (reviewer request, PR #16)

  • Phase: needs-review
  • Did: @MadaraUchiha-314 specified a GitHub auto-execution workflow. Added new requirement R6 and implemented it: spawnOnUnmatched: labeled mode + configurable autoExecuteLabel (default the-loop: auto-execute). The router now reads label presence from the payload (event_carries_label: labeled action's label.name or the item's current labels) and sets RoutedEvent.labeled; the dispatcher spawns only for labelled items under labeled mode, using a dedicated spawn prompt template (webhook-autoexecute-prompt.md) that kicks off /the-loop:work-on. Added issues to the event list. Config schema/template/repo config updated (repo default is now labeled). Two design decisions I made without a reply because the AskUserQuestion tool kept failing (connector flapping): (1) extend the enum with labeled rather than replace never/always (backward compatible); (2) a labelled spawn runs the full work-on loop — both stated on the PR for veto.
  • Checkpoint/tests: make check green (76 pytest; 13 integration scenarios). New tests: label detection, router flag, labeled-mode spawn-only-for-labelled, spawn prompt kicks off work-on, always still ignores the label, + 2 integration scenarios (label spawns; unlabelled new issue ignored).
  • Next: Meta — create the the-loop: auto-execute label + apply to issue #15 (needs GitHub MCP auth); reply on PR. Then review.
  • Blockers: GitHub MCP re-auth for the label meta-task.

Review cycles

CycleType (self/critic)ReviewerOutcomeLink
1selfharnessFixed: EARS phrasing, DAG edges, untrusted-payload handling made explicit
2human@MadaraUchiha-314Fixed: MCP-notifications rationale sharpened; optional Claude SDK adapter addedPR #16 comment
3human@MadaraUchiha-314Decision applied: CLI-only triggering for both harnesses; SDK adapter removedPR #16 comment
4selfharnessFixed: router and dispatcher now share one Deduper (router's early duplicate check was inert); design §3 protocol snippet aligned with implemented signatures

Final validation evidence

  • make check green: ruff · pyright · validate_config.py (schema + both configs) · 57 pytest (16 pre-existing + 41 new) · markdownlint (0 errors) — the same commands CI runs.
  • Acceptance mapping: R2 → registry + sessions command tests; R3 → router/dispatcher tests + the three Gherkin integration scenarios (the-loop scenarios lists them with Requirement: links); R4 → adapter tests against stub claude/cursor-agent binaries (argv + cwd asserted); R5 → same-session FIFO / cross-session parallelism dispatcher tests; R1 → decision-016 (accepted).

Released under the MIT License.