Files
apex-mcp-server/packages/builder/test/boundary.test.ts
FilipBl4gojevic 88068816e6 feat(builder)!: self-improvement family keyless - two-step proposal tools, @ts-nocheck dropped, safety.ts deleted, custody allowlist EMPTY
Rewrites self-improvement.ts (1024 -> 556 lines): typed, no @ts-nocheck,
imports build logic from gov-build.ts (Tasks 3-4). Splits the old single-shot
vector_self_improvement_submit_proposal into two keyless build tools
(proposal_lock, proposal_spend) since the deployed module validator requires
a confirmed lock before the spend+mint step can reference it; renames
critique/endorse to vector_build_self_improvement_*. browse and
analyze_metrics stay behavior-identical read-only tools (render strings
verified byte-for-byte against the custodial originals).

Deletes safety.ts (its last importer). Removes SpendLimits/SpendStatus/
AuditEntry from shared types (grep-verified zero references first). Drops
the now-dead spend-limit/audit-log env vars. Empties the custody boundary
allowlist with a size-0 pin - every builder family is now keyless.

Tool count 23 -> 24. Snapshot delta is surgical: 3 removed, 4 added, 19 of
the other 20 byte-identical (analyze_metrics' description had to drop the
literal word "mnemonic" to satisfy the custody grep - the one disclosed
exception).

Headline: grep -rni "mnemonic|seed|bip39|fromSeed" packages/builder/src
returns zero lines. This was the last custodial family.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 16:43:16 +02:00

104 lines
5.1 KiB
TypeScript

import { describe, test } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync, readdirSync, statSync } from 'node:fs';
import { resolve, join, relative } from 'node:path';
/**
* The builder-side custody guarantee, made mechanical (design-spec success
* criterion 1: "grep-able: zero mnemonic params in the builder").
*
* Files still on the custodial path are explicitly allowlisted with the PR
* that removes them. Shrinking this list is the definition of done for the
* non-custodial split; growing it should be impossible to do accidentally.
*/
const SRC_ROOT = resolve(import.meta.dirname!, '../src');
const SCRIPTS_ROOT = resolve(import.meta.dirname!, '../scripts');
// Empty since spec PR 8: family 3 (self-improvement) went keyless, taking the
// last custodial file (vector/self-improvement.ts) off the allowlist. Every
// builder family is now keyless - this set may never grow again (see the
// size-0 pin below).
const CUSTODIAL_UNTIL_LATER_PR = new Set<string>([]);
// No \b boundaries: this is deliberately a substring match, not a whole-word
// one. Word-bounding let `seedPhrase`, `WALLET_SEED` or `bip39x` slip past
// undetected — those are real key-material vocabulary wearing a different
// word shape, not false positives. `fromSeed`/`walletFromSeed` are already
// covered as substrings of `seed`, but stay in the alternation anyway for
// self-documentation (they cost nothing and name the exact API shape this
// guard exists to catch).
const FORBIDDEN = /(mnemonic|seed|bip39|fromSeed|walletFromSeed|selectWallet\.fromSeed)/i;
function sourceFiles(dir: string): string[] {
const out: string[] = [];
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) out.push(...sourceFiles(full));
else if (/\.(ts|js|mjs|cjs|mts|cts)$/.test(entry)) out.push(full);
}
return out;
}
describe('builder custody boundary', () => {
test('no key-material vocabulary outside the allowlisted custodial files', () => {
const offenders: string[] = [];
for (const file of sourceFiles(SRC_ROOT)) {
const rel = relative(SRC_ROOT, file).replace(/\\/g, '/');
if (CUSTODIAL_UNTIL_LATER_PR.has(rel)) continue;
const lines = readFileSync(file, 'utf-8').split('\n');
lines.forEach((line, i) => {
if (FORBIDDEN.test(line)) offenders.push(`${rel}:${i + 1}: ${line.trim()}`);
});
}
// scripts/ is dev-tooling, not shipped builder source - it has no
// custodial carve-out, so nothing here is ever checked against the
// allowlist (unlike SRC_ROOT files above).
for (const file of sourceFiles(SCRIPTS_ROOT)) {
const rel = relative(SCRIPTS_ROOT, file).replace(/\\/g, '/');
const lines = readFileSync(file, 'utf-8').split('\n');
lines.forEach((line, i) => {
if (FORBIDDEN.test(line)) offenders.push(`scripts/${rel}:${i + 1}: ${line.trim()}`);
});
}
assert.deepEqual(offenders, [], `key-material vocabulary found in keyless builder source:\n${offenders.join('\n')}`);
});
test('the allowlist matches reality (files removed from the custodial path must leave the list)', () => {
for (const rel of CUSTODIAL_UNTIL_LATER_PR) {
const content = readFileSync(join(SRC_ROOT, rel), 'utf-8');
assert.match(
content, FORBIDDEN,
`${rel} is allowlisted as custodial but contains no key-material vocabulary - remove it from CUSTODIAL_UNTIL_LATER_PR`,
);
}
});
// The allowlist may only shrink (PR 7 dropped agent-network.ts to reach 1,
// PR 8 drops self-improvement.ts to reach 0 - see the comment above
// CUSTODIAL_UNTIL_LATER_PR). Pinning the size means any widening is a
// deliberate, reviewable number change instead of a silent `.add(...)`
// slipping through - and since the migration is complete, that number can
// now only ever be 0.
test('the allowlist may only shrink', () => {
assert.equal(CUSTODIAL_UNTIL_LATER_PR.size, 0, 'the custodial allowlist is EMPTY - the migration is complete; it may never grow again');
});
// Pins every FORBIDDEN alternative individually against a fixture corpus, so
// deleting one alternative from the regex (e.g. "we don't need bip39
// anymore") fails a test by name instead of silently narrowing what this
// guard catches. Also pins representative keyless vocabulary that must NOT
// trip the guard, so widening FORBIDDEN too aggressively is equally visible.
test('every FORBIDDEN alternative is individually pinned against a fixture corpus', () => {
const mustMatch = [
'mnemonic', 'MNEMONIC', 'seedPhrase', 'WALLET_SEED', 'bip39',
'fromSeed', 'walletFromSeed', 'selectWallet.fromSeed', 'seed',
];
for (const s of mustMatch) {
assert.match(s, FORBIDDEN, `FORBIDDEN should match "${s}" - deleting the alternative that catches this would silently narrow the guard`);
}
const mustNotMatch = ['address-only wallet', 'unsigned transaction', 'changeAddress'];
for (const s of mustNotMatch) {
assert.doesNotMatch(s, FORBIDDEN, `FORBIDDEN should NOT match "${s}" - this is legitimate keyless vocabulary`);
}
});
});