Skip to content

Latest commit

 

History

History
234 lines (169 loc) · 9.13 KB

CONTRIBUTING.md

File metadata and controls

234 lines (169 loc) · 9.13 KB

Contributing to adrkit

Toolchain

Install Bun 1.4.2, then run:

bun install --frozen-lockfile

Bun 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.

Good first contributions

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 under linux/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 generated schema/adr.schema.json to 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.

Sign-off is required

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-lease

The 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.

Quality bar

These rules are enforced in CI. If a pull request violates them, fix the code, not the check.

1. A clean clone gets one narrow network exception

git clone <repo> && cd adrkit
bun install --frozen-lockfile
bun run typecheck && bun run build && bun test && bun run lint

The 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.

2. Core surfaces do not depend on adapters

packages/core, packages/cli, and schema/ must not import from packages/adapters/*.

Integrations are optional and separately versioned. The core and CLI are not.

3. A new check should fail before you trust it

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.

4. Derived state is generated; claims about the tree are verified by you

Three kinds of drift, three different mechanisms (ADR-0040):

  • Derived inventory — MANIFEST.md's record table is generated. Run bun run emit:manifest; never hand-edit the block between its markers.
  • Referential integrity — bun run check:stale-refs fails 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 in docs/adr/, CHANGELOG.md, or specs/, 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.

What to expect from CI on your PR

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_TOKEN is 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.

Changing a decision

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.

Changing the schema

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:emit

Then 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.

Changing the evaluator rubric

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.

Changing the CI Action (packages/ci)

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 afterward

Then commit packages/ci/dist. Any change to packages/ci/src or @adrkit/core (which is bundled in) requires regenerating it.

Dependabot pull requests: the regenerate-artifacts label

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 apply gate-change-acknowledged as 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.

Architecture and governance links