Splitting other work
The jobs guide splits a code change. The same commands split any
work whose pieces are files: each job names the files it may write, so two people
cannot quietly take the same slice, and magus job wait tells the person keeping
the list whether a piece is done. Three examples, each verified the way the first
page's are: the transcripts are compared byte for byte by
cmd/magus/testdata/script/job_people_*.txtar.
A dependency upgrade, one project at a time
A shop has two projects, api and web, each pinning a library in its own
deps.txt and each with its own test target. The upgrade splits one job per
project, declared together in one stream:
$ magus job apply -f - <<'EOF'
{"schema_version": 11, "id": "upgrade/api", "criteria": "api runs left-pad 1.3", "write_paths": ["api/deps.txt"], "check": {"target": "test", "project": "api"}}
{"schema_version": 11, "id": "upgrade/web", "criteria": "web runs left-pad 1.3", "write_paths": ["web/deps.txt"], "check": {"target": "test", "project": "web"}}
EOF
created upgrade/api, declared, with 1 write path(s)
model tier: standard (rank 1), default: "the declared default" at magus/modeltiers
created upgrade/web, declared, with 1 write path(s)
model tier: standard (rank 1), default: "the declared default" at magus/modeltiers
Each job's check is that project's own test, so api passing says nothing about
web. Ana takes upgrade/api in her worktree and Ben takes upgrade/web in his,
each with magus job exec. Who holds what, from anywhere:
$ magus ls jobs -o template='{{range .jobs}}{{.id}} {{.checkout_root}}{{"\n"}}{{end}}'
upgrade/api ~/src/ana
upgrade/web ~/src/ben
Each bumps the pin, runs magus run test api or magus run test web, and exits the
job with the run's ref, as on the first page. Whoever keeps the list checks each
piece:
$ magus job wait upgrade/api
verified upgrade/api, recorded pass
its holder reports it ran magus run test api
goal check: verified (out<hex>)
footprint: where its diff since the checkpoint landed
api/deps.txt:1-1
and the whole set is done when every row reads pass:
for job in upgrade/api upgrade/web; do
magus job wait "$job" > /dev/null || echo "$job is not done"
done
$ magus ls jobs --all
JOB HOLDER STATE MODEL PATHS PROOF CHECK
upgrade/api ana pass standard 1 alone magus run test api
upgrade/web ben pass standard 1 disjoint magus run test web
in flight: never fetched; `magus queue ls --provider <provider> --base <branch>` reads the open changes
A job holding upgrade/api that also changed web's deps.txt would not pass: wait
holds every changed line to the job's write paths.
Translating pages without sharing one
Two translators divide a docs site by page. The repository marks Markdown with git's Markdown diff driver, which is what lets magus see headings inside a page:
*.md diff=markdown
Ana forks fr/install on the French install page and takes it in her worktree. Ben,
in his, claims the same page for the section he wants:
$ magus job fork fr/upgrading --criteria 'upgrading section in French' --write-paths docs/fr/install.md --check 'lint .'
[error] 1 problem:
1. [MGS3032] write_paths[0]: fr/upgrading declares "docs/fr/install.md", and fr/install (running, updated 0s ago) already holds "docs/fr/install.md". Both are live and neither is the other's parent, child, or depends_on partner, so docs/fr/install.md has one owner unless the rows say otherwise. fr/install has touched none yet in it so far. fr/install has changed nothing under docs/fr/install.md
fix: Claim `docs/fr/install.md#<declaration>` on both rows, add `--depends-on fr/install` to fr/upgrading, or fold fr/upgrading into fr/install; or the root narrows fr/install to what it has changed with `magus job apply -f <record>`, dropping docs/fr/install.md
Refused, with no row written: the page is held in another checkout, so it has one owner until the rows say otherwise. The refusal names the ways out. They split the page by heading, which narrows Ana's job and declares Ben's in one stream:
$ magus job apply -f - <<'EOF'
{"schema_version": 11, "id": "fr/install", "criteria": "install page in French", "write_paths": ["docs/fr/install.md#Installer"], "check": {"target": "lint", "project": "."}}
{"schema_version": 11, "id": "fr/upgrading", "criteria": "upgrading section in French", "write_paths": ["docs/fr/install.md#Mettre à jour"], "check": {"target": "lint", "project": "."}}
EOF
updated fr/install, still running:
write_paths +docs/fr/install.md#Installer -docs/fr/install.md
created fr/upgrading, declared, with 1 write path(s)
model tier: standard (rank 1), default: "the declared default" at magus/modeltiers
The part after # names a heading the diff driver matches. Once Ben takes his job,
the board shows the shared page and why it is not a conflict:
$ magus ls jobs
JOB HOLDER STATE MODEL PATHS PROOF CHECK
fr/install ana running standard 1 alone magus run lint .
fr/upgrading ben running standard 1 disjoint magus run lint .
overlaps
fr/install and fr/upgrading claim common ground
fr/install: docs/fr/install.md#Installer
fr/upgrading: docs/fr/install.md#Mettre à jour
claims: disjoint (different declarations of one file: an integration order, not a wait)
footprints: disjoint
in flight: never fetched; `magus queue ls --provider <provider> --base <branch>` reads the open changes
When each exits, magus job wait places every changed line under the heading it
landed in, and a line in the other translator's section is named as a violation.
A release checklist
Each item on a release checklist is a job whose goal says what done means. A goal
needs no target: paths goals read the diff and the tree.
$ magus job apply -f - <<'EOF'
{"schema_version": 11, "id": "release-1.3/changelog", "criteria": "CHANGELOG.md has a 1.3 section", "write_paths": ["CHANGELOG.md"], "goals": [{"id": "changelog", "kind": "paths", "expect": "changed", "paths": ["CHANGELOG.md"]}]}
{"schema_version": 11, "id": "release-1.3/notes", "criteria": "release notes exist for 1.3", "write_paths": ["notes/1.3.md"], "goals": [{"id": "notes", "kind": "paths", "expect": "present", "paths": ["notes/1.3.md"]}]}
EOF
created release-1.3/changelog, declared, with 1 write path(s)
model tier: standard (rank 1), default: "the declared default" at magus/modeltiers
created release-1.3/notes, declared, with 1 write path(s)
model tier: standard (rank 1), default: "the declared default" at magus/modeltiers
Two people take an item each in their own worktree. The changelog gets written. The notes are blocked on numbers that do not exist yet, so their holder says so on the way out instead of pretending:
$ magus job exit release-1.3/notes --stdin <<'EOF'
{"schema_version": 2, "changed_paths": [], "unresolved_risks": ["the notes need the final benchmark numbers"]}
EOF
exited release-1.3/notes, recorded exited, with its result filed. Whoever forked it verifies with `magus job wait release-1.3/notes`
The board says both items came back, and nothing more:
$ magus ls jobs
JOB HOLDER STATE MODEL PATHS PROOF CHECK
release-1.3/changelog changelog exited standard 1 alone -
release-1.3/notes notes exited standard 1 disjoint -
in flight: never fetched; `magus queue ls --provider <provider> --base <branch>` reads the open changes
Coming back is not being done. The person cutting the release checks each item:
$ magus job wait release-1.3/changelog
verified release-1.3/changelog, recorded pass
goal changelog: verified
footprint: where its diff since the checkpoint landed
CHANGELOG.md:3-6
$ magus job wait release-1.3/notes
rejected release-1.3/notes, and its state is unchanged
job release-1.3/notes is not read-only and the result claims no changed paths at all
nothing in the diff since 5f6b136f2721af3fb8a117fbf824b24a3a78b059 is inside its write paths (notes/1.3.md)
goal "notes": nothing matching "notes/1.3.md" is in the tree, so this goal is unmet
goal notes: rejected
footprint: no line changed since the checkpoint
unresolved risks its holder reported
the notes need the final benchmark numbers
Exit 1, every unmet rule named, and the risk repeated where the person deciding reads it. The board with its ended rows is the checklist:
$ magus ls jobs --all
JOB HOLDER STATE MODEL PATHS PROOF CHECK
release-1.3/changelog changelog pass standard 1 alone -
release-1.3/notes notes exited standard 1 disjoint -
in flight: never fetched; `magus queue ls --provider <provider> --base <branch>` reads the open changes
The release waits until every row reads pass. An item nobody finishes ends with
magus job exit <job>, recorded no_return, so the board never shows a skipped item
as a finished one.