Files
stack/agents/dewey/work/queue-83/identity.mjs
T
jason.woltjeandClaude Opus 5.5 e1bf582a1a dewey: row 83 round 1 packet, unit script takes --send (#1576)
build.patch against d13b9300 (brief blob f8d14d70, rev 630), candidate
manifest, identity and mutant harnesses with results, gate summary.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:59:05 -05:00

89 lines
6.5 KiB
JavaScript

#!/usr/bin/env node
// Dewey row 83: the base script (d13b9300) and the candidate, run side by side
// in one scratch worktree (same REPO, same template) on every argument list
// the base service tests use, without --send. Expected: stdout, stderr and
// exit status identical, and every installed file identical, except the usage
// text on exit 4 (it names [--send]), status's new send: line, and the
// install message's start wording (lead decision 102).
// Usage: identity.mjs <worktree>, where scripts/webui-service-base.sh is the
// base script and scripts/webui-service.sh the candidate. Nothing here reaches
// the real systemd: install and uninstall run with --no-reload, status with a
// stand-in systemctl.
import { spawnSync } from 'node:child_process';
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
const WT = process.argv[2];
const BASE = join(WT, 'scripts', 'webui-service-base.sh');
const CAND = join(WT, 'scripts', 'webui-service.sh');
const NODE_DIR = dirname(process.execPath);
const PREVIEW = ['--public-host', 'console.example.com', '--auth-header', 'X-authentik-uid', '--allow-user', 'jason'];
const scratch = mkdtempSync(join(tmpdir(), 'r83-identity-'));
const bin = join(scratch, 'bin');
mkdirSync(bin);
writeFileSync(join(bin, 'systemctl'), '#!/bin/sh\ncase "$*" in *is-enabled*) echo enabled ;; *is-active*) echo inactive ;; esac\nexit 0\n');
chmodSync(join(bin, 'systemctl'), 0o755);
const set = (flag, value) => {
const preview = PREVIEW.slice();
const i = preview.indexOf(flag);
if (i >= 0) { preview[i + 1] = value; return preview; }
return [flag, value];
};
const U = join(scratch, 'units');
const cases = [
[], ['--port', '7340', '--business', 'mosaic.dev_1', '--board', 'http://127.0.0.1:7331', ...PREVIEW],
PREVIEW, ['--business', 'mosaic'], [...PREVIEW, '--business', 'mosaic'],
PREVIEW.slice(0, 2), PREVIEW.slice(2, 6), [...PREVIEW.slice(0, 2), ...PREVIEW.slice(4, 6)],
].map((a) => ['render', ...a]);
for (const flag of ['--business', '--board', '--public-host', '--auth-header', '--allow-user']) {
for (const bad of [' x', '%n', '\n', '$HOME', '"', '\\', '|', '&']) {
cases.push(['render', ...set(flag, flag === '--board' ? `http://127.0.0.1:7331${bad}` : `a${bad}b`)]);
}
}
for (const port of ['0', '65536', '7a', '', '-1', '999999', '7332', '65535']) cases.push(['render', '--port', port]);
for (const args of [['--business', 'Mosaic'], ['--business', '-x'], ['--business', 'a'.repeat(65)], ['--board', 'https://127.0.0.1:7331'], ['--board', '127.0.0.1:7331'], set('--public-host', 'console.example.com:443'), set('--public-host', 'https://console.example.com'), set('--auth-header', 'X_uid')]) cases.push(['render', ...args]);
for (const args of [[], ['start'], ['stop'], ['enable'], ['restart'], ['render', '--bogus'], ['render', '--port'], ['render', '--dir', scratch], ['render', '--no-reload'], ['uninstall', '--port', '7332'], ['status', '--business', 'mosaic'], ['status', '--no-reload'], ['--help']]) cases.push(args);
// Install, then the same again (unchanged), a refusal over it, then uninstall.
cases.push(['install', '--dir', U, '--no-reload', '--business', 'mosaic'], ['install', '--dir', U, '--no-reload', '--business', 'mosaic'], ['install', '--dir', U, '--no-reload', '--business', 'a b'], ['install', '--dir', join(scratch, 'never'), '--no-reload', '--port', '0'], ['install', '--dir', U, '--no-reload', ...PREVIEW, '--port', '7340'], ['status', '--dir', U], ['uninstall', '--dir', U, '--no-reload'], ['uninstall', '--dir', U, '--no-reload'], ['status', '--dir', U]);
const env = { PATH: `${bin}:${NODE_DIR}:/usr/bin:/bin`, HOME: scratch, XDG_CONFIG_HOME: join(scratch, 'config') };
const files = (dir) => (existsSync(dir) ? readdirSync(dir).sort().map((f) => `${f}\n${readFileSync(join(dir, f), 'utf8')}`).join('\n') : '<absent>');
// Each script runs the whole sequence from a clean scratch, so install state carries the same way.
function sequence(script) {
rmSync(U, { recursive: true, force: true });
rmSync(join(scratch, 'never'), { recursive: true, force: true });
return cases.map((args) => {
const r = spawnSync(script, args, { env, encoding: 'utf8' });
return { status: r.status, stdout: r.stdout, stderr: r.stderr, units: files(U), never: existsSync(join(scratch, 'never')) };
});
}
const base = sequence(BASE);
const cand = sequence(CAND);
const usageBase = readFileSync(BASE, 'utf8').match(/^ FLAGS: .*"$/m)[0].slice(0, -1);
const usageCand = readFileSync(CAND, 'utf8').match(/^ FLAGS: .*"$/m)[0].slice(0, -1);
// The install message's lines before its command list.
const message = (script) => { const s = readFileSync(script, 'utf8'); return s.slice(s.indexOf('cat <<MSG\n') + 10, s.indexOf(' systemctl --user enable --now mosaic-webui start')); };
const msgBase = message(BASE), msgCand = message(CAND);
let same = 0, expected = 0, unexpected = 0;
cases.forEach((args, i) => {
const b = base[i], c = cand[i];
const label = JSON.stringify(args).replaceAll(scratch, '$S');
if (JSON.stringify(b) === JSON.stringify(c)) { same++; return; }
const usageOnly = b.status === 4 && c.status === 4 && JSON.stringify({ ...b, stderr: b.stderr.replace(usageBase, usageCand) }) === JSON.stringify(c);
const helpOnly = args[0] === '--help' && JSON.stringify({ ...b, stdout: b.stdout.replace(usageBase, usageCand) }) === JSON.stringify(c);
const bl = b.stdout.split('\n'), cl = c.stdout.split('\n');
const sendLineOnly = args[0] === 'status' && b.status === c.status && b.stderr === c.stderr && b.units === c.units
&& JSON.stringify(cl.filter((l) => !/^send: off \(read-only\)$/.test(l))) === JSON.stringify(bl) && cl.length === bl.length + 1;
const messageOnly = args[0] === 'install' && b.status === 0 && msgBase !== msgCand && b.stdout.includes(msgBase) && JSON.stringify({ ...b, stdout: b.stdout.replace(msgBase, msgCand) }) === JSON.stringify(c);
if (messageOnly) { expected++; console.log(`start wording only ${label}`); return; }
if (usageOnly || helpOnly) { expected++; console.log(`usage text only ${label}`); return; }
if (sendLineOnly) { expected++; console.log(`send: line only ${label}: + ${cl.find((l) => l.startsWith('send:'))}`); return; }
unexpected++;
console.log(`DIFFERS ${label}\n base ${JSON.stringify(b)}\n cand ${JSON.stringify(c)}`);
});
console.log(`${cases.length} cases: ${same} identical, ${expected} expected differences, ${unexpected} unexpected`);
rmSync(scratch, { recursive: true, force: true });
process.exitCode = unexpected ? 1 : 0;