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.

$npm install

One install for the whole workspace.

$npm test

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

$npm run test:coverage

The same, with a coverage report.

$npm run build

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

$npm run typecheck

tsc over the whole tree.

$npm run format

Prettier.

$npm run deadcode

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

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

bash
npm run format && npm run typecheck && npm test && npm run 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, block. 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. npm run 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 runtime = new FakeRuntime().on('POST', '/v1/actions/check', decision);
const { out } = await runCli(['check', '--token', 't', '--action', 'x'], runtime);

The MCP firewall 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.

The MCP server follows the same split: McpServer.handle() is message-in, message-out and owns no sockets, so a test drives the real protocol.

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 advisor only tighten a decision, never loosen it?

Failure

If this throws, does the system block 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.