magus-job
Fork a job, take it, return it with its result, and verify that result
Synopsis
magus job <fork|exec|exit|wait|watch|run> [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 magus_job MCP tool 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 the lease on a job in this checkout. It writes the marker every lease-scoped rule reads, and 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. Two acts under one verb, because splitting them left the base unrecorded on every job anybody took by hand.
exec --vacate gives that marker up instead of writing one, so the checkout can exec a different job. Refused while the job is still declared or running, since walking away mid-flight would leave the checkout's next write ungraded; a job already exited, one the store no longer carries, or no binding at all, all vacate cleanly, which is what a checkout stuck on a lease nobody will ever wait on needs.
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 a recorded run of every declared completion gate that passed. 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.
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 the completion gates (--check and every --gate-* flag)
- --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
- --gate-check <id>=<target> <project>
- A further check this job must pass, as `<id>=<target> <project>`; repeatable
- --gate-paths <id>=<glob>[,<glob>...]
- Files this job must have CHANGED, as `<id>=<glob>[,<glob>...]`, proven against its checkpoint; repeatable
- --gate-paths-absent <id>=<glob>[,<glob>...]
- Files that must be GONE when the job is done, as `<id>=<glob>[,<glob>...]`; repeatable
- --gate-paths-present <id>=<glob>[,<glob>...]
- Files that must EXIST when the job is done, as `<id>=<glob>[,<glob>...]`; repeatable
- --gate-symbol <id>=<name>[,<name>...]
- Symbols whose definition this job must have CHANGED, as `<id>=<name>[,<name>...]`; repeatable
- --gate-symbol-absent <id>=<name>[,<name>...]
- Symbols that must resolve NOWHERE when the job is done, as `<id>=<name>[,<name>...]`; repeatable
- --gate-symbol-present <id>=<name>[,<name>...]
- Symbols that must resolve when the job is done, as `<id>=<name>[,<name>...]`; repeatable
- --gate-symbol-unreferenced <id>=<name>[,<name>...]
- Symbols nothing may reference when the job is done, as `<id>=<name>[,<name>...]`; repeatable
- --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 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)
- --session string
- The session taking the job, as this agent host names it. Several sessions in one checkout each hold their own lease; without it the binding is the whole checkout's
- --vacate
- Give up the lease this checkout holds, so a later exec can take a different one. A no-op if it holds none; refused while the job is declared or running
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
- --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
Subcommands
- fork
- Declare one job, from flags or a JSON record on stdin
- 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
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
Give up this checkout's binding
magus job exec --vacate
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
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-memory(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)