Files
tiny-pattern-ts/CONTRIBUTING.md
T
tmu 84d48c6e67 📝 Make the rule/rationale split explicit
AGENTS.md and CONTRIBUTING.md now state that a decision, its rejected
alternatives and its known issues belong in development/<category>.md, that the
actionable rule stays in CONTRIBUTING.md, and that both change in the same
commit. development/README.md names library.md in the category list and spells
out that the sync runs in both directions.
2026-09-15 13:39:58 +00:00

11 KiB

Contributing

This document is for maintainers and contributors working on the project itself. End-user documentation is in README.md. The reasons behind the rules here — the decisions, rejected alternatives, and known issues — live in development/. The machine entry point for AI coding agents is AGENTS.md; keep this file as the prose home for the rules so agents and humans don't diverge.

Setup

  1. Clone the repository.
  2. Install Node.js >= 26 — see .node-version; the exact pinned version is what CI and the runner image use.
  3. npm install.
  4. npm run setup — the one-time clone configuration (currently registers the commit-message template).

Development commands

  • Build: npm run build
  • Test: npm run test, npm run test:ci
  • Watch: npm run watch
  • Checks: npm run check, npm run fix
  • Verify: npm run verify — the definition of done
  • Maintenance: npm run maintain — advisory only
  • Individual fixes: npm run fix:oxfmt, npm run fix:oxlint

Feedback tiers

The tools are organized into a feedback ladder. Each tier catches different things at different costs; the rule of thumb is "earlier tiers fire more often, faster tiers catch less, slower tiers are more thorough":

Tier When What it runs Time
npm run watch manual watch:test — re-runs tests on file save ~0.1s
Pre-commit (auto) on stage tsc + oxlint + oxfmt + cspell (staged files only) ~1.3s
Pre-push (auto) on push npm test (full tsc + unit tests) ~3.5s
npm run check manual Correctness gates: tsc + oxlint + oxfmt + cspell (whole project) ~3s
npm run verify manual Definition of done: npm run check + unit tests, one shot ~6s
npm run fix manual Auto-resolve fixable issues (lint, format) ~3s
npm run maintain manual / CI (advisory) maintain:knip + maintain:outdated (whole-project + network scans) ~10s
CI build (auto) on push to main / tag build job (build + correctness + packaging) — see .gitea/workflows/ci.yml ~30s+
CI maintain (auto, non-blocking) on push to main npm run maintain — reports, never fails the build ~10s
CI publish (auto) on tag packaging checks + publish:publint / publish:attw, then the Gitea release page and npm publish (skipped, and the job failed, without NPM_TOKEN) ~15s

Before pushing, run npm run verify — the one-shot correctness gate. Run npm run maintain only on a maintenance / update-deps branch. Why the splits are where they are: development/workflow.md § Feedback tiers.

Testing discipline (type-driven)

For this library the types are the feature, so the loop is type → red → green → refactor: write the expectTypeOf(...) assertion first, then the runtime assert.*, then the implementation. Every test pairs the two; keep them together. Type-first is enforced structurally: npm test runs check:tsc before the test runner, so a wrong type can never be papered over by a passing assertion. Per AGENTS.md § Never do, reach green honestly — fix the types, never suppress the checks you can't make pass. Full rationale: development/testing.md.

Code style and formatting

oxfmt is the formatter and oxlint is the linter (with type-aware rules). npm run fix resolves the fixable issues; npm run check verifies without writing. Suppressions must be fixed at the root — do not add oxlint-disable directives or as casts to force a green run (see AGENTS.md § Never do).

Suggested VSCode extensions are in .vscode/extensions.json; the project's formatter and linter are wired up there. Toolchain decisions: development/tooling.md.

Commit messages

Gitmoji subject, imperative mood, 50/72 wrapping. The template is commit-message-template; npm run setup (or npm run setup:git-commit-message) registers it as git's commit.template. Examples and rationale: development/workflow.md § Commit messages.

Script prefix convention

A new npm run script must reuse an existing prefix: create: / check: / fix: / test: / watch: / maintain: / publish: / setup:. If none fits, that's a signal the script doesn't belong in the pipeline — not a reason to invent a new prefix. If it genuinely does belong, add the prefix to the list here in the same commit as its first member; an undocumented prefix becomes invisible and quietly accrues members. Full convention and why create: exists: development/workflow.md § Script prefix convention.

Rules the tools don't enforce

CI and review will bounce these even though npm run check and the linters don't catch them. They're the high-frequency things a contributor (or an agent) reaches for by default:

  • Source imports use .ts extensions, never .js. node --strip-types only resolves the .ts form at test time; "pre-fixing" an import to .js breaks the inner loop. (why: development/tooling.md)
  • A new npm run script must reuse an existing prefix. See Script prefix convention.
  • oxlint-disable directives live in source, not .oxlintrc.json. The trade-off must sit next to the code it silences. This is a human last-resort convention; agents must not add these — see AGENTS.md § Never do. (why: development/tooling.md)
  • Don't put slow / network / whole-project scans in check or pre-commit. Advisory scans are not correctness gates; they belong under maintain:. (why: development/workflow.md)
  • New work starts with npm run create:branch, never a hand-written git switch -c / git checkout -b. The command carries the branch precondition; branching around it skips the clean-tree, current-main and green-baseline checks, and the skip is invisible until a failure can no longer be attributed. (why: development/workflow.md)
  • Work is merged back with npm run create:finish, never a hand-written git merge. The command carries the merge-side preconditions (clean tree, current main, a feature//fix//chore/ branch) and runs npm run verify after the merge, so a merge cannot land unverified. (why: development/workflow.md)
  • There is no local npm run publish, and publish:publint / publish:attw don't go in check. (why: development/publishing.md)
  • A decision or its rationale belongs in development/, not here. This file holds the actionable rule; development/<category>.md holds why, the rejected alternatives and the known issues. When you change a rule, update its category file in the same commit and cross-link the two. (why: development/README.md)

Branching model

GitHub Flow (single-developer). Every change — feature, fix, refactor — branches off main and is merged back via a local commit. There is no pull request workflow on Gitea yet.

  • Base branch: main
  • Branch naming: feature/<desc> / fix/<desc> / chore/<desc>
  • Starting work: npm run create:branch -- <prefix>/<desc>. It refuses, without changing anything, unless the working tree is clean, no merge/rebase/cherry-pick is in progress, main matches its upstream, and npm run test is green on main. The prefix is your call, inferred from the task; the script validates it rather than guessing it.
  • Merging: npm run create:finish (on the branch). It re-asserts the same preconditions, merges --no-ff, runs npm run verify, and deletes the branch only after the merge is green. The push is left to create:release, so the merge stays local and reviewable.
  • CI runs on every push to main — see Feedback tiers and .gitea/workflows/ci.yml.

Full rationale, including the front-door decisions and a known issue about main being ahead of its upstream between a merge and the next push: development/workflow.md § Branching model.

Submitting changes

There is no pull request workflow on Gitea yet, so a contribution is submitted as a branch that is merged locally:

  1. npm run create:branch -- <prefix>/<desc>.
  2. Commit your work (one or more commits, per the tests and style rules above).
  3. npm run verify — the definition of done.
  4. npm run create:finish to merge the branch into main and verify the result.
  5. Present a handover for review. Once there are no further objections, the maintainer pushes.

When the project is promoted to GitHub, this step becomes a normal pull request against main.

Publishing

Publishing is maintainer-only and CI-only. See development/publishing.md.