control board: stale registrations and a launch-test leak (#1504)

Two defects in 69f99323, reported by the professor session and verified.

The darkwing launch test's flock-contention spawn ran without the fixture
config, so launch.sh re-entered scripts/mosaic against the real data root
and wrote fixture records for darkwing, dewey and filbert there. That
spawn now names the fixture config, and both launch test files set
MOSAIC_CONFIG to a nonexistent path and clear MOSAIC_LAUNCH_REGISTERED
process-wide, so a spawn that forgets fails instead of polluting.

A registration is written before the launch script's own checks, so a
refused launch left a record with a dead pid that the board honoured. The
scanner now probes the recorded pid (pidAlive, signal 0); a gone pid makes
the record stale: still on the Registered line with alive false, derived
task, project and workspace win, index gains registrationStale, CLI
summary gains a stale count.

Fleet launchers marked not planned per Jason. Board 90/90, seat 15/15,
launch scripts 5/5. Sonnet review APPROVED.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
2026-09-12 11:04:49 -05:00
co-authored by Claude Fable 5.1
parent 69f99323c7
commit 17153fe140
14 changed files with 166 additions and 18 deletions
+5 -1
View File
@@ -7,6 +7,10 @@ import { join, resolve, basename } from 'node:path';
import { spawnSync } from 'node:child_process';
const source = resolve(import.meta.dirname, '..');
// Every spawn below must name the fixture config explicitly. Anything that
// forgets inherits these and fails instead of registering into a real data root.
process.env.MOSAIC_CONFIG = join(tmpdir(), 'mosaic-launch-test-no-config.json');
process.env.MOSAIC_LAUNCH_REGISTERED = '';
for (const agent of ['darkwing', 'dewey', 'filbert']) test(`${agent} launch context, resources, session recovery, and refusals`, () => {
const root = mkdtempSync(join(tmpdir(), `${agent} launch `));
try {
@@ -127,7 +131,7 @@ fs.writeFileSync('capture.json', JSON.stringify({args: process.argv.slice(2), ag
assert.notEqual(launch('', false).status, 0);
const locked = spawnSync('script', ['-q', '-e', '-c',
`flock .pi/state/${agent}/launch.lock bash agents/${agent}/launch.sh --fresh`, '/dev/null'],
{ cwd: root, encoding: 'utf8', timeout: 10000 });
{ cwd: root, env: { ...process.env, MOSAIC_CONFIG: join(root, 'config.json') }, encoding: 'utf8', timeout: 10000 });
assert.notEqual(locked.status, 0);
assert.match(locked.stdout, new RegExp(`another ${agent} TUI`));
writeFileSync(join(root, 'alternate.md'), 'alternate user context\n');
+4
View File
@@ -7,6 +7,10 @@ import { join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
const source = resolve(import.meta.dirname, '..');
// Every spawn below must name the fixture config explicitly. Anything that
// forgets inherits these and fails instead of registering into a real data root.
process.env.MOSAIC_CONFIG = join(tmpdir(), 'mosaic-launch-test-no-config.json');
process.env.MOSAIC_LAUNCH_REGISTERED = '';
test('Rocko uses Sonnet, isolated resume IDs, current context and launch refusals', () => {
const root = mkdtempSync(join(tmpdir(), 'rocko launch '));
try {