Imported from stuffbucket/monimal (
packages/maximal-core/AGENTS.md). Install upstream withnpx skills add stuffbucket/monimal --skill maximal-core. Copyright stays with the author.
AGENTS.md
maximal-core is a headless local proxy that exposes GitHub Copilot as
OpenAI- and Anthropic-compatible HTTP endpoints. Bun + Hono + TypeScript.
There is no UI, no shell/, no frontend build in this repo — a separate
tier drives the engine over the loopback /control JSON-RPC 2.0 surface.
Every turn
- From the monorepo root, run
pnpm --filter @stuffbucket/maximal-core run check:fastafter each edit — oxlint +tsc+ ESLint. This is the native, non-product inner loop. - Before you call the task done, run
pnpm run check:corefrom the monorepo root. It runs Core's complete non-test gate (check:deep:host) and then the focused Core suite through the isolated native wrapper. It does not cover the pinned-dependency Docker rerun orci.yml's native Windows job. If you touchedscripts/ops/, also runpnpm --filter @stuffbucket/maximal-core run check:ops. - To rerun only Core's tests, use
pnpm test -- --corefrom the monorepo root. To rerun Core with pinned container dependencies, usepnpm run test:docker -- --suite=maximal-corefrom the primary checkout. The monorepo test workflow owns the scopes, isolation, and linked-worktree rule. - Never run raw host
bun testor package-localbun run check:deepin this monorepo. Package tests are inner commands admitted only by the isolated root wrapper or the Docker boundary. bun run e2eif you changed the control plane, the ready-line, or shutdown. Spawns the engine from source and drives the real socket — outsidebun testbecause it costs seconds and a port. Every bug it has caught was invisible to the unit suite.MAXIMAL_E2E_BINARY=<path> bun run e2ere-runs the same seams against a compiled binary — core builds none, butstuffbucket/maximalcompiles this repo'ssrc/main.tsand that is what ships.- Never report success on a command you did not run. If a check fails, say so and show the output.
Writing code
- Import through the
~/alias forsrc/(~/lib/errors/error), never a deep relative path. - Strict TS: no
any, no unused locals or parameters. verbatimModuleSyntax+erasableSyntaxOnlyare on — type-only imports must beimport type, and enums, namespaces, and parameter properties will not compile.- ESM only, no CommonJS.
camelCasevalues,PascalCasetypes. - Route handlers catch and call
forwardError(c, error); throwHTTPErrorfrom~/lib/errors/error. - Every API flow supports streaming (SSE via
streamSSE) and non-streaming, switching onpayload.stream. Implement both.
Rules that cost a turn when broken
Each rule states the prohibition; the linked doc is its only elaboration.
- Never
git stash popin a shared working tree. It merges another in-flight agent's stash into yours. Isolate first —docs/architecture.md→ Parallel-agent convention. - Do not
mock.modulea shared module — inject instead. The stub reaches every file evaluated after the installing one (bun testinterleaves evaluation and execution, so the leak is forward-only). AnafterAllrestore does run before the next file evaluates — but only works if it hands back a copy captured before the install:mock.modulemutates the live namespace in place, so() => realModulere-installs the stub. Captureconst real = { ...(await import("…")) }. That missing spread was the whole of #27. Prefer a DI seam (__setServeForTests,__setBootSecretsForTests);mockModuleLeakGuardcatches only some shapes —docs/dev/testing-strategy.md§5.1. - Reset module-level state in BOTH
beforeEachandafterEach. A singleton reset only on the way in leaks to the next file; only on the way out inherits from the previous one. Both bugs shipped here — §5.6. - A PR title must be a single valid Conventional Commit. Squash-merge uses
it as the commit subject, and the release notes are generated from PR titles,
so the title is the only thing that reaches the changelog. Mark a breaking
change with
!— that is what puts it in a minor rather than a patch, and a breaking change shipped as a patch lands inside a consumer's^0.y.zrange.docs/architecture.md→ Release & PR conventions. - Assign every PR to a release milestone. The milestone title is the tag that will be cut; it is how a PR pre-selects the release it ships in.
mainrequires a PR, three green checks, and an up-to-date branch.testandwindows(ci.yml) plusgate(release-gates.yml) are required status checks — a red one blocks the merge button, and so does a branch that has fallen behindmain(gh pr update-branch; nothing rebases for you here). Direct pushes tomainare rejected, andmaincannot be deleted or force-pushed by anyone. There is no exemption and no bypass actor, the release included:release:preparelands the release commit through a PR andrelease:tagcuts the tag on the merged head afterwards.docs/admin/branch-rulesets.md.
Read before you touch
| Area | Read first |
|---|---|
| Routing, middleware, model dispatch, config, token store, control API, diagnostics | docs/architecture.md |
| Tests, especially mocks or mutation testing | docs/architecture.md → Testing gotchas, then docs/dev/testing-strategy.md |
| Running scripts or setting up the dev environment | docs/commands.md |
| Running the checks on the pinned toolchain, off your own PATH | docs/dev/container-toolchain.md |
| Reproducing a Windows-only failure locally, instead of pushing and waiting | docs/dev/windows-vm-qemu.md |
| Opening a PR or cutting a release | docs/architecture.md → Release & PR conventions, then docs/release-runbook.md |
| Branch protection, required checks, or anything in repo settings | docs/admin/branch-rulesets.md |
| Spawning parallel agents or using worktrees | docs/architecture.md → Parallel-agent convention |
| Changing the pinned Bun version | docs/bun-version-policy.md |
| The Claude Code or Opencode plugin | docs/plugins.md |
| Dispatching or reviewing codegen feedback loops | docs/codegen-feedback-loops-practices.md |
Also available, unlinked above: docs/decisions/ (ADRs), docs/spec/
(feature specs), docs/dev/, docs/admin/, docs/guide/, and
CONTRIBUTORS.md (domain experts to loop in per area). Search docs/ before
you ask or infer.