Corbits Code is a single-process coding agent CLI built on the Interchange runtime. For how the system is built, read /docs — do not re-derive it from source.
Load the style, philosophy, and native-integration skills. Confirm working-tree status (git status) and run git log --oneline -5. When the task touches the agent loop, directors, tools, or prompts, read the relevant doc in /docs before writing code.
New contributors: configure git hooks and verify the environment before the first commit.
git config core.hooksPath .githooks
./bin/check-env- Runtime: Bun + TypeScript, ES modules only. No CommonJS.
- Paradigm: Functional. No classes, no OOP.
- Types: Full type safety. Avoid
any; preferunknown. Validate all external input at the boundary with arktype — do not hand-rolltypeofguards for structured data. - Files: Small functions, small files, clear names. Acronyms keep their case (
URL,JSON,API). - Comments: Comment why, never what. If a comment describes what the code does, fix the names instead.
- No emojis in code or docs.
Touch only code directly related to the task. No drive-by renames, reformatting, import reordering, or "while I'm here" refactors — they pollute diffs and risk breakage. Raise unrelated fixes as separate work.
When refactoring replaces an old path, delete the old one. No back-compat shims, re-exports, or _unused renames for callers you own.
- Add or update tests with every behavior change.
- Bug fixes start with a failing test that reproduces the bug. Do not start by patching.
tests/unit/shared unit tests and helpers · co-locatedsrc/**/*.test.tsfor module logic ·tests/fixtures/fixture repos ·tests/integration/reactor/permission harness. Planned:tests/e2e/(fixture-repo runs).- A test must not depend on another file having run, or on the default file order. It must pass under
bun test ./src ./tests ./evals ./scripts --randomize. If a test mutates module-level state or callsmock.module, it must restore that state itself (afterEach/afterAll), not rely on the process happening to reset it. When capturing a module's real exports to restore later, shallow-copy them ({ ...moduleNamespace }) at capture time, whether the namespace came fromawait import(path)or a staticimport * as ns from "path"— Bun mutates the live namespace object in place when the module is mocked, so holding a bare reference to it (either form) silently turns into the mocked exports. - Never call
mock.moduledirectly. Bun runs every test file in one process, so amock.modulecall without its own teardown stays installed for the rest of the run and silently replaces the real module for other files — producing failures in files the change never touched, with no obvious link to the cause and no signal fromtscor a per-file run (CL-6967). UsewithMockedModule/withMockedModuleDuringfromtests/helpers/mock-module.ts, which capture the real module and register their own restore. The oxlint plugin (corbits/no-bare-mock-modulein.oxlintrc.json/scripts/oxlint-plugin-corbits.js) rejects baremock.modulecalls in*.test.tsfiles. - A test earns its place only if a real behavior change can fail it. Document copy, brand colors, marketing assets, and splash text are not behavior: assertions that pin an asset's literal wording, an exact palette hex/ANSI value, or rendered copy fail on copy/design edits and catch no regressions — assert the contract instead (parsing, formatting, ranges, aliases, invariants). Tests are code too: pinning a source file's own text is the same trap. This bar is a review and authorship rule, not a linter shape match.
bun run checkbun run check is the single pre-PR gate: it runs lint, typecheck,
build, and check:projects-dir-guard — which runs the test suite under
the projects-dir sandbox guard — in that order, matching CI.
Run the full suite before declaring any task complete. Do not substitute individual targets. If a failure is pre-existing and unrelated to your change, say so explicitly.
bun run test runs bun test ./src ./tests ./evals ./scripts --randomize --seed 424242
as a single process. CI shards the same path union via test:paths
(.github/workflows/ci.yml) for wall clock. Path-union is not the same
isolation domain: a mock.module leak across ./src vs ./tests fails
locally in the one-process suite but not in a CI shard (CL-6967). A bare
bun test also scans vendor/, adding hundreds of unrelated results and
making pass/fail counts meaningless to compare across branches — always use
bun run test. test:paths with no path filters refuses to run for the
same reason.
MUST follow CONTRIBUTING.md. That file is the source of truth for commit
titles and bodies, PR titles and bodies, and Linear/GitHub linking. Do not use
Conventional Commits prefixes (feat:, fix:, docs:, ci:, …), ticket IDs
in commit subjects, or free-form PR body sections. Rewrite before push if a
message violates those rules. Commit with the operator's local git identity.
Never mutate git configuration outside the current repository, for any reason and not even temporarily with a plan to restore it — whatever the command (--global, --system, --edit, --file pointed at a path outside the repo, reassigning or unsetting GIT_CONFIG_GLOBAL, or writing ~/.gitconfig directly). That state is shared by every agent and every repo on the machine; a crash or a second agent running concurrently turns a "temporary" toggle into a lasting outage or collision. This is the same hazard class as running git stash (also global, also banned). Auto mode enforces this at the shell-policy layer (git-global-config in src/permission/auto-shell-policy.ts), which routes any such command to an operator ask instead of running it unattended — this instruction is the fallback for the cases the policy can't see, not the only line of defense.
If SSH push fails because the shell can't reach the ssh-agent socket, use bin/git-push-scoped instead of touching config:
bin/git-push-scoped origin <branch>It authenticates over HTTPS via gh's credential helper and rewrites the SSH remote to HTTPS, both scoped to that one git push invocation with -c. Nothing is written to any config file, so there is nothing to restore and nothing to collide over.
Interchange is the standard library for this repo. Every @intx/* package this repo imports resolves to vendored source under vendor/intx-* at a single pinned upstream commit — the sole exception is @intx/tools-lsp, which remains on published npm (provenance, patch ledgers, and the re-sync procedure: docs/VENDORING.md). We never modify or push to the upstream interchange repository. Before writing any new infrastructure — plugins, middleware, utilities, state management, logging, authz, inference, tools — check these packages.
| Package | Covers |
|---|---|
@intx/authz |
Grant matching (matchPattern, evaluateGrants) for permission approvals; Corbits owns the gate, store, and TUI ask |
@intx/inference |
Reactor loop, createAuthzExtension, DefaultDirector |
@intx/agent |
Agent lifecycle, send queue, stream |
@intx/tools-posix |
Shell, file read/write/edit, grep, search |
@intx/storage-isogit |
Git-backed state persistence |
@intx/log |
Structured logging via LogTape |
@intx/types |
All shared runtime types |
docs/ARCHITECTURE.md— reactor loop, events, directors, workflows, plugin chain, permission systemdocs/TUI.md— terminal UI behavior spec: layout, overlays, selectors, palette, prompt box, scrollingdocs/IMPLEMENTATION.md— runtime, dependencies, config resolution, settings precedence, CLI flags, state persistence, eval harnessdocs/PRODUCT.md— what we're building and whydocs/HOOKS.md— lifecycle hooksdocs/MCP.md— connecting MCP serversdocs/PLUGINS.md— plugin manifest system and discoverydocs/TELEMETRY.md— what usage telemetry is collected and whydocs/PERFTRACE.md— local PerfTrace and opt-in OTEL export settingsdocs/plans/— gitignored working notes and design spikes (local only); durable conclusions belong in the docs above or Linear — never left as a plan file, which is a stale doc waiting to happen