Contributing to the runtime
memnox-runtime is Apache-2.0 and takes contributions. It is the piece that actually decides whether an AI action is permitted, so it is written to be read: zero-dependency core packages, no framework magic, and a decision path you can follow end to end in an afternoon.
Setup
Node 20 or newer.
The repository is a pnpm workspace.
$pnpm install$pnpm test$pnpm test:watch$pnpm build$pnpm typecheck$pnpm format$pnpm deadcodeBefore opening a pull request, the same four that CI runs:
pnpm format && pnpm typecheck && pnpm test && pnpm deadcodeThe eleven ground rules
These are not style preferences. Each one exists because breaking it would weaken a guarantee the product makes.
- The decision path stays deterministic. No LLM calls, network requests, clocks read from inside evaluation, or randomness. Intelligence belongs in a separate optional layer that explains decisions and never makes them.
- Fail closed. When identity or state cannot be verified, deny. Never guess in the agent's favour.
- No
any.unknownplus explicit narrowing. Public and private async methods declare their return types. - No magic values. Numbers and strings with meaning live in a
*.constants.tsor a module-levelconstabove the class. - Every catch logs or rethrows. A silent catch is acceptable only for an expected first-run condition, and must say so in a one-line comment.
- Comments are one line and explain WHY. If code needs a paragraph, restructure the code.
- Every behaviour change ships with a test, in
packages/<name>/test. - Audit everything. Any new path through the gateway appends exactly one audit event. Not zero, not two.
- Take your dependencies as arguments. Nothing reaches for
console, the clock, the network orprocess.*in the middle of its logic. - No dead code.
pnpm deadcodefails CI on an unused export. - Gate, not reviewer. Memnox decides whether an action is permitted and states what a class of change must satisfy. It never generates code, never opines on code somebody already wrote, and never reviews a pull request.
Testing without processes or sockets
The two places that used to be untestable were untestable for the same reason: ambient IO. Both were fixed structurally, and both are the pattern to copy.
CLI commands take a CliContext carrying the output port and a client
factory, so the real command tree runs against a recording output and a stubbed
transport:
const out = new RecordedOutput();
await runCli(['policy', 'test', 'git push --force'], new CliContext(out));The MCP proxy splits routing (FirewallSession, over a FirewallChannel)
from process plumbing (McpFirewall, which owns the child process and stdio).
Tests drive the session directly, no spawn, no stdin.
Anything that touches git or the filesystem goes behind a port. Milestones
takes a GitPort and a WorktreePort, so a test asserts which git commands
would run without a test suite that eventually restores a working tree somewhere
it should not.
If your change is hard to test, that is usually the design telling you it reached for something it should have been handed.
What a reviewer checks
The question
Determinism
Direction
One resolver
No model
Failure
Audit
Scope
Tests
Where to read first
Every package has its own README covering what it does, how it is laid out, and what to touch when extending it. Read that before the source.
For how the product behaves from the outside, which is what you are changing, read this documentation site, particularly How a decision is made.

