Install Bun 1.4.2, then run:
bun install --frozen-lockfileBun is a development dependency only. Published artifacts are smoke-tested under Node and do not require Bun at runtime.
bunfig.toml sets linker = "isolated". Do not change it. The hoisted linker
permits phantom dependencies, which would let core surfaces import adapters
without the dependency boundary catching it.
You do not need to start with the hardest surface.
Two areas have the steepest on-ramp:
- The CI Action bundle (
packages/ci/dist) must be rebuilt underlinux/amd64, or the byte-for-byte diff gate fails on a Mac-built bundle. - The schema parity gate requires the Zod source
(
packages/core/src/schema/adr.schema.ts) and the generatedschema/adr.schema.jsonto stay in lockstep.
Good first PRs usually start here instead:
- Docs - package READMEs, root docs, and site content.
- Fixtures - add corpus fixtures for edge cases the current tests miss.
- Tests - cover an untested branch and prove the new check really fires.
Open an issue or a Discussion if you want help finding a starting point.
All commits require a DCO sign-off. There is no CLA.
git commit -s -m "your message"The dco job checks every commit in the pull request. If you forgot to sign
off earlier commits, re-sign the branch and force-push:
git rebase --signoff origin/main
git push --force-with-leaseThe trailer must name you exactly:
Signed-off-by: Your Name <your@email>.
GitHub's web editor does not add a sign-off. For browser edits, clone the branch locally, run the rebase above, and force-push.
These rules are enforced in CI. If a pull request violates them, fix the code, not the check.
git clone <repo> && cd adrkit
bun install --frozen-lockfile
bun run typecheck && bun run build && bun test && bun run lintThe frozen install is the only step that may use the network, and it may
contact only the unauthenticated public package registry. It must use the
committed bun.lock and repository bunfig.toml, including the isolated
linker and minimumReleaseAge.
After installation, build, typecheck, test, lint, packaging, smoke tests, and runtime behavior must require no credentials, no services, and no network access. Do not add private registries, authenticated APIs, network-dependent tests, or anything that requires a managed device.
packages/core, packages/cli, and schema/ must not import from
packages/adapters/*.
Integrations are optional and separately versioned. The core and CLI are not.
When you add or tighten an assertion, lint rule, or CI gate, build an input it is supposed to reject and confirm it actually fails on that input. "The suite still passes" only proves the check does not crash.
Keep the failing input as a permanent negative case. That is the artifact that shows the check works.
Prefer asserting a specific observed value over a count or an absence. 0,
[], and "no X found" can mean either "looked and found nothing" or "never
looked at all." Where a count or absence really is the right assertion, the
negative case is mandatory.
This applies beyond tests. If you are claiming "X is not there," make sure you checked the only place it could be.
Three kinds of drift, three different mechanisms (ADR-0040):
- Derived inventory —
MANIFEST.md's record table is generated. Runbun run emit:manifest; never hand-edit the block between its markers. - Referential integrity —
bun run check:stale-refsfails when a document that speaks in the present tense cites a record that is no longer live without saying so. The two states acknowledge differently: a superseded record needs its successor named in the same paragraph or list item, because the successor is the only thing that tells a reader where to go next; a rejected or deprecated record has no successor, so its paragraph must carry the status word instead. Narration of history belongs indocs/adr/,CHANGELOG.md, orspecs/, which are deliberately not scanned. - Implementation claims — no tool can decide whether an ADR action item is done; that is a judgment about the tree. Before ticking a checkbox, verify the claim against the repository, and leave the item unchecked when the evidence does not support it. An ADR that claims work the tree does not contain is worse than one that claims nothing.
This repository runs its own governing-decisions Action against pull requests.
- Your pull request gets a bot comment listing the decisions that govern the changed files. It is updated in place on each push; it should not re-post as a second comment.
- Fork pull requests skip that job. A fork's
GITHUB_TOKENis read-only, so the Action correctly declines to comment. Dependabot pull requests skip it for the same reason.
If action-dogfood fails and you did not touch packages/ci,
scripts/check-ci-comment.ts, or .github/workflows/ci.yml, call that out on
the pull request instead of changing unrelated code.
This project governs itself. If your change contradicts an accepted record in
docs/adr/, the PR must include a superseding ADR with the argument, not
just a status flip.
To add a new decision:
adr new "Use X for Y"Before you create a new record, run adr explain on the paths you would place
in affects. If an accepted record already governs them, extending that record
or completing work it already called for may be the better fit.
When you do write a new record, describe the alternatives honestly. See docs/EVALUATOR_RUBRIC.md for the scoring rubric the project uses when reviewing proposals.
The Zod source of truth lives at packages/core/src/schema/adr.schema.ts.
schema/adr.schema.ts is a compatibility re-export, and
schema/adr.schema.json is generated.
Run:
bun run schema:emitThen commit the source change and the generated JSON. CI fails if they diverge.
Breaking schema changes require a major version and a migration path. Additive changes are minor.
Rubric changes are ADRs, not casual wording tweaks. Include the decision record with the change and, once calibration metrics exist, attach the calibration deltas. Keep the stated shipped-vs-design-target boundary accurate.
The @adrkit/ci Action ships a committed, self-contained bundle at
packages/ci/dist/index.js, and CI enforces that it matches source with a
git diff --exit-code packages/ci/dist gate.
Rebuild the bundle under linux/amd64 Bun 1.4.2, not on a Mac. Bun's
CommonJS interop output differs across host targets, so a Mac-built bundle
drifts and fails the gate. Use the pinned container:
docker run --rm --platform linux/amd64 -v "$PWD":/work -w /work oven/bun:1.4.2 \
bash -c "bun install --frozen-lockfile && bun run --filter='@adrkit/ci' build"
bun install --frozen-lockfile # restore your local (host) install afterwardThen commit packages/ci/dist. Any change to packages/ci/src or
@adrkit/core (which is bundled in) requires regenerating it.
A Dependabot bump of anything bundled into packages/ci/dist, or of zod,
arrives failing clean-clone-builds, because Dependabot does not rebuild
committed artifacts. You do not need the container for these. Apply the
regenerate-artifacts label and .github/workflows/regenerate-artifacts.yml
rebuilds the bundles and the schema on linux/amd64 from main's scripts and
pushes one commit to the pull request
(ADR-0041).
- It refuses anything that is not Dependabot's own pull request changing only
dependency fields and
bun.lock, and says why in the job log. - The label is removed after every run. Apply it again after Dependabot
rebases — the regeneration commit carries
[dependabot skip], so a rebase replaces it. - The pushed commit touches
packages/ci/**, so review the diff and applygate-change-acknowledgedas for any other change there.
The container instructions above remain the path for your own changes and the fallback if the workflow's App is unavailable.
- README.md - product overview and installation paths
- docs/adr/ - governing decisions
- docs/EVALUATOR_RUBRIC.md - proposal review rubric
- docs/RELEASING.md - release procedures