Turn a plain Markdown todo list into verified work.
For each item on the list, Jumar breaks it into concrete subtasks, does one subtask at a time, and proves each one worked — with a real, executable check — before starting the next. An item is done when every one of its subtasks passed its own check. If a check cannot be passed, the item stops and says exactly which subtask and which check failed.
The unit of trust is the verified subtask, not the agent's claim of success.
todo file → ingest → select ─┬─ time eligibility (@not-before / @due / @every)
└─ dependency eligibility (@depends)
→ decompose → gate → ┌ execute one subtask ┐
│ verify it │ ← repeat
└ repair (bounded) ┘
→ complete item → report
A run that fails loudly instead of succeeding vaguely. Hand "migrate the export script" to an agent and you get something, plus a confident summary. Nothing checks that it works and nothing notices that steps two through five never happened. Here the checkbox stays unticked, the report names the subtask and the check that failed, and the exit status is non-zero. Confident, unverified, partial completion is the failure mode the whole design exists to refuse.
A forensic record of what actually happened. Every run appends to
runs/<run-id>/journal.jsonl — one line per event, strictly ordered, written
before the next step starts. Days later you can reconstruct which check ran,
what the evidence was, what the agent claimed versus what the verifier found,
and how each repair attempt differed. jumar report <run-id> renders it;
jumar status rolls it up per item across every run.
Something you can leave on a schedule. A single-flight lock means a slow run
never overlaps its successor — a second invocation exits 0 with
already_running, not an error. Repeated failure advances @failed= on the
item and parks it with @paused=auto-failures at the threshold, so a broken
item stops re-spending its budget every firing instead of failing nightly
forever. Recurrence is delegated to cron or launchd; there is no daemon and no
resident watcher.
A plan before any agent runs. jumar plan --dry-run decomposes the next
eligible item and prints the subtasks and their checks without executing
anything. On a vague item that breakdown is useful on its own, and it is cheap.
Pressure on you to define "done" first. An item the system will accept has to say what proof looks like. A subtask with no executable acceptance check is rejected at planning time — "trust me" is not a check — so the specification work happens before the agent runs rather than during review. This is the least obvious benefit and often the largest.
Nothing leaves the machine. The system never pushes and never opens a PR. It stops at a local commit and prints the commands for you to run yourself.
- A subtask with no executable acceptance check is rejected at planning time.
- A
commandcheck must be a real argv. A shell wrapper (bash -c "…") is refused outright, and so is an argv that cannot fail —true,echo, barels. A check that always passes is not a check. - A
filecheck fails on a zero-byte file. A download that returned no body leaves the path present and empty, andtest -fis happy with that. - Verification runs in a fresh context that never sees the executing agent's reasoning — only the world it left behind.
- The judge verifier is prompted adversarially: its default answer is fail, and it must cite specific evidence to pass.
- A check that cannot run is
inconclusive, which is not a pass. - Repairs are bounded, and a repair may never rewrite its own check.
- A deadline changes only queue position. Being overdue never shortens a
plan or skips verification — a test asserts
dueis unread by the decompose, execute, verify and repair paths.
Checks are proposed by the same model that does the work, so they establish that an artefact exists and has the shape that was asked for. They do not establish that its content is correct. Verification is strongest where acceptance is mechanical — a test suite, an exit status, a file that must contain a specific value — and weakest on judgement work, where "a document exists and mentions the right things" is the most a check can assert. On that kind of task the system still enforces the process (sources fetched before drafting, nothing marked done that did not run) but a human reviewer remains the one who decides whether the reasoning is sound.
Everything below is built, tested and merged.
| Command | What it does |
|---|---|
jumar plan |
Ingest, select, decompose, print. --dry-run stops before execution. |
jumar run |
The full pipeline for the next eligible item. |
jumar resume <run-id> |
Replay the journal and continue from the first unverified subtask. |
jumar report <run-id> |
Render a run report. Exit 1 if anything failed. |
jumar status |
Item-centric view across the todo file and every run journal. |
jumar schedule add|list|remove|show |
Install and inspect cron/launchd entries. |
jumar doctor |
Check config, harness binary, allow list, schedules, todo file. |
--json is available on plan, run, report and status; --verbose streams the
agent's output during run. Progress goes to stderr so stdout stays clean, and
is suppressed under --json or when stderr is not a TTY, so scheduled runs stay
quiet. CI runs make check with a per-module coverage floor.
Known gaps are tracked in IMPLEMENTATION_PLAN.md.
Python 3.11 or newer (the code uses datetime.UTC).
git clone https://github.com/justinmclean/jumar.git Jumar && cd Jumar
make install # editable install + dev tools (pytest, ruff, mypy)
make check # ruff + mypy + pytest + the build-loop fixture tests
jumar --versioncp todo.example.md todo.md # todo.md is git-ignored by default
$EDITOR todo.md
jumar plan --dry-run # see what it would pick, and why
jumar run --approve # confirm each subtask before it runs
jumar status # where everything standsFull worked examples, the flag reference, and the config reference are in USAGE.md.
Context prose above an item is passed to the agent as background,
never treated as work.
- [ ] Add a --json flag to the export script @id=export-json @priority=1 @capability=write_fs
- [ ] Update the README install section @depends=export-json
- [ ] Rewrite the install steps for the new flag
- [ ] Check every command in the README actually runs
- [ ] Rotate the backup logs @every=weekday
- [ ] Draft the quarterly summary @not-before=2026-09-01 @due=2026-09-05
- [x] Already done — skipped| Token | Meaning |
|---|---|
@id= |
Stable item id. Otherwise derived from the text plus a hash. |
@priority= |
Lower sorts first. |
@depends= |
Blocks until the named item is done. Cycles are a startup error. |
@capability= |
Grants authority: read_fs, write_fs, run_commands, network, git_commit. |
@max-subtasks= |
Per-item override of the plan-length cap. |
@not-before= |
Eligibility gate. Before this instant the item is deferred. |
@due= |
Advisory deadline. Affects ordering and reporting only. |
@every= |
Recurrence: weekday, 1d, 2w, mon,thu. |
@failed= |
Consecutive failure count. Written by the system, cleared on success. |
@paused= |
Parked; never selected. Written by the system at the failure threshold. |
An indented task list under an item is your breakdown and is used verbatim; the model is only asked to supply acceptance checks for steps that lack one.
A completed @every= item stays unchecked with its @not-before= advanced
to the next occurrence — that is what makes it recurring. An unparseable
schedule token blocks that item rather than being guessed at.
| Path | What it is |
|---|---|
specs/ |
The specification. Ten stages, ~74 numbered acceptance criteria. |
IMPLEMENTATION_PLAN.md |
Prioritised work items. One item = one branch = one PR. |
AGENTS.md |
Operational rules for any agent working in this repo. |
tools/spec-loop/ |
The spec-driven build loop that builds this repo. |
src/jumar/ |
The Python package. |
tests/ |
pytest suites, one module per stage. |
USAGE.md |
Worked examples, command reference, config reference. |
- 01 — Product & Vision — problem, goals, non-goals, guiding principles.
- 02 — Functional Spec — the ten stages, each with behaviour, contract, and numbered acceptance criteria (AC1.1 … AC10.9) that tests check directly.
- 03 — Data Model — every shape, its invariants, and the append-only journal format.
- 04 — Technical Plan — stack, package layout, the harness abstraction, execution isolation, clock and scheduling rules, and the build phases.
- 05 — Operator Tooling — the spec loop, and how the product's discipline maps onto how the repo is built.
Jumar is built the way Jumar works — one work item at a time, on its own branch, validated before it commits:
| product concept | build-loop equivalent |
|---|---|
| todo item | work item in IMPLEMENTATION_PLAN.md |
| decomposition | the plan beat |
| one subtask at a time | one work item per build iteration |
| acceptance check | the item's Validation command (make check) |
| verification gate | no commit until validation is green |
| journal | git history — one commit per verified item |
./tools/spec-loop/loop.sh plan # derive work items from the specs
./tools/spec-loop/loop.sh build 1 # build exactly one, on its own branchReview the branch, push it yourself, open the PR yourself. The loop stops at a
local commit by design. Full detail in
tools/spec-loop/README.md.
Prefer build 1 followed by a merge over build 3. Parallel iterations can't
see each other's files, so two branches will independently implement the same
shared module and you'll spend the saved time untangling it.
The enforced boundary is send, not fetch. An agent that cannot reach a primary source writes from memory instead, which is the worse outcome.
networkis a default capability, andcurl/wgetare on the allow list.- The deny list is the outbound-transmission vectors:
mail,mailx,sendmail,ssmtp,msmtp,ssh,scp,sftp,rsync.git pushandghare hard-denied in every dispatched argv, matched on the argv basename so an absolute path does not slip past. - The allow list is consulted before a process is spawned. A refused argv is
inconclusiveand never runs. - Every subprocess is argv with
shell=False, andmodels.Checkrefusesbash -cwrappers so the list cannot be sidestepped with one array element. - Secrets come from the environment or a git-ignored
.env, never the repo.
With python3 on the allow list and the network reachable, none of the above
stops a determined agent — it is defence in depth, not a sandbox. The real
control is the execution environment: run jumar inside a container or VM with
restricted egress and no push credentials. The agent CLI runs with its
unattended flag, which bypasses the agent's permission prompts, not the OS.
Hand-written PRs are welcome — the spec-loop is how this repo is usually built, not a requirement for contributing. Start with CONTRIBUTING.md. Security reports go through SECURITY.md, not the public issue tracker.
Apache-2.0.