magus-job
Fork a job, take it, return it with its result, and verify that result
Synopsis
magus job <fork|apply|exec|exit|wait|watch|run|rm|prune> [flags]
Description
Delegated work on the shell's own lifecycle. A JOB is the unit of work; a LEASE is the grant one holder has on it: its write and read paths, plus the one check it runs. A job is not a run: `magus run` executes a target with no job involved, while a job's check and the server's maintenance each cause runs.
Two channels write the job store. The client MCP tool (magus\job) is an agent's, this verb is a person's, and they reach the same store and the same rules. One author per JOB is the property that matters, and the store enforces it: a session holding a lease may record the base it landed on, shrink its own write paths, end its own job in fail or no_return, and fork a child of itself inside its own paths. Everything else, widening a boundary and verifying a job included, is refused by name.
Jobs are kept per repository rather than per checkout, so a session forking in one worktree and a holder working in another see the same set.
fork declares one job, from flags for the one-job case or from a JSON record on --stdin. It replaces the job it names rather than merging into it, which is the difference between a person declaring what a job IS and an agent advancing one field of a live one. Every path flag is repeatable or comma-separated, and an empty segment is refused rather than dropped. There is no --state: this declares a new job, and one nobody has taken is declared.
exec takes a job in this checkout. It records the base this tree actually landed on beside the checkpoint the job was handed, with the divergence between them as a fact rather than a refusal. The guard hook binds the caller when it lets exec through, keyed on the session and subagent its agent host names, so a subagent's exec never binds its parent; a host that names neither binds the checkout. A caller holding a job still declared or running is refused another; once its job has exited or ended, the next exec takes the next job.
exit returns a job with its result, FILED ONTO THE JOB so whoever waits on it reads the same record from any checkout. The run behind the result's output ref is resolved in the holder's own checkout and its record filed alongside, because an output store belongs to a cache dir and nobody else can reopen it. Do the work first, then file the result: it is a record of what happened, not a form to fill in while you are still deciding what to do. With no --stdin the job is abandoned and recorded no_return, which is not the same as returning it and failing.
wait verifies what comes back. It reads the filed result and verifies EVIDENCE, not claims: every changed path inside the declared write paths and outside the denied ones, a change set that is not empty on a job that writes, descendants the store carries, and every declared goal held: a recorded passing run for a check, the diff since the checkpoint and the knowledge graph for paths and symbols. Evidence must have been captured after the job was declared, so an old green run cannot satisfy new work. A target's own run policy bounds its execution; wait does not add a competing wall-clock timeout. There is no field for whether the holder thinks it passed. A job that verifies is recorded pass. Exit 1 is a status, naming every rule that failed; exit 2 is magus unable to answer, which is a result that would not decode, nothing filed and nothing piped in, or a job that would not write. Whether the work is GOOD stays the reading of whoever forked it.
run submits one of the server's own jobs, the housekeeping magus does for itself, and returns. It is a no-op when no server is running, so a VCS hook can call it unconditionally.
apply upserts jobs' specs from -f <file|->, the way kubectl apply reads a manifest: the record is the whole spec, and the job keeps its state, holder and registration, where a re-fork would hand a taken job out again. Only the orchestrator widens; a holder may apply only the release of its own paths.
rm removes one row that should never have been written. prune ENDS every job nobody is working, as exit would abandon it, and keeps each row as the record.
Reading is elsewhere, on the verbs that read everywhere else: magus ls jobs lists them and magus describe job prints one job's terms.
job fork options
- --check <target> <project> [-- args]
- The one check this job runs, as `<target> <project> [-- args]` (the `magus run` is implied)
- --checkpoint magus vcs checkpoint -o name
- The working state this job is handed, as `magus vcs checkpoint -o name` prints it
- --criteria string
- What this job is for and what done means, as prose; the machine-checkable half is --check, and the goals a --stdin record declares
- --deny-paths string
- A path inside the write paths this job may not write; repeatable or comma-separated
- --depends-on string
- A job this one waits on; repeatable or comma-separated
- --model string
- The model the work was matched to
- --parent string
- The job this one is forked from
- --read-only
- A job that gathers evidence and writes nothing
- --read-paths string
- A path whose projects this job may read; repeatable or comma-separated (additive: the written paths are readable already)
- --schema
- Print the JSON schema a job must satisfy, and exit
- --stdin
- Read one job as JSON on stdin instead of taking it from flags
- --timeout duration
- Deny this job's writes once this long has passed since the fork (e.g. 45m, 2h); unset means no bound, unless magus.yaml sets jobs.default_timeout
- --write-paths <file>#<declaration>
- A path this job may write, or `<file>#<declaration>` to claim one declaration of a file (a path holding a literal # is spelled `./a#b.md` or `a\#b.md`); repeatable or comma-separated
job apply options
- -f string
- The records to apply: a file, or - for stdin; one JSON job, a JSON array, or one job per line
job exec options
- --base magus vcs checkpoint -o name
- The base this checkout landed on, as `magus vcs checkpoint -o name` prints it (default: read from this checkout)
job exit options
- --schema
- Print the JSON schema a result must satisfy, and exit
- --stdin
- Read this job's result from stdin; without it the job is abandoned
job wait options
- --integration
- Grade only the job's check goals against the runs the --stdin result names in THIS checkout, and record that beside its state
- --schema
- Print the JSON schema a result must satisfy, and exit
- --stdin
- Read the result from stdin instead of from the job, for one that was never filed
job rm options
- --force
- Remove a row that already ended, destroying the record of what happened
job prune options
- --all
- Also end a job a holder took and nobody touched within jobs.stale_after
Subcommands
- fork
- Declare one job, from flags or a JSON record on stdin
- apply
- Upsert jobs' specs from a file or stdin, keeping each job's state
- exec
- Take the lease on a job here, and record the base this checkout landed on
- exit
- Return a job with its result, or abandon it
- wait
- Verify the result a job was exited with
- watch
- Follow what a job's holder is doing, until interrupted
- run
- Submit one of the server's own jobs and return
- rm
- Remove one job from the plan
- prune
- End every job nobody is working
Examples
Declare a job
magus job fork session-load/core --write-paths internal/sessions/sessions.go --check 'test internal/sessions'
Declare it from a record
magus job fork --stdin < job.json
Take it in this checkout
magus job exec session-load/core
Return it with its result
magus job exit session-load/core --stdin < result.json
Verify what came back
magus job wait session-load/core
Print the result schema
magus job exit --schema
Submit a server job
magus job run sync-graph
Change a live job's spec, keeping its state
magus job apply -f job.json
Preview that change
magus job apply -f job.json --dry-run
Grade a job's checks in a merged tree
magus job wait session-load/core --integration --stdin < evidence.json
See which jobs a prune would end
magus job prune --dry-run
See Also
magus(1), magus-ls(1), magus-describe(1), magus-run(1), magus-x(1), magus-where(1), magus-affected(1), magus-graph(1), magus-query(1), magus-explain(1), magus-path(1), magus-refs(1), magus-watch(1), magus-events(1), magus-status(1), magus-clean(1), magus-shell(1), magus-vcs(1), magus-queue(1), magus-doctor(1), magus-config(1), magus-session(1), magus-notes(1), magus-diff(1), magus-server(1), magus-broker(1), magus-mcp(1), magus-buzz(1), magus-completion(1), magus-man(1), magus-init(1), magus-spell(1), magus-agent(1), magus-self(1), magus-version(1)