Skip to content

GitHub milestones and issues are generated from the canonical model, and a hand-edited generated region is reported rather than overwritten

scripts/github-sync renders each canonical record through majordomus plan body and projects it onto a GitHub milestone or issue: title, body region, milestone assignment, labels, and whether the issue is open. Comments, assignees and any text a human writes outside the generated region belong to GitHub and are never touched. A human edit *inside* the generated region is reported as drift and left alone.

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

The adapter lives in scripts/ because the tool itself makes no network call, which test/cases/08_no_forbidden_constructs.sh proves. The begin marker carries the hash of the record it was generated from, which is what separates a moved plan from a hand edit; a second marker carries the record's identity, so a title a person rewrites cannot orphan it. This claim is about what the adapter renders and refuses. Whether the remote has actually received it is a different question, and github-projection-gated is the claim that answers it.

What it means

scripts/github-sync renders each canonical record through majordomus plan body and projects it onto a GitHub milestone or issue: title, body region, milestone assignment, labels, and whether the issue is open. Comments, assignees and any text a human writes outside the generated region belong to GitHub and are never touched. A human edit inside the generated region is reported as drift and left alone.

How it works

The body is spliced between <!-- majordomus:begin <hash> --> and <!-- majordomus:end -->, the same region mechanism that lets Majordomus share a CLAUDE.md with a hand-written one. The hash in the begin marker is the hash of the canonical record the region was generated from, which is what separates the two cases: a different hash means the plan moved and the region is refreshed; the same hash with different content means a person edited it, and --apply refuses without --force.

The adapter lives in scripts/ rather than in lib/, because bin/, lib/, share/ and test/ contain no network client and test/cases/08_no_forbidden_constructs.sh proves it. The model it projects comes from the same lib/project.sh the CLI uses.

How to see it

scripts/github-sync --plan        # offline: what would be created or changed, and the body hashes
scripts/github-sync --check       # compare against the live repository; exit 11 on drift
scripts/github-sync --apply       # create what is missing, update what canonically changed

What it does not cover

Nothing is read back. Closing an issue on GitHub does not complete it canonically; the next --check reports the disagreement and the canonical record wins. There is no mapping file either — a record is matched by the id that prefixes its title, so renaming that prefix on GitHub orphans the issue.

Why it exists

GitHub is where the conversation happens and a poor place for the plan to live: it has no dependency graph, no validation, and no way to refuse a status that contradicts one. Making it a projection keeps the collaboration and moves the truth into the repository.

Detail rendered from docs/claims/github-projection.md.

Provenance

defined in
read it on this site · docs/PLANNING.md
implemented in
scripts/github-sync
proved by
test/cases/45_github_projection.sh
claim id
github-projection

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 45_github_projection

Where this claim is used

responsibilities it covers

The moments this answers