Skip to content

Latest commit

 

History

History
110 lines (83 loc) · 5.08 KB

File metadata and controls

110 lines (83 loc) · 5.08 KB

Status

Last updated: 2026-07-16


What this is

A dependency-aware deployment loop with Claude self-healing. Two processes run on two separate machines and coordinate entirely through git commits — no sockets, no HTTP.

[Sender — runner machine]               [Receiver — Mac]
sender.py  ──── git push ──────────►  receiver.py
  runs make targets in dep order          watches sender-state.json for failures
  writes sender-state.json                invokes Claude to diagnose + fix
  reads receiver-state.json               pushes fix, sets fix_pushed=true
  retries on fix  ◄──── git pull ──────

File ownership is strict:

  • sender-state.json — written only by the sender
  • receiver-state.json — written only by the receiver

This eliminates merge conflicts entirely.

sender_resilient.py wraps sender.py — auto-restarts on crash, pulls latest code before each restart. make loop-start-sender always runs sender_resilient.py.

Single machine: run make loop-start-sender and make loop-start-receiver in separate terminals on the same machine.


Current state

What is done

  • Full Python implementation — all scripts in Python, no bash
  • Strict file ownership — sender writes sender-state.json only, receiver writes receiver-state.json only
  • No waiting_for_fix flag — receiver infers sender needs fix from sender-state.json directly via lib.sender_needs_fix()
  • Renamed state filesloop-state.jsonreceiver-state.json, loop-run-state.jsonsender-state.json
  • Extracted pure functions in receiver.pyshould_invoke_claude(), build_claude_prompt(), gather_previous_logs(), set_fix_pushed(), notify_human() — all testable without subprocess
  • stop/pause signalsmake loop-stop and make loop-pause kill local tmux and push signal via git
  • loop-start-sender / loop-start-receiver split — separate make targets for each side
  • Auto-create loop-context.md — created at startup by sender_resilient.py if missing
  • 210 passing tests, 76% coverage — 201 unit tests + 9 integration tests

Coverage breakdown

File Coverage
lib.py 99%
receiver.py 99%
trim_loop_context.py 97%
loop_ack.py 97%
loop_reset.py 97%
sender_resilient.py 93%
loop_pause.py 95%
loop_status.py 95%
loop_stop.py 95%
loop_targets.py 89%
sender.py 89%

Remaining coverage gaps

The uncovered lines in loop_ack.py, loop_stop.py, loop_pause.py, and loop_reset.py are the push/retry-on-conflict paths (lines after the first successful git push). These require a live git remote to test properly.

receiver.py lines 72 and 391 — module-level constant initialization and the final time.sleep after an Claude invocation. Both trivial to skip.


File map

In this repo (.loop/)

File Purpose
bin/lib.py Shared library — all pure logic: state I/O, git ops, dep resolution, blocker detection, signal helpers, state transitions
bin/sender.py Core sender loop — runs make <target> in dep order, retries, polls receiver for fix signal
bin/sender_resilient.py Crash-resilient wrapper — restarts sender.py on crash, pulls latest code first, auto-creates loop-context.md
bin/receiver.py Receiver — polls sender-state.json for failures, invokes Claude to fix, sets fix_pushed in receiver-state.json
bin/loop_ack.py Acknowledges human action / resumes after pause
bin/loop_pause.py Sets pause signal in receiver-state.json, kills local tmux
bin/loop_stop.py Sets stop signal in receiver-state.json, kills local tmux
bin/loop_reset.py Resets both state files to blank, commits and pushes
bin/loop_status.py Prints current status from sender-state.json
bin/trim_loop_context.py Caps loop-context.md at 500 lines, archives overflow
Makefile loop-start-sender, loop-start-receiver, loop-attach, loop-stop, loop-pause, loop-status, loop-reset, loop-ack, loop-test
receiver-state.json Template — copy to consuming repo root, edit targets/deps/blocker_patterns
sender-state.json Template — copy to consuming repo root
tests/test_loop.py 201 tests — unit tests
tests/test_integration.py 9 tests — end-to-end subprocess integration tests

Generated in the consuming repo

File Owner Purpose
receiver-state.json Receiver Config: targets, deps, max attempts, blocker patterns, fix signals, stop/pause
sender-state.json Sender Runtime state: completed/failed targets, attempt counts, status
loop-context.md You + Claude Claude's memory — failure history and fixes. Auto-created on startup.
runs/ Sender Per-target logs, resilient wrapper log, Claude invocation logs
docs/loop-context-archive.md Auto Overflow from loop-context.md when it exceeds 500 lines

Next steps (in order)

  1. Push coverage on ack/stop/pause/reset retry paths — requires live git remote or test git server