Pin your docs to the truth so they can’t drift.
Yakir — Ukrainian for anchor — is a drift-prevention framework: it tracks every place the same fact lives across code, docs and generated files, and reconciles them when one changes. It’s for anyone maintaining a repository, or a monorepo, where the same version, API name or measured number is quoted in more than one place.
53
tests passing
0
runtime dependencies
2/5
roadmap milestones shipped
{ "id": "package-version", "tier": "token", "policy": { "severity": "block", "mode": "auto" }, "sites": [ { "artifact": "package.json", "locator": { "kind": "json-pointer", "path": "/version" }, "write": "manual" }, { "artifact": "README.md", "locator": { "kind": "region", "name": "version" }, "write": "managed" } ]}At a glance
- Type
- My own open-source library
- Scope
- Everything: the tether/site/fingerprint engine, the check/fix/accept/init/discover CLI, and the test suite — dogfooded against StitchAPI, another of my projects.
- Status
- In use — in StitchAPI’s repo, not released publicly · as of 24 Sep 2026
- Timeline
Started Jun 2026 · not released yet
- Platforms
- Node.js 18+
- Source code
- Source on GitHub (opens in a new tab)
- Try it
- Not on npm yet — the CLI runs from a clone of the repo.
- Stack
The challenge
The same fact often lives in more than one artefact — a version number, an API option name, a generated file — with no live binding between the copies. Each one is edited or regenerated on its own, so they drift apart quietly. Worse, you can’t continuously prove that two prose artefacts still agree; the best you can do is notice the moment one of them changes.
- A release moves the version in package.json; nothing forces the README badge to move with it.
- An API option gets renamed in the source; three doc pages and a blog post keep the old name.
- A generated file like llms.txt quietly falls behind the source it was generated from.
- You can’t continuously prove that two prose artefacts still agree — only notice the moment one of them changes.
- Two sites can each drift to a different new value, and nothing says which one is now true.
The constraints
A few limits here are deliberate, not accidental: yakir never runs code it wasn’t told to, never writes into a site that hasn’t earned that trust, and — in v1 — watches one repository at a time.
- Zero runtime dependencies — the engine has to stay small and embeddable.
- A command site runs arbitrary shell, so it’s declared-only: discover never proposes or runs one.
- Every site defaults to manual (read-only); it only becomes managed — auto-writable — once trusted.
- A measured value, a set, or a generated file is detect-and-report only — never rewritten in place.
- v1 watches one repository at a time, in-repo files only; cross-repo and repo-to-world watching is a later layer.
The solution
I modelled every fact as a tether over co-equal sites, with no single site called “the source” — each one carries a fingerprint, and comparing fingerprints to the last baseline is what tells yakir which site actually moved. Facts are guarded at the cheapest tier that fits: a literal token, a value measured by running a command, or eventually a diff-aware judgement. Reconciliation only ever writes into a site that has earned that trust.
No site is “the source” — a tether binds co-equal sites.
A single source of truth doesn’t exist across artefacts you can’t import between; per-site fingerprints reveal which site actually moved, which is what makes “the newest value is the truth” computable without ever naming one site as canonical.
Guard each fact at the cheapest tier that fits.
Most drift is a literal value that only needs comparing. A few facts need something to actually run — a bundle measured, a fence type-checked — and only what’s left over needs a real judgement call.
Auto-write is a capability a site earns, not a property of a tether.
Defaulting every site to read-only and promoting it only once the binding is trusted means a typo in a generated badge can never corrupt package.json. It’s the same “earn auto-fix” ratchet used for counted lint suppressions.
A command site is declared-only.
A command site runs arbitrary shell, so letting a scanner invent and execute one automatically would be a real security hole. A human has to write it into the manifest first.
Measured facts and generated files are detect-and-report, never rewritten in place.
A measurement, a set of quotes, or a generated file has no single “winning” literal to copy in. yakir surfaces the mismatch and lets a human, or the real generator, make the fix.
Architecture
yakir.json manifest
Declares tethers — id, tier, sites, policy
Locators
json-pointer, region, pattern, file, command
Extract & fingerprint
Reads and hashes each site’s current value
Expand globs
One site per matching file, across a monorepo
Reconcile
Compares to the baseline — fresh, suspect or drifted
check / fix / accept / init
The CI gate — read-only, or apply the fix
discover
Proposes candidate tethers; never auto-fixes
yakir.lock
Committed baseline + per-site fingerprints
I build — you own
Built to hand over
Yakir’s repository is public, so I can point at a handover instead of describing one — every item below is a real link into it, not a summary of it.
- View (opens in a new tab)
Source and licence
Apache-2.0-licensed — 11 source files in src/, all one author.
- View (opens in a new tab)
Design docs
docs/DESIGN.md, docs/manifest-sketch.md and docs/examples.md — the tether/site/fingerprint model and the file format, worked through in full.
- View (opens in a new tab)
Test suite
7 Vitest files, 53 tests green, covering the engine, discovery, reconciliation, expansion and locators.
- View (opens in a new tab)
Dogfood manifests
Two real yakir manifests running against StitchAPI, another of my projects, whose own repo now runs them directly — they catch an actual version lag and a stale bundle-size quote across five files.
Results
Milestones 1 and 2 are shipped: the engine, the full check / fix / accept / init / discover CLI, and both the token and executable tiers — 53 tests green, zero runtime dependencies. It’s dogfooded on StitchAPI, another of my projects, where it already catches a real version lag and a stale bundle-size quote across five files.
Apache-2.0
open-source licence
What’s next
Next is the semantic tier — a diff-aware judge that only runs when a source actually changes — plus type-checking twoslash fences and link resolution as new executable-tier runners. CI-gate hardening and a repo-to-world watching layer follow after that.
Have a similar project in mind?
I’ll build it and hand it over: the code, the docs and the accounts are yours.
Discuss a project