Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 90 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,96 @@ model is left to the CLI to validate.
> model) is therefore validated by the vendor CLI at run time, not at `apply`.
> Live catalog probing is deferred to a later milestone.

**Shipped adapter scope (M3).** Codex and Claude are full adapters: they grant
each declared ledger-output directory to the sandboxed run (`--add-dir`), so a
loop runs unchanged on either. The **Cursor adapter is limited** in M3 — it has
no equivalent writable-root grant, so a loop that declares `state/...` outputs is
reported as unsupported for Cursor at preflight (use Codex/Claude for
output-producing loops). The M3 design's cross-provider **sub-agent** capability
(a Cursor loop spawning a sub-agent on another provider) and per-role multi-model
compilation are **deferred to M3.5**; the shipped adapters run a single headless
invocation per loop.
**Outputs never require an out-of-worktree write grant (M3.5).** Declared
`state/...` outputs are staged *inside* the run worktree
(`<worktree>/outputs/...`); the agent writes only there, and the control plane
**promotes** the produced files to the durable ledger after the run. Because no
adapter writes outside its worktree, Codex/Claude no longer need `--add-dir` and
Cursor keeps its sandbox — the earlier coarse `--sandbox disabled` grant is gone
in the normal path. (If a loop is ever pointed at a write target outside the
worktree, the adapters still grant it: `--add-dir` for Codex/Claude, and
`--sandbox disabled --force` for Cursor, since `cursor-agent` has no per-dir
flag.) All three adapters run output-producing loops.

## Multi-model loops (M3.5)

A loop can split into per-role agents on different providers — e.g. a `gpt-5.5`
**implementer** and an `opus` **reviewer**. A role binds a vendor-neutral *agent
definition* (its behavior — instructions, tools, read-only flag, verify rubric)
to an *execution binding* (`vendor` + `model`); the agent definition is the
single source of truth for behavior and the engine is a swappable binding on top
of it.

```yaml
# roles decompose a loop into maker/checker; omit for a single-model loop.
roles:
implementer:
agent: agents/implementer.md # vendor-neutral behavior
vendor: codex
model: gpt-5.5
reviewer:
agent: agents/reviewer.md # readonly: true travels with the role
vendor: claude
model: opus
outputs: [state/build/reviews/{{run_id}}.md] # its own review notes
execution: inter-stage # inter-stage (default) | intra-run
```

A role owns its declared `outputs`. `readonly` means the role must not modify
source code or the maker's outputs — but a read-only reviewer still writes its
**own** review-notes output (above). A maker with no role `outputs` inherits the
loop's top-level `outputs`.

Two execution paths:

- **`inter-stage`** (portable default): each role runs as its own ordered
adapter invocation and hands a **structured artifact** (prior status, promoted
ledger output paths + content digests, and captured stdout) to the next stage
through the ledger. Works across any mix of Codex/Claude/Cursor with no
gateway. A read-only reviewer's contract is enforced: it runs against the
maker's promoted ledger outputs and the control plane rejects the run if the
reviewer modifies any protected (pre-existing) worktree file.
- **`intra-run`**: the role agent definitions are compiled into the harness
runtime's current native sub-agent format (`.codex/agents/*.toml` with
`developer_instructions`/`sandbox_mode`, and Markdown-with-frontmatter
`.claude/agents/*.md` / `.cursor/agents/*.md`) and one invocation spawns them
as sub-agents. Cross-provider intra-run is native only on **Cursor**, so a
mixed-vendor intra-run loop must use a Cursor harness (enforced at
validation/preflight).

**Cross-provider Cursor spawn — verified live.** The compiled `.cursor/agents/*.md`
sub-agent is discovered and spawned by a real `cursor-agent` run: a
`gpt-5.5-high` main agent spawned the compiler-emitted `reviewer` sub-agent
running on `claude-opus-4-8-high` (confirmed via the run's structured
`taskToolCall`), satisfying the M3.5 cross-provider exit criterion. The opt-in
smoke test reproduces it (skipped offline per `CONTRIBUTING.md`):

```bash
LOOPCRAFT_LIVE_CURSOR=1 uv run pytest tests/test_cursor_live.py -q
```

> Caveat: per-sub-agent model selection is plan-dependent. On legacy
> request-based plans without Max Mode, Cursor may run sub-agents on the
> parent/Composer model regardless of the compiled `model` field.

**Scope (M3.5).** The inter-stage handoff is a **structured artifact** (status,
promoted output paths + content digests, stdout) persisted through the ledger and
reconstructed for the next stage — not a Git diff; a code maker/checker against a
real Git worktree/diff is deferred with the L4 build loop.
Enforced: per-role vendor/model, output ownership, read-only enforcement
(control-plane hash check inter-stage; native `readonly`/`sandbox_mode`
intra-run, with a read-only role rejected under a Claude harness), a single
explicit reviewer `Verdict: PASS`/`FAIL` (missing/conflicting/FAIL fails and
stops the pipeline), and the aggregate **runtime** budget (measured from pipeline
start). Not enforced yet (named deferrals): `max_turns`/`max_tokens` need adapter
usage telemetry, and `max_consecutive_failures` is a scheduler/store concern.
Role `tools` are **policy-validated** (unknown tool or a read-only role holding a
mutating local tool fails preflight), not yet mapped to runtime-native
allowlists.

`loopctl run <loop>` and `--dry-run` detect a roles loop automatically: dry-run
shows the resolved per-role vendor/model, and preflight checks every role's
adapter, binary, and agent definition.

## Scheduling & deployment (M2)

Expand Down
36 changes: 36 additions & 0 deletions agents/implementer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
name: implementer
description: >-
Maker role for a multi-model loop. Given a scoped task, writes the change in
the run worktree and gets the declared checks green before handing off.
readonly: false
tools: [repo-read, repo-write]
verify: "the declared outputs exist and the task's acceptance checks pass"
---
You are the implementer (maker). Work to explicit, verifiable success criteria.

Before you start, read the repo's `CONTRIBUTING.md` (at the repository root; it
lists the requirements for code and other contributions — style, structure,
testing, docs, commit conventions, and safety rules) and **adhere to every
requirement in it**. The reviewer will treat any unmet requirement as a blocker.

Follow these engineering principles (adapted from Andrej Karpathy's notes on LLM
coding pitfalls — https://github.com/multica-ai/andrej-karpathy-skills):

1. **Think before coding.** Don't assume. If the task is ambiguous, state your
assumption explicitly (or stop and flag it) instead of guessing silently.
Surface tradeoffs and push back when a simpler approach exists.
2. **Simplicity first.** Write the minimum code that solves the task — no
speculative features, no abstractions for single-use code, no error handling
for impossible cases. If 200 lines could be 50, write 50.
3. **Surgical changes.** Touch only what the task requires. Don't refactor,
reformat, or "improve" adjacent code or comments. Remove only the dead code
your own change created; mention unrelated dead code rather than deleting it.
4. **Goal-driven execution.** Turn the task into a verifiable goal: identify (or
write) the checks first, then loop until they pass or the budget is hit.

Then:

- Write the declared outputs listed in the I/O contract to their exact paths.
- End with a plain-text summary of what you changed and why — this summary is
handed to the reviewer as the next stage's input.
98 changes: 98 additions & 0 deletions agents/reviewer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
name: reviewer
description: >-
Deep adversarial reviewer and review orchestrator. Verifies implementation
claims against the spec, repository rules, code, and tests by coordinating
independent sub-reviewers and a final synthesizer.
readonly: true
tools: [repo-read, agent-spawn, test-run]
verify: >-
independent review passes (parallel sub-agents when available, sequential
otherwise) were synthesized; every finding cites a verified file:line; every
CONTRIBUTING.md violation cites the rule; claimed fixes are classified fixed,
partial, or unresolved; the verdict is an explicit PASS or FAIL; review notes
are written to the declared output
---
You are the checker and review orchestrator, not the maker. Review deeply, but
write concisely. Never modify source or maker outputs; write only your declared
review-notes output.

## Establish scope

Assume the workspace includes an implementation plan, typically an HTML file.
Locate it, identify the milestone named by the task, and review the feature
branch diff from its merge base through HEAD against that milestone's scope,
deliverables, exit criteria, risks, and deferrals. Also read `CONTRIBUTING.md`,
which lists the requirements for code and other contributions: style, structure,
testing, docs, commit conventions, and safety rules. **Every requirement is
binding**: treat an unmet requirement as a blocker and cite its specific
section/heading. If `CONTRIBUTING.md` is absent, note that and review against the
spec and general best practice. Also read the acceptance criteria and prior
review reports/responses, and record relevant uncommitted changes. Turn the
resulting contract into a checklist; do not review the branch in isolation or
let a response silently weaken the plan.

## Use independent reviewers

Run parallel sub-agent passes when supported:

- **Requirements:** design, prior claims, and `CONTRIBUTING.md`.
- **Correctness/security:** execution paths, trust boundaries, failures, state,
permissions, symlinks/concurrency, and cleanup.
- **Tests/API:** compatibility, typing/models, negative tests, docs, and UX.

Use a separate synthesizer to deduplicate findings, challenge speculation,
resolve disagreements against current code, and rank root causes by merge
impact. You own the final verdict. If sub-agents are unavailable, state that and
perform the passes sequentially.

## Review focus

Read full changed files, not only hunks, and trace success and failure end to
end. Check especially:

- design/exit-criterion coverage across every entry point and execution mode;
- validation, preflight, runtime, override, ownership, status, verdict, budget,
metrics, persistence, and DAG consistency;
- technically enforced safety versus prompt-only claims, including sandbox/tool
policy, path containment, TOCTOU, atomicity, and partial failure;
- vendor-native schemas, model/provider compatibility, backward compatibility,
and surrounding-code conventions;
- focused positive and negative tests for each behavior change.

Also grade against these four principles from Andrej Karpathy's notes on LLM
coding pitfalls (https://github.com/multica-ai/andrej-karpathy-skills):

1. **Think before coding:** exposed assumptions, ambiguity, and tradeoffs instead
of guessing silently.
2. **Simplicity first:** wrote the minimum solution without speculative
abstractions, config, or impossible-case handling.
3. **Surgical changes:** touched only what the task required; avoided drive-by
refactors and unrelated deletion.
4. **Goal-driven execution:** defined verifiable checks and did not claim success
before the acceptance criteria passed.

Treat tests as evidence, not proof. Use safe offline checks only; never invoke
live/paid services or state-changing operations for review. Mark unverified
claims explicitly.

## Evidence and output

For each finding, trace input to impact, check for existing defenses, verify the
current `file:line`, and give a fix plus regression test. Omit speculation.
Classify prior findings as **fixed**, **partial**, **not fixed**, or
**regressed**.

Write your review to the declared review-notes output, in this order:

1. **Scope and evidence** — reviewed state, references, checks, and limitations.
2. **Executive summary** — the most important conclusion and merge readiness.
3. **Claim/requirements status**, when applicable — concise fixed/partial/open
coverage; omit this section for a first-pass review.
4. **Blockers** — must-fix findings, ordered by severity.
5. **Suggestions and nits** — include the most important missing tests here.
6. **Merge gate and verdict** — required next steps, then explicit PASS or FAIL.

Every blocker must cite concrete `file:line` evidence, explain impact, and give
an actionable fix. Keep positives factual, and never mark PASS when an
acceptance criterion or prior blocker is only partially addressed.
13 changes: 6 additions & 7 deletions docs/loopcraft-implementation-design.html
Original file line number Diff line number Diff line change
Expand Up @@ -724,9 +724,8 @@ <h3>Annotated example</h3>
agent: agents/reviewer.md # the checker's own instructions + readonly tools
vendor: claude # cross-provider checker
model: opus
# execution:
# intra-run -> one harness spawns both as sub-agents (native on cursor)
# inter-stage-> control plane runs each as an ordered stage (any providers)
execution: inter-stage # inter-stage (default) -> ordered stages, any providers
# intra-run -> sub-agents in one harness (cross-provider native on cursor)

locus: vm # vm | cloud | local (default: vm — always-on first)
cadence:
Expand Down Expand Up @@ -797,7 +796,7 @@ <h4>Roles are backed by agent definitions</h4>
You are the checker, not the maker. Find what the implementer talked itself
into. Output: blockers, then nits, then an explicit PASS/FAIL.</code></pre>
<p>
At deploy time the adapter <strong>compiles each role&rsquo;s agent definition into the chosen runtime&rsquo;s native agent format</strong> &mdash; <code>.codex/agents/*.toml</code>, <code>.claude/agents/*</code>, or <code>.cursor/agents/*.yaml</code> &mdash; and attaches the role&rsquo;s model. For an <em>intra-run</em> loop the harness spawns both as sub-agents in one process; for an <em>inter-stage</em> loop the control plane runs each agent definition as its own ordered adapter invocation, passing the diff through the memory ledger. Either way, the <strong>agent definition is the single source of truth for what a role does</strong>, and the vendor/model is a swappable binding on top of it.
At deploy time the adapter <strong>compiles each role&rsquo;s agent definition into the chosen runtime&rsquo;s native agent format</strong> &mdash; <code>.codex/agents/*.toml</code> (TOML with <code>developer_instructions</code> + <code>sandbox_mode</code>), <code>.claude/agents/*.md</code>, or <code>.cursor/agents/*.md</code> (Markdown with YAML frontmatter) &mdash; and attaches the role&rsquo;s model. For an <em>intra-run</em> loop the harness spawns both as sub-agents in one process; for an <em>inter-stage</em> loop the control plane runs each agent definition as its own ordered adapter invocation, passing a structured handoff artifact (status, promoted output paths + digests, stdout) through the memory ledger. Either way, the <strong>agent definition is the single source of truth for what a role does</strong>, and the vendor/model is a swappable binding on top of it. <em>(Full Git-diff code maker/checker execution is deferred with the L4 build loop.)</em>
</p>
<div class="note">
<strong>Why this matters:</strong> separating behavior (agent def) from execution (vendor+model) keeps the maker/checker split portable and honest &mdash; the reviewer&rsquo;s <code>readonly: true</code> and verify rubric travel with the <em>role</em>, not the model, so a checker can never quietly become a maker just because you swapped its engine.
Expand Down Expand Up @@ -835,7 +834,7 @@ <h3>How each adapter maps the loop</h3>
<tr><td>Tools / connectors</td><td>MCP + <code>nv-tools</code></td><td>MCP + <code>nv-tools</code></td><td>MCP + <code>nv-tools</code></td></tr>
<tr><td>Stop condition</td><td><code>/goal</code>-style check</td><td><code>/goal</code> + evaluator</td><td>wrapper loop + evaluator sub-agent</td></tr>
<tr><td>Isolation</td><td>built-in worktree</td><td><code>git worktree</code></td><td><code>--worktree</code> flag</td></tr>
<tr><td>Sub-agents</td><td><code>.codex/agents/</code> (TOML)</td><td><code>.claude/agents/</code></td><td><code>.cursor/agents/</code> (YAML)</td></tr>
<tr><td>Sub-agents</td><td><code>.codex/agents/</code> (TOML)</td><td><code>.claude/agents/</code> (Markdown)</td><td><code>.cursor/agents/</code> (Markdown)</td></tr>
<tr><td>Run logs / cost</td><td><code>~/.codex/</code></td><td><code>~/.claude/</code></td><td>Cursor session logs</td></tr>
<tr><td>Model selection</td><td><code>--model</code> / effort</td><td><code>--model</code></td><td><code>--model</code>; per-subagent model</td></tr>
<tr><td>Cross-provider in one run</td><td>via gateway only</td><td>via gateway only</td><td><strong>native</strong> (GPT + Opus)</td></tr>
Expand Down Expand Up @@ -1510,8 +1509,8 @@ <h2>Build Plan &amp; Milestones</h2>
<ul>
<li><strong>Build:</strong> the <code>roles:</code> manifest block; the agent-definition compiler (role def &rarr; <code>.codex/.claude/.cursor</code> agent formats); both execution paths &mdash; intra-run sub-agents (native on Cursor) and inter-stage composition across adapters via the memory ledger; the Cursor writable-root grant so ledger-writing loops run under Cursor (parity carried over from M3).</li>
<li><strong>Depends on:</strong> M3 (adapters).</li>
<li><strong>Exit criteria:</strong> a maker/checker loop runs a <code>gpt-5.5</code> implementer + an <code>opus</code> reviewer and hands the diff between stages cleanly; a Cursor loop spawns a cross-provider sub-agent; a ledger-writing loop runs unchanged under Cursor.</li>
<li><strong>Risks:</strong> passing context/diffs across stages without loss; two-model token cost; verifying the readonly reviewer can&rsquo;t mutate.</li>
<li><strong>Exit criteria:</strong> a maker/checker loop runs a <code>gpt-5.5</code> implementer + an <code>opus</code> reviewer and hands a <strong>structured artifact</strong> (status, promoted output paths + content digests, stdout) between stages cleanly through the ledger; the read-only reviewer&rsquo;s verdict gates the pipeline and it cannot mutate protected state; a ledger-writing loop runs unchanged under Cursor; a Cursor loop spawns a cross-provider sub-agent &mdash; <strong>verified live</strong>: a <code>gpt-5.5-high</code> main agent spawned the compiler-emitted <code>reviewer</code> sub-agent on <code>claude-opus-4-8-high</code> (opt-in smoke test <code>tests/test_cursor_live.py</code>; per-sub-agent model honoring is plan/Max-Mode dependent). <em>Deferred to L4:</em> a full Git-diff code maker/checker.</li>
<li><strong>Risks:</strong> passing context/diffs across stages without loss; two-model token cost; verifying the readonly reviewer can&rsquo;t mutate. <em>Budget note:</em> the aggregate <strong>runtime</strong> cap is enforced across stages (measured from pipeline start); <code>max_turns</code>/<code>max_tokens</code> need adapter usage telemetry and <code>max_consecutive_failures</code> is a scheduler/store concern &mdash; both are named deferrals, not silently dropped.</li>
</ul>
</details>

Expand Down
Loading