loopy-loop 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- loopy_loop-0.3.0/.github/workflows/docs-deploy.yml +66 -0
- loopy_loop-0.3.0/AGENTS.md +80 -0
- loopy_loop-0.3.0/CHANGELOG.md +88 -0
- loopy_loop-0.3.0/CLAUDE.md +17 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/Makefile +2 -2
- loopy_loop-0.3.0/PKG-INFO +515 -0
- loopy_loop-0.3.0/README.md +475 -0
- loopy_loop-0.3.0/design/analysis/codex_parallel_analysis.md +665 -0
- loopy_loop-0.3.0/design/analysis/loopy-loop-state-and-roadmap.md +445 -0
- loopy_loop-0.3.0/design/analysis/synthesis.md +274 -0
- loopy_loop-0.3.0/design/decisions.md +205 -0
- loopy_loop-0.3.0/design/designs/documentation-site.md +302 -0
- loopy_loop-0.3.0/design/designs/improvement-proposals.md +385 -0
- loopy_loop-0.3.0/design/designs/success-semantics-and-evaluation.md +189 -0
- loopy_loop-0.3.0/docs/http-contract.md +164 -0
- loopy_loop-0.3.0/docs/images/logo.png +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/docs/session-layout.md +18 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/pyproject.toml +3 -3
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/skills/loopy-loop/SKILL.md +2 -2
- loopy_loop-0.3.0/skills-lock.json +16 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/cli.py +13 -2
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/config.py +18 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/coordinator_app.py +221 -27
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/models.py +24 -0
- loopy_loop-0.3.0/src/loopy_loop/recovery.py +262 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/worker.py +40 -5
- loopy_loop-0.3.0/src/loopy_loop/worker_identity.py +102 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_coordinator_app.py +25 -21
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_idempotent_finished.py +7 -3
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_worker.py +8 -2
- loopy_loop-0.3.0/src/tests/test_worker_liveness_recovery.py +728 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/uv.lock +5 -5
- loopy_loop-0.3.0/website/.gitignore +25 -0
- loopy_loop-0.3.0/website/README.md +71 -0
- loopy_loop-0.3.0/website/mdx-components.tsx +36 -0
- loopy_loop-0.3.0/website/next.config.ts +26 -0
- loopy_loop-0.3.0/website/package-lock.json +4779 -0
- loopy_loop-0.3.0/website/package.json +41 -0
- loopy_loop-0.3.0/website/postcss.config.mjs +8 -0
- loopy_loop-0.3.0/website/public/.nojekyll +0 -0
- loopy_loop-0.3.0/website/public/CNAME +1 -0
- loopy_loop-0.3.0/website/src/app/docs/child-sessions/page.mdx +135 -0
- loopy_loop-0.3.0/website/src/app/docs/cli-reference/page.mdx +104 -0
- loopy_loop-0.3.0/website/src/app/docs/concepts/page.mdx +63 -0
- loopy_loop-0.3.0/website/src/app/docs/configuration/page.mdx +118 -0
- loopy_loop-0.3.0/website/src/app/docs/evaluation/page.mdx +110 -0
- loopy_loop-0.3.0/website/src/app/docs/getting-started/page.mdx +122 -0
- loopy_loop-0.3.0/website/src/app/docs/http-contract/page.mdx +215 -0
- loopy_loop-0.3.0/website/src/app/docs/layout.tsx +46 -0
- loopy_loop-0.3.0/website/src/app/docs/page.mdx +37 -0
- loopy_loop-0.3.0/website/src/app/docs/session-layout/page.mdx +202 -0
- loopy_loop-0.3.0/website/src/app/docs/success-and-control/page.mdx +172 -0
- loopy_loop-0.3.0/website/src/app/docs/troubleshooting/page.mdx +186 -0
- loopy_loop-0.3.0/website/src/app/docs/workflows/page.mdx +126 -0
- loopy_loop-0.3.0/website/src/app/globals.css +166 -0
- loopy_loop-0.3.0/website/src/app/layout.tsx +46 -0
- loopy_loop-0.3.0/website/src/app/page.tsx +49 -0
- loopy_loop-0.3.0/website/src/components/docs/DocsPageNavigation.tsx +42 -0
- loopy_loop-0.3.0/website/src/components/docs/DocsSidebar.tsx +62 -0
- loopy_loop-0.3.0/website/src/components/docs/DocsTableOfContents.tsx +81 -0
- loopy_loop-0.3.0/website/src/components/docs/MobileDocsSidebar.tsx +54 -0
- loopy_loop-0.3.0/website/src/components/search/SearchCommand.tsx +181 -0
- loopy_loop-0.3.0/website/src/components/site/SiteHeader.tsx +38 -0
- loopy_loop-0.3.0/website/src/lib/docs/navigation.ts +64 -0
- loopy_loop-0.3.0/website/src/lib/search/pagefind.ts +86 -0
- loopy_loop-0.3.0/website/src/lib/utils.ts +7 -0
- loopy_loop-0.3.0/website/tsconfig.json +42 -0
- loopy_loop-0.2.0/CHANGELOG.md +0 -41
- loopy_loop-0.2.0/PKG-INFO +0 -408
- loopy_loop-0.2.0/README.md +0 -368
- loopy_loop-0.2.0/docs/http-contract.md +0 -106
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/.eval-banana/config.toml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/.github/workflows/ci.yml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/.github/workflows/release.yml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/.gitignore +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/.team-harness/config.toml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/.team-harness/coordinator_system_message.md +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/.team-harness/worker_footer.md +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/.team-harness/worker_suffix.md +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/LICENSE +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/__init__.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/harness_runner.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/scheduler.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/sessions.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/state_store.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.gitignore +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_reviewer/config.yaml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_reviewer/prompt.txt +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_runner/config.yaml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/eval_runner/prompt.txt +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/inner/config.yaml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/inner/prompt.txt +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/outer/config.yaml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/.loopy_loop/workflow_sets/inner_outer_eval/workflows/outer/prompt.txt +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/loopy_loop_config.yaml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/inner_outer_eval/loopy_loop_goal.txt +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/pm_planner_dispatcher/.gitignore +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/dispatcher/config.yaml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/dispatcher/prompt.txt +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/planner/config.yaml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/pm_planner_dispatcher/.loopy_loop/workflow_sets/pm_planner_dispatcher/workflows/planner/prompt.txt +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/pm_planner_dispatcher/loopy_loop_config.yaml +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/loopy_loop/templates/pm_planner_dispatcher/loopy_loop_goal.txt +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/__init__.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/conftest.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_api_base_normalization.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_cli.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_config.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_examples.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_fresh_run_archive.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_goal_check_gate.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_goal_hash_derivation.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_harness_runner.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_must_follow_success.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_scheduler.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_sessions.py +0 -0
- {loopy_loop-0.2.0 → loopy_loop-0.3.0}/src/tests/test_state_store.py +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: Docs Site
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
paths:
|
|
7
|
+
- "website/**"
|
|
8
|
+
- ".github/workflows/docs-deploy.yml"
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: ["main"]
|
|
11
|
+
paths:
|
|
12
|
+
- "website/**"
|
|
13
|
+
- ".github/workflows/docs-deploy.yml"
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
|
|
16
|
+
# Allow GITHUB_TOKEN to deploy to GitHub Pages.
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
pages: write
|
|
20
|
+
id-token: write
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
build:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
defaults:
|
|
26
|
+
run:
|
|
27
|
+
working-directory: website
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- uses: actions/setup-node@v4
|
|
32
|
+
with:
|
|
33
|
+
node-version: "22"
|
|
34
|
+
cache: npm
|
|
35
|
+
cache-dependency-path: website/package-lock.json
|
|
36
|
+
|
|
37
|
+
- name: Install dependencies
|
|
38
|
+
run: npm ci
|
|
39
|
+
|
|
40
|
+
# `next build` type-checks the project and generates .next/types, so a
|
|
41
|
+
# separate pre-build `tsc` step is intentionally omitted (it would run
|
|
42
|
+
# before those generated types exist).
|
|
43
|
+
- name: Build static site (Next.js export + Pagefind index)
|
|
44
|
+
run: npm run build
|
|
45
|
+
|
|
46
|
+
- name: Upload Pages artifact
|
|
47
|
+
uses: actions/upload-pages-artifact@v3
|
|
48
|
+
with:
|
|
49
|
+
path: website/out
|
|
50
|
+
|
|
51
|
+
deploy:
|
|
52
|
+
# Only publish from main; PRs run the build job above as a check.
|
|
53
|
+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
|
|
54
|
+
needs: build
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
# Serialize production deployments only, so PR build-checks stay concurrent.
|
|
57
|
+
concurrency:
|
|
58
|
+
group: "pages-deploy"
|
|
59
|
+
cancel-in-progress: false
|
|
60
|
+
environment:
|
|
61
|
+
name: github-pages
|
|
62
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
63
|
+
steps:
|
|
64
|
+
- name: Deploy to GitHub Pages
|
|
65
|
+
id: deployment
|
|
66
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# AGENTS.md — working agreement for this repo
|
|
2
|
+
|
|
3
|
+
`loopy-loop` runs long-running AI agent workflows inside a repository. A FastAPI
|
|
4
|
+
**coordinator** owns durable loop state in files and picks the next workflow; a single
|
|
5
|
+
**worker** runs each assignment through `team-harness`. Continuity lives in files and git,
|
|
6
|
+
not in a chat transcript. Read `README.md` for the user-facing model, and
|
|
7
|
+
`design/decisions.md` for *why the system is the way it is*.
|
|
8
|
+
|
|
9
|
+
This file is the working agreement for any agent (or human) changing this repo. Three
|
|
10
|
+
things are non-negotiable.
|
|
11
|
+
|
|
12
|
+
## Rule 1 — Respect the deliberate decisions; don't "fix" them
|
|
13
|
+
|
|
14
|
+
`design/decisions.md` is the canonical Architecture Decision Log (D1, D2, …). **Several
|
|
15
|
+
decisions there look like defects if you only skim the code, and are recorded precisely so
|
|
16
|
+
they don't get "fixed" by accident.** Before changing behavior in these areas, read the
|
|
17
|
+
relevant decision (and its companion design doc):
|
|
18
|
+
|
|
19
|
+
- **D3 — iteration success ≠ "work is good."** `IterationResult.success` means only that
|
|
20
|
+
the harness ran without erroring. Do not make it consult worker exit codes / infer
|
|
21
|
+
semantic success. The eval layer (`control.json` + `goal_check.json`) decides quality.
|
|
22
|
+
- **D4 — evaluation is LLM-as-judge; agents don't author deterministic checks.** Do not
|
|
23
|
+
"add deterministic checks" to the stock `inner_outer_eval` eval workflows. (A target
|
|
24
|
+
repo that owns its own test suite is a different case — see D4.)
|
|
25
|
+
- **D2 — single worker is deliberate.** Do not add parallel loopy workers as a scaling
|
|
26
|
+
feature.
|
|
27
|
+
- **D5 — full autonomy with a last-resort escape hatch.** See Rule 2.
|
|
28
|
+
|
|
29
|
+
If you believe a decision is genuinely wrong, propose amending `design/decisions.md`
|
|
30
|
+
(state what changes and why) — do not silently contradict it in code.
|
|
31
|
+
|
|
32
|
+
## Rule 2 — Autonomy is the goal; the human escape hatch is a LAST resort (D5)
|
|
33
|
+
|
|
34
|
+
This system is meant to run **fully autonomously, with no human in the loop**. Human
|
|
35
|
+
involvement is a last resort, never a normal step.
|
|
36
|
+
|
|
37
|
+
- **The one sanctioned escape hatch already exists.** When a workflow hits a *genuinely
|
|
38
|
+
terminal* blocker — a decision only a human can make, a missing credential, a
|
|
39
|
+
billable/destructive action it isn't permitted to take — it writes the session
|
|
40
|
+
`control.json` with:
|
|
41
|
+
```json
|
|
42
|
+
{"state": "stopped", "reason": "<specific terminal blocker>", "stop_reason": "unresolvable_error", "schema_version": 1}
|
|
43
|
+
```
|
|
44
|
+
This stops the loop as terminal, with a recorded reason. That is the entire
|
|
45
|
+
human-in-the-loop mechanism, and it is enough.
|
|
46
|
+
- **Exhaust autonomous options first.** Re-scope, retry with a better child goal, route
|
|
47
|
+
around the blocker. Reach for `unresolvable_error` only when a blocker is genuinely
|
|
48
|
+
unavoidable without a human. It should fire rarely.
|
|
49
|
+
- **Make the give-up legible.** The recorded blocker is the only thing a human sees when a
|
|
50
|
+
run stops — state exactly what was missing and what was tried.
|
|
51
|
+
- **Do NOT build or assume a preferred human gate.** No `paused` / `waiting_for_human`
|
|
52
|
+
state, no `gate_request.json`, no external-action approval flow. That was considered and
|
|
53
|
+
rejected (D5; `design/designs/improvement-proposals.md` P0.2). Do not pause a run to ask
|
|
54
|
+
a question when you could either solve it autonomously or stop cleanly with
|
|
55
|
+
`unresolvable_error`.
|
|
56
|
+
|
|
57
|
+
## Rule 3 — Design and decision docs must be understandable cold, by future agents AND humans
|
|
58
|
+
|
|
59
|
+
A design or decision doc is read by someone who was **not** in the conversation that
|
|
60
|
+
produced it — a future agent with no memory of this session, or a human who is not a
|
|
61
|
+
specialist. Write for them.
|
|
62
|
+
|
|
63
|
+
- **Explain, don't just name.** Naming a mechanism ("the ping-pong protocol", "cadence
|
|
64
|
+
scheduling", "the double loop") is not explaining it. State, in plain language, *what it
|
|
65
|
+
is, what problem it solves, and why we chose it*, with a concrete example where it helps.
|
|
66
|
+
- **The reasoning lives in the doc, not in your head.** Don't rely on a future reader
|
|
67
|
+
re-deriving the rationale. A decision-log entry may state the conclusion tersely; the
|
|
68
|
+
companion design section must be self-contained.
|
|
69
|
+
- **Anchor claims in the code.** Reference the file/function (e.g. `coordinator_app.py`,
|
|
70
|
+
`harness_runner.py`) so a reader can verify — but lead with the plain-English meaning.
|
|
71
|
+
- **Keep `design/` honest about status.** `design/designs/` is binding; `design/analysis/`
|
|
72
|
+
is working notes (may be superseded); `improvement-proposals.md` is *proposed, not
|
|
73
|
+
decided*. Don't cite a proposal as if it were a decision.
|
|
74
|
+
|
|
75
|
+
When in doubt on any rule, favor the version a stranger could read cold and fully
|
|
76
|
+
understand.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
Note: `CLAUDE.md` points here. Keep this file as the single source; don't fork the two.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 (breaking)
|
|
4
|
+
|
|
5
|
+
**Breaking API change — `/register` requires the worker's process identity.**
|
|
6
|
+
A register without a `worker` object is rejected with HTTP 400. Pre-0.3
|
|
7
|
+
workers cannot register against a 0.3 coordinator; upgrade workers and
|
|
8
|
+
coordinator together (they normally ship in the same install).
|
|
9
|
+
|
|
10
|
+
- **Worker liveness verification (D7).** The worker sends its process
|
|
11
|
+
identity (hostname + pid + a pid-reuse-proof start-time token) with
|
|
12
|
+
`/register` and `/finished`; the coordinator stamps it onto the dispatched
|
|
13
|
+
task. A `/register` while the recorded worker is *verifiably still alive*
|
|
14
|
+
returns HTTP 409 instead of abandoning live work — closing the
|
|
15
|
+
duplicate-work window. Unverifiable identities (remote hosts, or a
|
|
16
|
+
team-harness without process identity) keep the pre-existing
|
|
17
|
+
assume-abandoned recovery behavior. Because every dispatched task now has
|
|
18
|
+
a recorded owner, a stale `/finished` is replayed **only to that owner**
|
|
19
|
+
(anyone else gets HTTP 409) — a task persisted by a pre-identity version
|
|
20
|
+
keeps the legacy replay for that one resume.
|
|
21
|
+
- **Orphaned-agent recovery (P2.5 / TH-D5 consumer side).** When a worker is
|
|
22
|
+
confirmed dead with nothing recoverable, the coordinator applies
|
|
23
|
+
`recovery_policy` (new coordinator-side config, NOT part of the wire
|
|
24
|
+
snapshot; default `drain`, one shared `recovery_drain_timeout_s` deadline,
|
|
25
|
+
default 600s) to agent processes the dead worker's harness run left behind,
|
|
26
|
+
via team-harness's process reaper: drained agents finish and their repo
|
|
27
|
+
edits survive; a `salvage.json` in the interrupted iteration directory
|
|
28
|
+
records what was handled; the history entry is `abandoned_after_<policy>`
|
|
29
|
+
when anything settled. Recovery runs OUTSIDE the state lock (`loopy
|
|
30
|
+
status`/`stop` stay usable while draining), validates that each discovered
|
|
31
|
+
run record actually belongs to the iteration, is same-host-only (a worker
|
|
32
|
+
identity from another hostname skips reaping), and refuses to dispatch
|
|
33
|
+
replacement work (HTTP 409) when any orphan may still be running or when
|
|
34
|
+
team-harness's parent-liveness guard reports the run's owner alive.
|
|
35
|
+
Requires team-harness with the process reaper (> 0.2.10); older versions
|
|
36
|
+
skip orphan recovery gracefully.
|
|
37
|
+
- A stale `/finished` from a **different identified worker** now gets HTTP 409
|
|
38
|
+
instead of a second copy of the live task; unknown identities keep the
|
|
39
|
+
pre-existing stale-retry behavior. State-lock contention surfaces as a clean
|
|
40
|
+
HTTP 503 (and friendly CLI errors) instead of raw tracebacks.
|
|
41
|
+
- The bundled worker uses an unbounded read timeout on `/register` only
|
|
42
|
+
(recovery can legitimately block registration up to the drain deadline),
|
|
43
|
+
keeps the bounded timeout on `/finished`, and exits with code 3 on a 409.
|
|
44
|
+
|
|
45
|
+
## 0.2.1
|
|
46
|
+
|
|
47
|
+
- Improve README onboarding, install, initialization, configuration, and logging docs.
|
|
48
|
+
- Update package and skill descriptions to avoid unclear repository-scope jargon.
|
|
49
|
+
|
|
50
|
+
## 0.2.0 (breaking)
|
|
51
|
+
|
|
52
|
+
**Breaking API change — drop leases, polling, and worker identity.**
|
|
53
|
+
|
|
54
|
+
The three-endpoint API (`POST /workers/register`, `POST /workers/{worker_id}/next`,
|
|
55
|
+
`POST /workers/{worker_id}/finished`) is replaced by a two-endpoint ping-pong API:
|
|
56
|
+
|
|
57
|
+
- `POST /register` — returns `TaskResponse` directly (`action: "run"` or `"stop"`).
|
|
58
|
+
- `POST /finished` — returns `TaskResponse` for the next task or stop signal.
|
|
59
|
+
|
|
60
|
+
The `"wait"` action is gone. The worker loop is now a simple `while task.action == "run"`
|
|
61
|
+
loop with no polling.
|
|
62
|
+
|
|
63
|
+
**Removed models and fields:**
|
|
64
|
+
- `WorkerState`, `ActiveAssignment`, `RegisterWorkerResponse`, `NextActionResponse` removed.
|
|
65
|
+
- `assignment_id` removed from `FinishedRequest` and `HistoryEntry`.
|
|
66
|
+
- `worker_id` removed from `HistoryEntry`.
|
|
67
|
+
- `workers` dict and `active_assignment` removed from `LoopState`.
|
|
68
|
+
- Constants `DEFAULT_LEASE_SECONDS`, `DEFAULT_POLL_INTERVAL_SECONDS`,
|
|
69
|
+
`DEFAULT_FINISHED_RETRY_ATTEMPTS`, `DEFAULT_FINISHED_RETRY_BACKOFF_SECONDS`,
|
|
70
|
+
`WAIT_ACTION` removed.
|
|
71
|
+
|
|
72
|
+
**Added:**
|
|
73
|
+
- `CurrentTask` model replaces `ActiveAssignment` (no `worker_id`, no lease).
|
|
74
|
+
- `TaskResponse` model returned by both `/register` and `/finished`.
|
|
75
|
+
- `LoopState.current_task: CurrentTask | None` replaces `active_assignment`.
|
|
76
|
+
|
|
77
|
+
**Crash recovery:** If a worker crashes mid-run, the next `/register` call detects the
|
|
78
|
+
orphaned `current_task`, records it in history as `error="abandoned"`, and dispatches
|
|
79
|
+
fresh work.
|
|
80
|
+
|
|
81
|
+
**Migration note:** Existing `state.json` files containing `active_assignment` or `workers`
|
|
82
|
+
fields will have those fields silently dropped on first read (Pydantic ignores unknown
|
|
83
|
+
fields). Any in-flight run at upgrade time will lose its `active_assignment` record without
|
|
84
|
+
a history entry. Drain all workers before deploying this version.
|
|
85
|
+
|
|
86
|
+
## 0.1.0
|
|
87
|
+
|
|
88
|
+
- Initial release.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
The working agreement for this repo lives in **[`AGENTS.md`](./AGENTS.md)** — read it first.
|
|
4
|
+
It is the single source; this file only points to it so the two never diverge.
|
|
5
|
+
|
|
6
|
+
Quick orientation for what most often trips up a change here:
|
|
7
|
+
|
|
8
|
+
- **`design/decisions.md`** is the canonical decision log. Several decisions look like
|
|
9
|
+
defects if you only skim the code and are recorded so they don't get "fixed" by accident
|
|
10
|
+
(notably **D3** iteration-success semantics and **D4** LLM-as-judge evaluation).
|
|
11
|
+
- **Autonomy is the goal; the human escape hatch is a last resort (D5).** The system runs
|
|
12
|
+
unattended. When a workflow hits a genuinely terminal blocker it stops by writing
|
|
13
|
+
`control.json` with `stop_reason: "unresolvable_error"` and a specific recorded reason —
|
|
14
|
+
that is the entire human-in-the-loop mechanism. Do **not** build or assume a preferred
|
|
15
|
+
`paused` / `waiting_for_human` gate; exhaust autonomous options first, then stop cleanly.
|
|
16
|
+
|
|
17
|
+
See `AGENTS.md` Rules 1–3 for the full agreement.
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
## Start Claude Code with MCP configuration
|
|
8
8
|
claude:
|
|
9
9
|
@test -f .mcp.json || (echo "Error: .mcp.json not found. Please create it first." && exit 1)
|
|
10
|
-
|
|
10
|
+
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 CLAUDE_CODE_ENABLE_AUTO_MODE=1 claude --dangerously-skip-permissions --mcp-config .mcp.json --remote-control
|
|
11
11
|
|
|
12
12
|
## Continue previous Claude Code session
|
|
13
13
|
claudecontinue:
|
|
14
14
|
@test -f .mcp.json || (echo "Error: .mcp.json not found. Please create it first." && exit 1)
|
|
15
|
-
|
|
15
|
+
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 CLAUDE_CODE_ENABLE_AUTO_MODE=1 claude --dangerously-skip-permissions --mcp-config .mcp.json --continue --remote-control
|
|
16
16
|
|
|
17
17
|
## Codex
|
|
18
18
|
codex:
|