feat(mosaic): WI-2 mutator-class guard for directive-freshness (#837)
Some checks failed
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/push/publish Pipeline was successful

WI-2: mutator-class guard for the compaction directive-freshness mechanism — command-position parser (prefix x var-indirection unified) + primitive-anchored invariant backstop + all-tools-hook fail-close. Parser-complete on principle: realistic evasion matrix RED-regression-covered, residual exotic evasions proven backstop-caught (B-tests), lens-convergence reached.

closes #829
This commit was merged in pull request #837.
This commit is contained in:
2026-07-18 05:51:58 +00:00
parent 8ec67a1126
commit abd2791f59
27 changed files with 2579 additions and 46 deletions

View File

@@ -115,7 +115,7 @@ function auditClaudeSettings(): SettingsAudit {
// Check required hooks
const hooks = settings['hooks'] as Record<string, unknown[]> | undefined;
const requiredPreToolUse = ['prevent-memory-write.sh'];
const requiredPreToolUse = ['mutator-gate.py', 'prevent-memory-write.sh'];
const requiredPostToolUse = ['qa-hook-stdin.sh', 'typecheck-hook.sh'];
const preHooks = (hooks?.['PreToolUse'] ?? []) as Array<Record<string, unknown>>;
@@ -755,7 +755,7 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev
printSettingsWarnings(settingsAudit);
const prompt = buildRuntimePrompt('claude');
const cliArgs = yolo ? ['--dangerously-skip-permissions'] : [];
const cliArgs: string[] = [];
cliArgs.push('--append-system-prompt', prompt);
if (hasMissionNoArgs) {
cliArgs.push(missionPrompt);
@@ -763,7 +763,7 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev
cliArgs.push(...args);
}
console.log(`[mosaic] Launching ${label}${modeStr}${missionStr}...`);
execRuntime('claude', cliArgs);
execLeaseGatedRuntime('claude', cliArgs, process.env, yolo);
break;
}
@@ -798,7 +798,7 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev
cliArgs.push(...args);
}
console.log(`[mosaic] Launching ${label}${modeStr}${missionStr}...`);
execRuntime('pi', cliArgs);
execLeaseGatedRuntime('pi', cliArgs);
break;
}
}
@@ -806,6 +806,33 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev
process.exit(0); // Unreachable but satisfies never
}
function defaultLeaseBrokerSocket(env: NodeJS.ProcessEnv = process.env): string {
if (env['MOSAIC_LEASE_BROKER_SOCKET']) return env['MOSAIC_LEASE_BROKER_SOCKET'];
const runtimeDir = env['XDG_RUNTIME_DIR'];
if (runtimeDir) return join(runtimeDir, 'mosaic-lease', 'broker.sock');
const uid = typeof process.getuid === 'function' ? process.getuid() : 0;
return join('/run/user', String(uid), 'mosaic-lease', 'broker.sock');
}
function execLeaseGatedRuntime(
runtime: 'claude' | 'pi',
args: string[],
baseEnv: NodeJS.ProcessEnv = process.env,
dangerous = false,
): void {
const launcher = resolveTool('lease-broker', 'launch-runtime.py');
const dangerousArgs = dangerous ? ['--dangerous'] : [];
execRuntime(
'python3',
[launcher, ...dangerousArgs, '--runtime', runtime, '--', runtime, ...args],
{
...baseEnv,
MOSAIC_LEASE_BROKER_SOCKET: defaultLeaseBrokerSocket(baseEnv),
MOSAIC_RUNTIME_GENERATION: baseEnv['MOSAIC_RUNTIME_GENERATION'] ?? '1',
},
);
}
/** exec into the runtime, replacing the current process. */
function execRuntime(cmd: string, args: string[], env: NodeJS.ProcessEnv = process.env): void {
try {
@@ -839,7 +866,8 @@ function launchClaudexProduction(args: string[], yolo: boolean): void {
checkSequentialThinking('claude');
},
composePrompt: () => buildRuntimePrompt('claude'),
exec: (cmd, cmdArgs, env) => execRuntime(cmd, cmdArgs, env),
execLeaseGated: (cmdArgs, env, dangerous) =>
execLeaseGatedRuntime('claude', cmdArgs, env, dangerous),
};
void launchClaudex(args, yolo, adapter);
}