An open-source project kit for git and Claude Code
Every project leaves a stone for the next one.
Morrowcairn is what a new project starts with: record templates, patterns with the safeguard built in, leak checks that refuse, and working rules for Claude Code sessions. One person built it while directing a crew of sessions, so each new project would start with what the last one learned.
- MIT-0
- Release v0.10.0
- Since July 2026
- Linux and Mac
mor·row·cairn
/ˈmɒr.oʊ.kɛərn/ · noun
- A pile of stones raised by someone who walked the path, to mark the way for whoever walks it tomorrow.
- A kit that hands each new project what the last one learned, at the moment it applies.
Why it exists
Lessons written down get missed at the moment they matter.
“Most ‘lessons learned’ documents fail the same way: the knowledge is written down, and then the next person — or the next agent — writes the bug anyway.”
The case that started it
-
01
Half the machinery
A sibling project took part of the record-keeping. It kept a decisions log and never started an incident log.
-
02
Months later
Turning its experience into rules needed exactly the incident history it had never kept.
-
03
Not recoverable
The record was never made, so it couldn't be rebuilt. The gap had become a shape the process adapted to.
“The machinery is cheap at commit one and expensive at month six.”
So the kit delivers each lesson when it applies: starting points with the safeguard already built in, checks that refuse, and rules loaded into every session.
Who it's built for
One person, directing a crew of Claude Code sessions.
-
You decide
Purpose, priorities and the calls only you can make. Each one goes into the decisions log with its reasons.
-
Sessions do the work
From written briefs, every session running the same rules, loaded from the same release.
-
The record carries it forward
Decisions, incidents and follow-ups land in append-only logs, so the next session, or the next project, starts from what's known.
That's the setup it was built in. If yours looks like it, take what helps.
What a project starts with
Five stones, stacked.
Top to bottom, the way it stands: the rules sessions load on top, the record everything rests on underneath. Open any stone for the detail.
-
Stone 5 · capstone
Session rules
Five rule files Claude Code loads from the installed release. Two load in every session: working from a brief, and running shell commands. Three load when a task calls for them: changing a gate, stamping a record with the time, and closing a session.
More on the rules
- The rules load once per machine, from the one release that machine's setup names. A project's pin picks the code it runs, never the rules.
- A daily load check reads each session's own record of what it loaded, and raises an alert when a session that loaded the machine's setup missed a rule file it imports, or when no session on a Claude Code version loaded the setup at all.
- A closing routine has a session bring its records up to date and write a handoff before it ends, so the next session starts from what's known.
- Claims that something is absent need proof: a search whose exit status shows nothing matched, run in a form that reads every byte of every file.
-
Stone 4 · CI on the remote
Checks
Once a project has a remote, CI scans again where the code lands: the whole tree for credential shapes, plus a marker scan that names no one. It runs even when a local hook was skipped. Your identity list stays on your machine: CI only ever gets terms that identify no one.
-
Stone 3 · commit and push hooks
Guards
Every commit is scanned for credentials, and every push for credentials and your own identity terms: in files, in binaries, and in commit and tag metadata. The first commit waits for the stakes frame. When a hook can't run its checks, it refuses.
What each hook checks
At commit
- Credentials in what's staged, with gitleaks.
- A placeholder author email is refused.
- A project's first commit waits until its stakes frame is filled in.
At push
- Credentials, again, in the commits the push adds.
- Your identity terms in every file of the tree at each tip you push, every line read as text whatever bytes it holds.
- Images and video pass only once reviewed, and only if they carry no metadata.
- Author, committer and message on every commit and tag, read without git's name-rewriting file, so a .mailmap can't mask a name.
When something is wrong
- No gitleaks, no identity list, or a list that doesn't match its pinned hash: the push is refused.
- Python runs isolated, so a module planted on your path can't run inside a check.
- A check that can't run counts as a failure, never as a pass.
-
Stone 2 · starting points
Patterns
Copy-paste starting points with the safeguard already built in, each naming the failure it prevents.
- dry-run gated runner
- atomic deploy
- counted-skip loader
- repo genesis
- local spawn
- local upgrade
- numbered relays
-
Stone 1 · foundation
Records
Four logs, numbered and append-only: decisions with their reasons and the alternatives that lost; incidents with how each fix was verified; a follow-up queue sorted by what happens if an item never gets done; and kit feedback, written at the moment of friction. Corrections are dated notes, never quiet edits.
Day one
Before the first commit, a project answers four questions.
The answers set how careful everything after them has to be. The first commit is refused while they're blank, and the last question names the change that means it's time to answer again.
The stakes frame
- 1Who acts on this project's outputs: no one yet, one person, or many?
- 2What does a wrong output cost once it has been acted on?
- 3How long until anyone would notice it was wrong?
- 4What change would make this frame wrong (a first outside user, a first remote, money acted on for the first time)?
How it runs
Pinned, not copied.
Through v0.7.0 the kit was copied into every project, and the copies drifted apart. Since v0.8.0, each release installs once and each project points at the one it runs.
- 1
One install per release
Each release installs once per machine, read-only. If the install is edited, every commit and push stops.
- 2
Each project pins one
A small kit.pin file names the release and the hash of its file list. Before every commit and push, two stub hooks check the install file by file, then run that release's hooks.
- 3
Rules load per machine
Session rules load in every session from the one release your machine's setup names, whatever release a project pins. A project's pin picks code, never rules.
~/.local/share/morrowcairn/releases/ └── v0.10.0/ read-only, every file hashed your-project/ ├── kit.pin v0.10.0 + manifest hash └── hooks stub → check install → run v0.10.0 hooks every session machine setup → v0.10.0 rules
How it's tested
Every fix starts as a test that fails.
A hole gets a test that must refuse it, and that test runs against the release with the hole first, to prove it can fail. Then the fix. v0.10.0 shipped only after every suite passed on Linux and on Mac, a second session had checked it with its own tests, and the owner said go.
-
636
checks passed on macOS
Four suites: hooks 327, installer 204, docs 64, load check 41. One more case skips where ripgrep isn't installed.
-
2
platforms measured
Linux, and macOS. On the Mac, the hooks ran on Apple's own bash, grep, git and python3, and all four suites on Apple's grep alongside Homebrew's tools. Windows is in progress.
-
46
files in a release
Each one hashed in the release's manifest. Change one and every commit and push stops.
-
13
releases since July 21
From v0.1.0 to v0.10.0, each listed below with what it changed.
Check counts from v0.10.0's macOS suites run on GitHub's Mac machines, which have Xcode installed. Files are counted in the release's manifest; releases by their tags.
A release, start to finish
The hole v0.10.0 closed.
A real case, in the order it happened. It shows the method better than any claim about it.
- 1
Found
A term on a line that also held invalid UTF-8 could get past the push check. Testing the fix turned up a second gap: on a Mac, Apple's grep can miss plain text on a line that holds invalid UTF-8.
- 2
Proved
A test that must refuse that line was written and run against v0.9.0 first. It failed there, which shows it can fail.
- 3
Fixed
Pre-push and the CI scan now read every line as text, whatever bytes it holds, and every gate runs its text tools byte for byte, the same on Linux and Mac. Matching keeps your locale, so in a UTF-8 locale a term with an accented letter is still caught in either case.
- 4
Checked twice
All four suites passed on Linux and on GitHub's Mac machines. A separate session then ran its own cases: those for a term on an invalid-UTF-8 line, in pre-push and the CI tree scan, failed on v0.9.0 and passed on v0.10.0. The Mac and locale fixes rest on the kit's own tests. Two cases for the pin checker fail on both.
- 5
Released
Tagged on the owner's go, with the pin checker's two known gaps listed in the release notes for v0.11.0.
Everything else in v0.10.0
- Python starts isolated in every hook and check and in the installer, so a module planted on your path can't run inside them.
- Author and committer names are read without git's .mailmap, which could otherwise rewrite a name before the check sees it.
- A git setting that forces coloured output can no longer hide a credential from the scanner.
- A file whose name matches an identity term isn't printed in a refusal.
- A lint check stops the old search forms that missed these cases from coming back.
How the work is run
One person decides. Sessions do the work and measure it.
The kit was built the way it expects to be used. Here is the loop it's built in, so you can judge the kit by how it was made.
- 1
The owner makes the calls
Purpose, priorities, and anything that can't be undone. Each ruling goes on record in the owner's own words, with the date and where it was said.
- 2
A planning chat writes the brief
Each task goes out as a numbered relay: what's in scope and what isn't, a size estimate, and the conditions that stop the work.
- 3
A session does it and reports
A Claude Code session works from the relay and reports every claim as measured or inferred. If the work outgrows its estimate or a stop condition fires, it stops and asks.
- 4
The record carries it
Decisions, incidents and follow-ups land in the logs. When a session's memory fills, it writes a handoff and a fresh session picks up from it.
- 5
A second session checks
For v0.10.0, a separate session ran its own tests against the old release and the new one before the tag.
The numbered relay ships as one of the kit's patterns. The rest of this loop is how the kit itself was built. Nothing in the kit makes you work this way.
The trail · July to October 2026
Every project adds a stone.
Projects file kit feedback at the moment something chafes. When there's enough of it, it becomes the next release.
- v0.1.0Jul 21
Records machinery, patterns and two-layer leak detection. A first project was made from it within minutes.
- v0.1.1Jul 21
A credential canary, so both scanners are proven to fire.
- v0.2.0Sep 23
Hooks fail closed. A push scans exactly the commits it adds. Checks on a project's first commit.
- v0.2.1Sep 25
Hook tests travel with each project. A file manifest and an upgrade pattern.
- v0.3.0Sep 26
An upgrade stops on a kit file a project deleted that the kit has since changed.
- v0.4.0Sep 26
Reviewed, metadata-free images can be pushed. Projects can add their own hook checks.
- v0.4.1Sep 27
A patch: upgrade-test fixes, and a note that binaries are checked at push.
- v0.5.0Sep 29
Reviewed, structurally plain MP4 and WebM video can be pushed.
- v0.6.0Oct 2
Identity terms refused in commit and tag metadata. The first session rules.
- v0.7.0Oct 4
The identity list is read as data, never run.
- v0.8.0Oct 7
One read-only install per release, pinned by each project. Session rules load once per machine.
- v0.9.0Oct 18
Mac support, measured on GitHub's Mac machines. The identity list is checked against its pinned hash. A placeholder author email is refused at commit.
- v0.10.0Oct 20
Every line is read as text whatever bytes it holds, closing a gap for lines with invalid UTF-8. Python runs isolated. Hooks measured on a stock Mac's own bash, grep and python3.
Before you clone
Built for one setup. Here's what yours needs.
Morrowcairn runs on Linux and Mac today, and Windows support is being built and tested on GitHub's Windows machines. In the spirit of the stakes frame, here's what it assumes.
Linux or Mac Windows in progress
The hooks, the installer and all four test suites pass on Linux and on GitHub's Mac machines, which have Xcode installed. A Mac with only Apple's Command Line Tools hasn't been measured yet. On Windows, line endings and some tests are still being fixed. This page will say when it passes.
bash, git 2.31+, python3, gitleaks
Python 3.9 and 3.12 are the versions measured. gitleaks has to be on your PATH; without it, the hooks refuse. The installer also needs sha256sum or shasum.
Your own identity list
Each clone keeps its own list of terms that must never leave it. Pushes refuse until it exists.
For CI: GitHub and one secret
The workflows run on GitHub and read a LEAK_PATTERN repository secret holding only terms that identify no one.
Session rules, wired in once
They load through your user-level Claude Code setup. A setup guide comes with the public release.
What it won't catch
A net, not a guarantee.
Every check has edges. These are the ones known today, so you can decide what else your project needs.
- Hooks run per clone, and git's --no-verify skips them. CI still scans for credentials, but identity terms are checked only by the local hook.
- CI reads the tree a push lands, not the history behind it. File names, and text a later commit removed, aren't scanned for identity terms.
- A term written in an encoded form, such as an HTML entity, isn't recognized.
- The hooks trust the shell they run in. A tampered environment, such as a fake tool placed early on your PATH, can get past them. Hardening is planned for v0.11.0.
- The pin checker has two known gaps, planned for v0.11.0: it reports a broken identity list as working, though pre-push then refuses every push; and an inherited GIT_DIR setting can point it at another repository's list and pin.
What's next
The next stones.
-
Next release · v0.11.0
Windows, and a tougher shell
Windows support, hooks that clean their own environment before they run, and fixes for the pin checker's two known gaps.
-
Being designed
An install route for teams
The kit's code installed once per machine, plus a short rules file that each person's Claude Code imports. Whether an administrator can set it for everyone at once is still an open question. Not built or tested yet.
-
Then
The first public release
The repository opens, with a setup guide written for someone who has never seen the kit.
Open source
Take what helps.
Morrowcairn is licensed MIT-0. Use it, change it and ship it inside anything you like. No credit is required, though if it helped, a mention of Morrowcairn is appreciated.
It's built for one person's projects first and offered as is, with no warranty of any kind: not that it works, not that it fits your use, and not that it infringes no one's rights. You use it at your own risk.
A plain-language summary. The LICENSE file governs.
The repository opens with the first public release.
The kit is being readied for anyone's machine first: Windows support, a setup guide, and a fresh public copy. The link goes here when it's live.
MIT-0 · no account needed to read it