DocsOpen sourceContributing

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

One install for the whole workspace.

$pnpm test

Vitest, running against source through workspace aliases. No build needed. This is the loop you live in.

$pnpm test:watch

The same, left running.

$pnpm build

Builds every package with tsup. Needed only to run the CLI from dist/.

$pnpm typecheck

tsc over the whole tree.

$pnpm format

Prettier.

$pnpm deadcode

knip. An unused export fails CI, delete it, git remembers.

Before opening a pull request, the same four that CI runs:

bash
pnpm format && pnpm typecheck && pnpm test && pnpm deadcode

The eleven ground rules

These are not style preferences. Each one exists because breaking it would weaken a guarantee the product makes.

  1. 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.
  2. Fail closed. When identity or state cannot be verified, deny. Never guess in the agent's favour.
  3. No any. unknown plus explicit narrowing. Public and private async methods declare their return types.
  4. No magic values. Numbers and strings with meaning live in a *.constants.ts or a module-level const above the class.
  5. 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.
  6. Comments are one line and explain WHY. If code needs a paragraph, restructure the code.
  7. Every behaviour change ships with a test, in packages/<name>/test.
  8. Audit everything. Any new path through the gateway appends exactly one audit event. Not zero, not two.
  9. Take your dependencies as arguments. Nothing reaches for console, the clock, the network or process.* in the middle of its logic.
  10. No dead code. pnpm deadcode fails CI on an unused export.
  11. 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:

ts
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

Could this produce a different verdict on the same input?

Direction

Can this only tighten a decision, never loosen it?

One resolver

Does a command line still produce the same action name on every surface, or has a second resolver appeared?

No model

Is there anything probabilistic between an action and its verdict? That is rejected in review whatever the deadline.

Failure

If this throws, does the system deny or does it quietly allow?

Audit

Does this path append exactly one event?

Scope

Does this stay a gate, or has it started reviewing code?

Tests

Does a behaviour change come with a test that would fail without it?

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.