Contributing to bang-lang
Welcome — human or agent. This is the entry point: it tells you how the project is organized, where
knowledge lives, and how a change gets from an idea to main. It routes to the authoritative docs
rather than repeating them (single source of truth), so follow the links.
One idea underneath everything: correctness by construction + generate, don't hand-maintain. Illegal states are made unrepresentable (types over runtime checks); derived facts (docs, indexes, the changelog) are generated from a single source so they can't drift. Every rule below is a consequence of that.
1. Orient (read in this order)
| Read | For |
|---|---|
CLAUDE.md | the always-loaded core — invariants (never break these), the glossary, architecture-in-force, the verify command |
CONTEXT.md | where the project is right now (volatile; the lead is the current position) |
ROADMAP.md + docs/roadmap/project-roadmap.md | where it's going — the ◊ proof-map, and the product-axis DAG (projects → the features they pull) |
ONBOARDING.md | first-time setup + a tighter reference index |
Everything else is on-demand — the tables in CLAUDE.md and docs/notes/README.md index it.
2. The knowledge map — where documentation belongs (ADR-0077)
Docs are placed by (audience × temporality). Knowing a doc's coordinate tells you its home, its lifecycle, and how it's maintained:
PRODUCT (the artifact, a snapshot — for USERS) PROJECT (the work, time-indexed — for CONTRIBUTORS)
GENERATED from code · publishable ┌ DONE (immutable): CHANGELOG · ADRs · git history
docs/reference/language.md · PRD · README · ├ NOW (volatile): CONTEXT.md · paths/
ONBOARDING └ NEXT (revisable): ROADMAP · project-roadmap ·
OPEN_QUESTIONS (the design ledger)
Deciding where your doc goes: are you describing the thing as it is (→ product, and prefer to generate it from the code) or the work of building it (→ project, and which tense — done/now/next)? Putting a "future feature" line in the product reference, or a "current status" line in a timeless doc, is a category error that will drift. Full rationale: ADR-0077.
3. Where knowledge lives, and how it's stored (ADR-0078)
Docs are git-native. The design ledger, ADRs, and notes are files in this repo — because the top priority is that documentation be directly readable by AI agents (an agent reads the repo; a hosted tool needs an API round-trip). Git files are also diffable, PR-reviewed, and generated-from-frontmatter.
- Design decisions (a fork future work could reverse) → an ADR in
docs/decisions/(copy an existing one's shape; record the rejected alternatives, not just the choice). The ledgerdocs/decisions/README.mdis generated from ADR frontmatter — don't hand-edit it. - Open design questions (a fork not yet decided) → an OKF file in
docs/notes/questions/(frontmatter:type/title/description/status/area/ties/see-also).OPEN_QUESTIONS.mdis a generated multi-view index (by area, by status, a validated tie-graph) — edit the question file, thenjust questions-index. Aties:edge to a nonexistent question/ADR fails the build (dangling edges are unrepresentable). - Volatile state (current position, blockers, active work) →
CONTEXT.md/paths/PATH-*.md. - History → git commits. Don't narrate the past in docs ("we used to X") — the commit message holds it.
Project management is GitHub Issues (not a doc file) — issues, milestones, labels, the Projects board.
Issues and docs cross-reference via #N in a question's see-also; a gh-bridge (roadmap) resolves those
into the tie-graph, so issues ↔ questions ↔ ADRs form one queryable graph across two well-chosen stores. Full
rationale (and why not the Wiki / Linear / Jira): ADR-0078.
4. Set up
nix develop # ENTER THE DEV SHELL FIRST — bare lake/just/node are NOT on PATH
just verify # the default gate: selfcheck (Node) + lake build + audit
just # list all recipes
First lake build pulls Mathlib (lake exe cache get; network, minutes). See docs/notes/dev-env.md for
the flake/scripts/gotchas.
5. The change workflow
issue (GitHub) → branch → make the change → the VERIFY GATE → PR (closes the issue) → review → main
- Branch off
main(never commit tomaindirectly). Conventional-commit subjects (feat(scope): …,docs(scope): …,fix(scope): …) — the CHANGELOG is generated from them. - Land through GitHub squash merge only. Repository settings enforce
allow_squash_merge=true,allow_merge_commit=false,allow_rebase_merge=false, withCOMMIT_OR_PR_TITLE/COMMIT_MESSAGES. A product PR has onefeat/fix/perfsource commit plus its generated follow-up; its PR title must match that source subject (GitHub may append(#N)when landing). Do not edit the squash title in the merge dialog: the reviewed title is an input to the stablechange:identity. - The verify gate — before you claim done, run it and read the real exit code:
Green means:
just verify # selfcheck + lake build + audit just fitness # the derived-doc gate: adr-check · reference · questions-index · hygiene · changelog · … just axioms # for proof work: #print axioms per headline theoremlake build Bang bangsucceeds without exceeding the ratcheted library/runner warning budget,fitnessexits 0, and (for proofs) each headline theorem's axiom set ⊆{propext, Classical.choice, Quot.sound}. Gate-traps to avoid: a piped exit code (cmd | headis always 0), andgrep "sorry"/grep "error:"(use#print axiomsand the build exit code — seeCLAUDE.md§"How to verify"). - Gate the committed content, on a clean tree — never a summary, a dirty worktree, or your own say-so.
- Follow Lean/Mathlib conventions when reasonable (operator policy, 2026-07-10). Where this repo has no
rule of its own, the Lean community's is the default: the Mathlib naming convention
(snake_case proofs · UpperCamelCase types/props · a function named for its return), the
library style guide, and for
comments/docstrings the adopted convention in
docs/notes/lean-comment-style.md(contract-first/--docstrings ·/-!banners · no history in comments). The mechanized face isjust lint-lean(Batteries/Mathlib environment linters — advisory today, seeplans/007-lint-triage.md). "When reasonable" is load-bearing: where bang's own conventions deliberately differ (the §-map + ADR cross-ref style, generated-docs-over-hand-maintained, the agent-first lens), the repo's rule wins — and a new deviation should say why, in the ADR or note that introduces it.
6. If you are an AI agent
The workflow above holds, plus a hard-won discipline for agents that write files:
- No-git-writes for spawned ICs. An IC works in its own worktree (
tools/new-worktree.sh <path> <branch> main— never a baregit worktree add), builds + gates there, and hands the finished files back; the manager lands them onmain. One writer per file. Verify isolation (git worktree list); don't assume it. - Seed before the first build. In a fresh LINKED worktree (e.g. a harness-created one), run
bash tools/seed-lake.shbefore anylake build— it reflink-copies the main checkout's.lake(packages AND build; seconds on btrfs), so the first build is incremental instead of a cold ~20-min rebuild. Neverlake exe cache getin a seeded worktree. Staleness is safe: lake trace-verifies and rebuilds only mismatches. git addnew files IN the worktree before runningjust fitness— hygiene checks scangit ls-files, so untracked files give a false green. Gate on tracked content.- Gate the artifact, not the claim. A confident wrong summary is the same failure as a green stub — go look at the real build / diff / proof. Verify your own claims the same way; welcome being checked.
- Send a full report when you finish (what landed, every gate result, judgment calls) — never just go idle.
- Memory persists across sessions in the auto-memory dir (one fact per file + a
MEMORY.mdpointer). Save the non-obvious (a gotcha, a preference, project state) — not what the repo already records.
The .claude/agents/ role files (kernel-engineer, proof-engineer, lean-proof-auditor) define
specialist agents; the manager/IC split and the full incident log live in the auto-memory.
7. Never break these (the invariants)
The load-bearing invariants — the kernel's five primitives (thunk · force · effect rows · handlers · STM),
effect rows are sets, STM is the only privileged primitive, the machine is calculated not hand-designed,
no implicit capture — are in CLAUDE.md §"Invariants — never break these" and the Do NOT list.
Read them before touching the kernel. Adding a sixth primitive, ordering rows, or hand-designing the VM is a
spec change requiring an ADR.
Questions about how to contribute → open a GitHub Discussion or issue. Questions about what the language
should be → the design ledger (docs/notes/questions/) is where those live and get grilled.