Skip to content

An obligation whose fact the tool can hold is established live rather than recorded — a clean tree, a remote-tracking ref that reaches the head, a trunk that reaches it, a published site that serves it — and a hand-recorded line neither discharges it nor rescues it

Six of the eleven obligation tokens name a fact that lives outside the working tree:

guaranteed Deterministic and blocking. Implemented, and a behavioural test proves it.

share/obligations.yaml gives every token an established_by, and one of `none` obliges it to say in `unestablished` why nothing here can settle it. commit, push and target are settled from git without fetching, so a stale tracking ref can only say "not yet" and never "yes"; pages is settled by scripts/pages verify with --timeout 0, the single probe of the publication check the Pages workflow already runs, which now exits 12 rather than 10 when the site never answered — unreachable and unpublished are different findings. A fact established this way is taken at HEAD, which is mj_git_label's `exact`; the other three labels keep their meaning on the fallback path, and deploy and verify stay hand-recorded because a deployment is a fact about a machine this repository never contacts.

What it means

Six of the eleven obligation tokens name a fact that lives outside the working tree: commit, push, target, pages, deploy, verify. Until this landed, all six were discharged the same way — a person ran something, or said they had, and typed majordomus evidence --covers push --command 'git push'. The ledger held the sentence. Git held the answer, and nobody asked it.

Four of the six are now asked rather than recorded:

tokenthe facthow it is settled
committhe task's changes are in the branch's history, not the working treeno path inside the task's scope is dirty, and HEAD has moved past the commit the task started from
pushthe branch head exists on the remote it tracksa remote-tracking ref reaches HEAD
targetthe repository's default branch reaches the commitrefs/remotes/<remote>/HEAD reaches HEAD
pagesthe published site serves this commitscripts/pages verify --timeout 0 — one probe of the identity document the site publishes

deploy and verify stay hand-recorded, and the vocabulary says why in one line rather than leaving a reader to infer it.

An established obligation discharges by being true and refuses by being false. Recording evidence for one has no effect in either direction: a task.evidence line saying push against a branch the remote has never seen does not survive contact with git.

How it works

share/obligations.yaml gives every token an established_bygit, a command, or none. A token whose value is none must also carry unestablished, one line saying why nothing here can settle it; the behavioural case fails if a token declares one without the other, in either direction, because a token declared establishable with nothing to establish it would otherwise pass silently.

mj_validate_obligations in lib/evidence.sh asks mj_obligation_establish first. That dispatches to mj_obl_est_<token>, which exits 0 established, 1 refuted, or 2 undecidable — nothing in this checkout could settle it. Only the third case reads the ledger, and it then behaves exactly as it did before this existed.

Nothing fetches. The git tokens read the remote-tracking refs as they stand, because a validator that went to the network on every check would make a diagnostic depend on connectivity. The reading is safe in the direction that matters: a tracking ref cannot contain a commit the remote never received, so a stale ref can only say "not yet" when the answer is "yes" — never the reverse.

pages calls the probe that already existed. scripts/pages verify has polled the published site's identity document for the commit it names since the Pages fast path was written, and until now the Pages workflow was its only caller. Two things made it callable from a validator: --timeout 0 is a single probe, because the loop reads the site before it looks at the clock; and each probe is now bounded by --max-time, so an unreachable host costs a known number of seconds. It also tells its two failures apart now. A site that answers and names another commit is a refusal, exit 10 — this work is not published. A site that never answered was not measured, exit 12 — the check could not be made, and a laptop with no network must not be able to fail a task it cannot see.

Why it is live rather than recorded

Both are defensible and the alternative is worth stating. A ledger line records when the fact was true, which is what somebody auditing the history a month later wants, and it keeps one mechanism instead of two.

Live wins on the argument this whole subsystem rests on. The obligation ledger exists because a record outlives the thing it described, and the cure it applies everywhere else is recomputation, never a timestamp: mj_obligation_inputs_hash recomputes, the site's source_hash recomputes. A git fact recomputes in milliseconds and is always a statement about now. Recording it would manufacture exactly the staleness this subsystem was written to remove, and then need the staleness machinery to take it away again — a round trip whose only product is a window during which the record and the repository disagree.

And the ledger already holds what a worker did: every start, checkpoint, evidence and finish is in it, with the head, the branch and the session in the envelope. It is not where the tool writes down what it can look up. A hand-recorded remote fact is also the one kind of evidence a worker can be wrong about in the direction that flatters them.

The staleness discipline is kept rather than dropped. A fact established here is taken at HEAD by construction, which is mj_git_label's exact, and exact is the word the finding says. advanced, diverged and different_context keep their meaning on the fallback path. No fifth vocabulary was invented.

How to see it

majordomus check
# OK   obligation  commit — exact: the tree is clean and 1bcd0b3790c5 carries the task's changes
# OK   obligation  push   — exact: origin/master contains 1bcd0b3790c5
# OK   obligation  target — exact: origin/master reaches 1bcd0b3790c5

git commit -qm 'one more'
majordomus finish --outcome completed --note done
# FAIL obligation  push   — no remote-tracking ref reaches 9c8a74105c8c; the commit has not
#                           reached the remote
# FAIL obligation  target — origin/master does not reach 9c8a74105c8c; the work is not on
#                           the trunk

majordomus evidence --covers push --command 'git push'   # recorded, and it changes nothing
majordomus finish --outcome completed --note done
# FAIL obligation  push   — no remote-tracking ref reaches 9c8a74105c8c; ...

What it does not cover

deploy and verify are still discharged by a person, deliberately. majordomus deployment reads the declared deployment object and contacts nothing — no capability in apps/majordomus-cli/src/capability/builtin/deploy.rs reaches the provider, which is a rule of that module and not an oversight — and the object it reads is declared rather than running, so there is no host to ask. A check that always passed would be worse than a gap that is written down.

Nor are the local tokens established. tests could only be settled by running the suite, which would make check cost what test/run.sh costs. generated could be settled by majordomus generate --check in about twelve seconds, and docs by scripts/ci/reference-check; neither is wired, because what a diagnostic may spend on every invocation has not been decided. Both say so in unestablished, which is where this repository puts a gap it has not closed.

An established commit also does not judge whether the committed change is the change the task promised. That is implementation, and it is a judgement, not a fact.

Detail rendered from docs/claims/obligation-establishment.md.

Provenance

defined in
read it on this site · docs/CLI.md
implemented in
lib/evidence.sh · majordomus evidence
proved by
test/cases/103_obligations.sh
claim id
obligation-establishment

Verify it yourself

The test runs in a disposable temporary repository and asserts the behaviour, not a string in the source.

from a clone of the repository
bash test/run.sh 103_obligations

Where this claim is used

responsibilities it covers

Related claims same implementation