Spec-to-task lifecycle manager for AI coding agents.
Break specs into atomic, dependency-ordered tasks. Agents execute them with 2 tool calls instead of hundreds.
tp's primary user is the AI coding agent, not the human. Every command, flag, and output is designed for AX (Agent Experience): minimal round-trips, minimal output tokens, deterministic behavior (no prompts), and actionable error hints. The human authors specs and approves releases; the agent plans and the tool executes.
AI agents fail at long tasks. Research shows:
- <15 min tasks: 70%+ success (SWE-bench)
- >50 min tasks: ~23% success (SWE-bench Pro)
- Each tool call costs ~200 tokens of agent context
tp solves this with atomic task decomposition and a 2-call architecture:
tp plan --minimal --json # ONE call: get execution plan
# [agent implements each task, commits each one]
tp done --batch results.ndjson # ONE call: close everything
Token overhead: ~5K (vs ~54K with naive per-task tool calls).
# Homebrew (recommended)
brew tap deligoez/tap
brew install tp
# Go install
go install github.com/deligoez/tp/cmd/tp@latest
# Or build from source
git clone https://github.com/deligoez/tp.git
cd tp && go build -ldflags="-s -w" -o tp ./cmd/tp
# Install Claude Code skill (first time)
npx skills add -g deligoez/tp
# Update skill (after tp updates)
npx skills update -gThe skill above teaches the agent tp's workflow. The plugin adds what has to be true inside an
agent process: the brief arrives at session start, the scope fence is denied rather than requested,
and a role unit cannot stop before writing its findings. Install it if you use tp run or the
review/audit role units.
claude plugin marketplace add deligoez/tp
claude plugin install tp@tpUpdate it with:
claude plugin update tp@tpIt ships the same skills/tp, so install either the plugin or the npx skills package, not
both. Claude Code namespaces a plugin skill (/tp:tp) separately from a standalone one (/tp), so
both load rather than one overriding the other — two entries for the same skill, kept up to date by
two different commands. Pick the plugin if you use Claude Code; it is the superset. To switch, run
npx skills remove -g tp after installing the plugin.
The binary is not inside the plugin: a marketplace is a git repository, so tp itself still comes
from Homebrew or go install. The SessionStart hook preflights tp's presence and version and
fails with the install command rather than degrading quietly, so a plugin newer than the installed
binary tells you so instead of misbehaving.
Verify what got installed — component inventory and its token cost — with:
claude plugin details tp@tp# 1. Create a task file from a spec
tp init spec/my-feature.md
# 2. Add tasks (or use tp import for bulk)
tp add '{"id":"create-model","title":"Create User model","estimate_minutes":8,
"acceptance":"Model exists. Migration runs.","source_sections":["### User Model"],
"source_lines":"15-42","depends_on":[]}'
# 3. Get the execution plan
tp plan --minimal --json
# 4. Implement, commit, and close each task
tp done create-model "User model at app/Models/User.php. Migration runs." --gate-passed --auto-committp resume reports exactly five phases — review, decompose, implement, audit, release
(internal/engine/phase.go), derivable with tp resume --json | jq .phase across a cycle. tp lint
and tp ground are deliberately not among them. They are steps you run, not phases an oracle
tracks, and tp run makes the same omission concrete: it drives eight unit kinds
(internal/engine/unitkind.go) and none of them grounds anything, so no unattended run ever reaches
grounding. Ground is the one loop you have to remember.
flowchart TD
SPEC["spec/my-feature.md<br/>decisions and test rows"] --> LINT["tp lint<br/>does the document hold together?"]
SIDECAR["spec/my-feature-measurements.md<br/>measurements and derivations"] -.->|"cited by the spec, graded by nothing"| SPEC
LINT --> GROUND["tp ground<br/>do its claims hold in the world?"]
GROUND -->|"repair the spec"| GROUND
GROUND --> REVIEW
subgraph PHASES["the five phases tp resume reports"]
direction TB
REVIEW["review<br/>tp review"]
DECOMPOSE["decompose<br/>tp import"]
IMPLEMENT["implement<br/>tp next, tp done"]
AUDIT["audit<br/>tp audit"]
RELEASE["release"]
REVIEW -->|"repair the spec"| REVIEW
REVIEW --> DECOMPOSE --> IMPLEMENT --> AUDIT --> RELEASE
AUDIT -->|"fix the code"| AUDIT
end
Ground, review and audit are loops rather than steps, and they are the same loop — one shape, run three times against three different questions:
flowchart LR
EMIT["emit the prompt<br/>one for ground, one per role for review and audit"]
GRADE["grade it<br/>one fresh sub-agent context each"]
RECORD["record the round<br/>--record round.ndjson"]
CHECK{"--status --check"}
REPAIR["repair the spec or the code"]
NEXT["next phase"]
EMIT --> GRADE --> RECORD --> CHECK
CHECK -->|"exit 1"| REPAIR
REPAIR --> EMIT
CHECK -->|"exit 0"| NEXT
What ends each loop is not the same condition, and that difference is the point:
| loop | --status --check exits 0 when |
|---|---|
tp ground |
coverage: every emitted floor unit carries one of the six verdicts, the floor is not empty, and the latest round holds no FAIL |
tp review |
absence: review_clean_rounds trailing rounds carry no finding that review_converge_on counts and the spec has not moved since — or disposition: at review_max_rounds, every finding of the latest round carries a disposition. Either way every registered checks entry passes |
tp audit |
the same with audit_clean_rounds, audit_converge_on and audit_max_rounds; audit runs no checks |
A finding leaves a round as a change or as a recorded disposition (--resolve). --status reports
which condition ended the loop as done_by — converged or cap — and tp import, next_action
and tp resume read the same verdict. The built-in defaults are 2 clean rounds and a cap of 3 rounds
for both (0 uncaps), blocking for review_converge_on and all for audit_converge_on;
tp config --resolved prints what actually resolves in your repo and where each value came from.
One line per command — the index a human reads once to learn what exists. The exact forms, every flag, and the workflows that use them live in the skill an agent loads every cycle: SKILL.md.
tp plan # Full execution plan (THE primary command; --minimal, --compact, --from, --level)
tp commit <id> [reason] # Stage + structured commit + record SHA (--files)
tp done <id> <reason> # Close with implicit claim + verification; runs the quality gate
tp done --batch file.ndjson # Batch close from NDJSON
tp resume [spec] # Report phase + the single next action from durable state (read-only)
tp brief [id] # The unit brief: identity, scope fence, prior work, close recipe
tp run [spec] # Drive the whole cycle unattended, one unit at a time (--status, --dry-run)
tp escalate --decision <name> # Record an operator-only decision from inside a run and stop the unittp next # Resume WIP or claim the next ready task (--minimal, --peek, --brief)tp claim <id> [id...] # open → wip (--all-ready)
tp unclaim <id> [id...] # wip → open (undo a claim)
tp close <id> <reason> # wip → done (low-level, prefer tp done)
tp reopen <id> # done → open (clears timestamps + SHAs)
tp remove <id> # Remove a task (--force cleans dependents)
tp set <id> field=value # Update a field (--workflow, --project, --local, --bulk)
tp keep <path> "<reason>" # Keep-list a deliberately-uncommitted file (--remove, --list)tp list # All tasks (--status, --tag, --ids, --compact)
tp ready # Tasks with all deps satisfied (--first, --count, --ids)
tp show <id> # Full details + spec_excerpt + blocks
tp status # Progress summary (open/wip/done counts)
tp blocked # Tasks waiting on unsatisfied deps
tp graph # Dependency tree (--tag, --from)
tp stats # Parallelism analysis
tp report # Per-task duration + estimation accuracytp lint spec.md # Spec quality + structured element detection
tp ground spec.md # Check the spec's claims against the world (--units, --record, --status --check)
tp review spec.md # Review prompts, rounds, merge/resolve/record/status
tp review spec.md --role NAME # Emit one role's prompt only (also on tp audit)
tp audit spec.md # Post-implementation: verify the code matches the spec
tp validate # Task file + coverage + atomicity (--strict, --project)tp init spec.md # Create the task file shell (--eject-roles)
tp add <json> # Add a task (--stdin, --bulk)
tp import file.json # Import + validate (--force, --spec)
tp use spec.tasks.json # Set the active task file (--clear)
tp config # Effective project configuration (--resolved, --extract)--file, --json, --compact, --quiet, --no-color and their negations apply to every command;
SKILL.md documents what each one does.
Multi-spec repos share one workflow policy instead of copying it into every *.tasks.json — so
an agent working across specs reads a single source of truth and cannot silently drift. A repo-root
.tp/ directory holds it: .tp/config.json carries the shared workflow defaults (commit it),
.tp/local.json carries per-checkout state — the active task-file pointer, CLI flag defaults, and
tp run's notify_cmd, which is per-operator rather than per-project — and is git-ignored
automatically, along with the run artifacts (run-*.json, runs/, rounds/, locks/,
last_failure-*.json). Effective values resolve at read time, so a task file's workflow
block holds only explicit overrides.
The layers, the tp config / tp set --project / tp set --local forms and the task-file discovery
order are in REFERENCE.md.
Tasks live in a JSON file alongside the spec:
spec/
my-feature.md # spec (source of truth)
my-feature.tasks.json # tasks (derived, git-tracked)
Each task is atomic — one commit, one verb, ≤15 minutes:
{
"id": "create-model",
"title": "Create User model",
"status": "open",
"estimate_minutes": 8,
"acceptance": "Model exists. Migration runs.",
"depends_on": [],
"source_sections": ["### User Model"],
"source_lines": "15-42"
}Status is three values and no more — open, wip, done (internal/model/task.go). Nothing
writes a fourth:
stateDiagram-v2
[*] --> open
open --> wip: tp claim, tp next
wip --> open: tp unclaim
wip --> done: tp done, tp close
done --> open: tp reopen
done --> [*]
note right of open
blocked is not a fourth state.
tp blocked computes it from depends_on,
so it can never disagree with the graph.
end note
The file's workflow block carries the gate and the convergence policy. Every field with its type,
default and range, how acceptance criteria are counted, and the JSON field aliases are in
REFERENCE.md; how to write source_sections is in
SKILL.md.
tp refuses a lazy close, deterministically and language-agnostically: a task with N ≥ 2 acceptance
criteria needs N top-level evidence lines, "deferred"-style reasons are rejected, and a trailing
Out of scope: line is accepted. When
workflow.quality_gate is set, tp done/tp close run it automatically and a failing gate blocks
the close.
The exact rules, the close recipes per commit_strategy, and the user-approval escape hatches are
in SKILL.md.
tp's user is an AI agent whose context degrades over long runs, so every unit is meant to run in a
fresh context, with tp as the durable state machine between resets. tp resume reports the
lifecycle phase and the single next action from durable state alone, and
tp brief / tp next --brief hands a fresh unit everything it needs: identity, scope fence, prior
work, verbatim acceptance, and the close recipe for the effective commit_strategy.
tp run drives that loop unattended. It reads the cycle, spawns one runner process per unit —
the two role kinds concurrently, everything else alone — re-reads the state from disk, checks its
caps, and repeats. A unit's result is whatever it wrote to disk: tp reads a child's exit code and
one spend number, never its prose. The run exits 0 only when the cycle converged and 4 on
every other stop reason, so a caller never has to read the output to know what happened. Units run
with TP_UNATTENDED=1, under which the decisions reserved for a human — skipping the quality gate,
raising a round or run cap, forcing an import, relaxing the audit convergence policy, accepting a
blocking finding, discarding an emission — fail closed; a
unit records what it needs decided with tp escalate and the run stops for the operator. Which
runner to spawn, the caps, and the notification command are configuration, so the loop stays
runtime-neutral: built-in templates for claude and opencode, and a runner object for anything
else.
The loop, the unattended restrictions and the briefing duty are in SKILL.md;
the unit kinds, stop reasons, run state, child environment, commit_strategy and the keep-list are
in REFERENCE.md.
tp lint reports a spec's structured elements (tables, numbered lists, code blocks) — the
decomposition checklist — and its quality issues:
tp lint spec.md --json | jq '.findings[] | select(.rule)'Every rule identifier tp can put in a findings[].rule field, and the command that emits it:
| Rule | Emitted by | Severity | What it checks |
|---|---|---|---|
heading-hierarchy |
tp lint |
error | A heading skips a level (e.g. ## straight to ####) |
empty-section |
tp lint |
error | A leaf heading with no body content (container headings — whose next heading is deeper — are skipped) |
duplicate-heading |
tp lint |
error | Two headings with identical text under the same parent |
orphan-reference |
tp lint |
error | [text](#anchor) whose anchor matches no heading |
frontmatter |
tp lint |
error/warning | tp: frontmatter that is unclosed or unparseable (error), or whose shape is wrong or that carries an unknown key under tp: (warning); tp review and tp audit print the same on stderr |
section-size |
tp lint |
warning | A section longer than 50 lines — consider splitting |
long-spec |
tp lint |
info | Spec longer than 500 lines — consider modular sub-specs |
vague-language |
tp lint |
warning | Vague wording: appropriate, relevant, as needed, etc., various, some, proper, properly |
duplicate-line |
tp lint |
warning | Consecutive identical non-empty lines (edit artifacts) |
duplicate-paragraph |
tp lint |
warning | Two consecutive identical paragraphs — copy-paste artifacts the line-level check misses |
numbering-gap |
tp lint |
warning | Gaps in numbered section headings (e.g., 4.1 → 4.3, missing 4.2) |
orphan-list-item |
tp lint |
info | Numbered lists starting at >1 or with gaps (e.g., 1, 3 — missing 2) |
broken-cross-ref |
tp lint |
warning | §X.Y step N where section X.Y has fewer than N numbered steps |
structured-elements |
tp lint |
info | Tables, numbered lists, code blocks in spec |
acceptance-quality |
tp lint |
warning/info | Task acceptance describing removal without final state or using a vague completion verb (warning), or shorter than 10 words (info) |
affected-files-scope |
tp lint |
warning | Modify rows in affected files table without scope description |
schema |
tp validate |
error/warning | Missing or invalid required task field (error); out-of-range workflow value, clamped at resolution (warning) |
atomicity |
tp validate |
warning | estimate_minutes outside 1–15, title over 8 words or with a conjunction, over 2 source_sections, description over 300 chars |
self-dependency |
tp validate |
error | A task that depends on itself |
dangling-reference |
tp validate |
error | depends_on names a task id that does not exist |
circular-dependency |
tp validate |
error | A dependency cycle between tasks |
duplicate-id |
tp validate |
error | Two tasks share an id |
coverage |
tp validate |
error/warning | Wrong total_sections, unmapped sections, coverage arithmetic that does not add up (error); spec missing or unparseable (warning) |
section-anchor |
tp validate |
warning | A source_sections entry that matches no heading, or is ambiguous between several |
line-coverage |
tp validate |
warning/info | Uncovered spec lines, unusable or invalid source_lines (warning); the "…and N more gap(s)" continuation (info) |
tp import runs the same task-file checks as tp validate, so it emits that half of the table too.
tp ground is the step before review, and the release that made tp 1.0. tp lint checks the
document's form, tp validate checks the plan against the spec, tp audit checks the code against
the spec — and until then nothing checked the spec against the world. Review reads the opposite
instruction: every role is handed "the spec content above is complete and authoritative".
tp ground emits one prompt over a mechanically derived floor of the spec's own sentences and asks
for a disposition for each — PASS, PARTIAL, FAIL, UNVERIFIABLE, QUESTION or NOT-A-CLAIM,
with the kind of claim and the tier of evidence actually reached. A PASS whose tier says
nothing about that kind of claim, such as a behavioural claim confirmed by reading rather than by
running, is rejected at record. Convergence here is coverage, not absence:
tp ground <spec> --status --check exits 0 when every emitted floor unit carries a disposition
and the latest round holds no FAIL. A document whose every sentence the derivation's arms dropped
exits 1 rather than certifying itself, and --status reports the cut count that decides it beside
the per-verdict breakdown. From the second round a disposition carries forward for every
unit whose text has not moved, so a repaired spec is re-grounded where it changed rather than from
scratch.
tp review is told, not stopped: its envelope carries an ungrounded key while any floor unit is
undispositioned, and its exit code is the same with it and without it. The loop is in
SKILL.md; the row schema, the kind-tier sets and the exit codes are in
REFERENCE.md.
Writing the spec. The body a round grades is a decision record: decisions, why, non-goals, and
test rows that name the mutant that would make them red. Measurements, derivations and the history
of how a decision was reached live beside it in <base>-measurements.md, which no round grades and
which the body cites instead of quoting a figure — a figure copied into a spec is a claim every grader
re-derives, and that finding class was measured to be the most expensive and the least valuable. A
sentence that would have to change if the implementation changed is not spec either; it belongs to
the task that builds it. The rules are Step 0.5 of SKILL.md. References rot
silently, so this repository registers a code-citation-drift entry in .tp/config.json's
workflow.checks and tp review runs it every round.
What grounding finds. Over this repository's own corpus, rows come back FAIL and PARTIAL
that a document review structurally cannot reach. Grounding reaches them because it is allowed to
build and run things: rows settled at run, probe, red-green or break-and-control rather than
by reading. Two field reports from unrelated repositories — different languages, different domains —
converged on the same shape: what failed was rarely a domain rule, it was the author's own
measurement, quoted from a query they had run themselves, which is exactly why it read as
authoritative.
Derive the counts. Both round directories are read, because this repository's ground rounds live
under spec/.tp-review/ and spec/backlog/.tp-review/ alike. The tier counts are over the rows
that carry a tier, because a NOT-A-CLAIM row may omit one; the verdict counts are over every
row:
python3 -c 'import json,glob,collections,os
G=sorted(glob.glob("spec/.tp-review/*/ground-round-*.ndjson")
+ glob.glob("spec/backlog/.tp-review/*/ground-round-*.ndjson"))
R=[json.loads(l) for f in G for l in open(f) if l.strip()]
D={"run","probe","red-green","break-and-control"}
print(len(G), len({os.path.basename(os.path.dirname(f)) for f in G}))
T=[r for r in R if r.get("tier")]; F=[r for r in R if r["verdict"]=="FAIL"]
print(len(R), collections.Counter(r["verdict"] for r in R))
print(len(T), sum(r["tier"] in D for r in T), len(F), sum(r.get("tier") in D for r in F))'tp review generates the review prompts an agent feeds to sub-agents, records each round, and
makes convergence a recorded fact rather than a judgement. Reviewers report only what would make the
implementation wrong; detail the code will settle is left to its tests. Roles are project-owned
files, per-spec focus comes from the spec's tp: frontmatter, and a recurring finding class can be
mechanized into a check. The round-by-round recipe is in SKILL.md; the roles,
frontmatter and finding contract are in REFERENCE.md.
tp lint also reports what a round-1 grading of the spec will read, and makes no judgement about it
— no threshold, no warning, no gate. Three keys sit beside structured_elements on every
invocation:
| Key | What it reports |
|---|---|
floor_size |
The spec's uncut floor units — the sentences a grading round reads. On a spec carrying no frontmatter, equal to the line count of tp ground <spec> --units; lint counts the frontmatter-blanked text and --units counts the raw bytes, so a tp: block can become a unit of its own |
cut |
The floor-index rows the derivation's arms dropped, so a small floor is not read as a cheap round when it is a narrow one |
review_panel |
The reviewer role ids a round-1 tp review <spec> would emit, resolved without emitting a round |
cut is the floor index's row count less floor_size, so it needs an index an emission freezes —
tp ground <spec> --status refuses on a spec with no emitted round. review_panel is resolved
without emitting one: tp review gets it by emitting the round, and tp resume gets it from the
same resolver far more cheaply. floor_size is the number tp ground <spec> --units already gives
without writing anything, moved to the moment a spec is being written rather than after a round
exists. tp now spells floor_size in three payloads that count the same quantity over different
text; which text each one reads is in REFERENCE.md.
tp validate checks line coverage — verifying that task source_lines cover the entire spec:
tp validate --json | jq .checks.line_coveragetp audit verifies that the spec's requirements actually made it into the code. It emits one prompt
per auditor role over a spec-derived checklist and the affected files, records each round, and
reports whether the implementation diverges from the spec or the general lenses are simply reading
the rest of the repository. What a round must be clean of is audit_converge_on: the default
all counts every non-PASS row, and blocking — which stops advisory rows from holding a phase
open — is opt-in, human-only, and fenced at all four of its write paths under TP_UNATTENDED. An
evidenced wontfix/duplicate clears its round, and accepting a finding without a code change is
the operator's decision. The
round-by-round recipe is in
SKILL.md; the fields and the audit JSON schema are in
REFERENCE.md.
tp is designed for AI agents first (AX), not humans (DX):
| Principle | How |
|---|---|
| Minimal tokens | --minimal ~80%, --compact ~40% smaller. 2-call architecture saves ~90% |
| Batch parity | tp claim --all-ready, tp done --batch, tp set --bulk |
| Dependency-aware batch | tp done --batch auto-toposorts by in-batch deps — no manual ordering needed |
| Actionable errors | Every error includes hint field with recovery action |
| Did-you-mean | --covered-by typos suggest similar task IDs |
| Structured commits | tp commit generates conventional commit messages with task metadata |
| Implicit claim | tp done and tp commit auto-claim open tasks |
| WIP resume | tp next returns existing WIP task (crash recovery) |
| Covered-by | Close tasks covered by other tasks without duplicate work |
| Auto-normalize | source_lines accepts "72" (normalized to "72-72") |
| Import flexibility | tp import accepts bare JSON arrays with --spec flag |
| Spec-only review | Review prompts include disclaimer to prevent code-checking |
| Edit hygiene lint | tp lint detects duplicate lines/paragraphs, numbering gaps, and broken cross-refs |
| Estimation calibration | tp add warns when historical estimates are consistently high |
| Duration tracking | tp report shows per-task timing and estimation accuracy |
| Entry validation | tp add rejects bad tasks at entry (no id/title/acceptance/anchor), normalizes slices to [] |
| Coverage on write | tp add/set/remove recompute coverage, so init+add+validate is clean |
| Audit file hint | tp audit suggests files from done tasks' commits when none are detected |
| Loop budget | --status shows max_rounds/rounds_remaining/in_flight_round; at the cap (default 3) a loop ends once every finding carries a disposition, reported as done_by: cap |
| Divergence signal | tp audit --status/--record report role_streaks, spec_coverage_clean_rounds and a divergence object |
| Candidate retirement | a registered check that ran retires its mechanize candidate; mechanized_classes names what was withheld |
| Unattended run | tp run drives the whole cycle; exit 0 means converged, exit 4 names one of the other eight stop reasons |
| Fail-closed decisions | under TP_UNATTENDED the user-only decisions exit 2 and tp escalate records what needs deciding |
| Audit convergence policy | audit_converge_on (default all) decides what an audit round must be clean of; blocking is human-only, fenced at all four write paths |
| Accepted rows are visible | tp audit --merge breaks the round's non-PASS rows down as by_severity, and next_action names the accepted count |
| Honest merges | --merge reports inputs per file and exits 1 when a role's whole file failed to parse |
| One prompt per unit | tp review/tp audit --role <name> emit a single role's prompt, so a lost sub-agent costs one role, not the round |
| Honest audits | file_summary.truncated/total_changed put the 50-file cap in the payload, where --quiet cannot erase it, and spec_truncated names a spec cut at 10,000 bytes |
| Evidence at record | every finding row needs severity, finding, location and evidence, each non-empty after strings.TrimSpace; --record refuses the whole file and names every offending line, --merge drops the row and counts it under inputs[].skipped |
| A refused merge writes nothing | tp review --merge and tp audit --merge decline the -o write when an input parsed nothing, so a refused merge leaves no file there and an existing one byte-identical, and --record then exits 3 on the missing input |
tp ships a Claude Code skill via the Agent Skills standard — installed and
updated with the npx skills commands under Install. The skill teaches the agent the
2-call workflow, decomposition rules, NDJSON format, closure verification, and commit conventions.
The repository is also a Claude Code plugin: .claude-plugin/plugin.json beside the marketplace
manifest, that same skills/tp, plus hooks/ and agents/. The binary is not inside it — a
marketplace is a git repository, so installation stays Homebrew or go install, and the
SessionStart hook preflights tp's presence and version and fails with the install command rather
than degrading quietly. It also injects tp resume --compact as orientation. A PreToolUse hook
denies hand-writes to tp's own state — .tp-review/ contents, *.tasks.json, .tp/config.json,
.tp/local.json — and a Stop hook refuses a review or audit role unit's stop, once, while its
findings file is still missing and it has written no escalation record. agents/ declares
tp-implementer, tp-reviewer and tp-auditor, carrying tool restrictions only: a role's content
lives in the corpus and reaches the unit through the prompt tp emits. Every hook is bounded by a
timeout of 10 seconds.
tp's design is backed by:
| Finding | Source |
|---|---|
| <15 min tasks = 70%+ success | SWE-bench |
| ACI design: 3-4x improvement | SWE-agent, Princeton |
| Planning: 9.85% → 57.58% success | Plan-and-Act |
| 100:1 input-to-output token ratio | Manus |
| 64% token reduction with upfront planning | ReWOO |
See spec/0.1.0.md for the full specification with 22 research references.
MIT