TASK-pilot-generated-check-artifacts isolate check outputs

Closes: TASK-pilot-generated-check-artifacts
This commit is contained in:
thePR0M3TH3AN committed 2026-09-30 19:33:30 -04:00
1 parent f72daadcba
commit f46c713cc9
5 files changed
+261 -38

No files matched your search

+31
View File
@@ -1,5 +1,36 @@
# Test Integrity Notes
## 2026-09-30 — Generated exact-check artifacts
```yaml
test_integrity_note:
change_type: new_tests
scenarios:
- id: SCN-generated-check-artifacts
given: A clean managed worktree and an exact check that emits evidence
when: The same check runs twice through TORCH
then: Each pass retains separate external bytes and hashes bound to candidate commit, check definition, and run ID while the tested tree stays clean
- id: SCN-generated-check-mutation
given: A clean managed worktree and an exact check that emits external evidence
when: The command exits zero after mutating a tracked source file
then: The receipt remains incomplete and cannot qualify despite retained output
observable_outcomes:
- Recoverable output paths, byte counts, SHA-256 hashes, candidate/check/run provenance, and exact-pass qualification
- Git worktree status and incomplete receipt for a zero-exit source mutation
determinism_controls:
- Disposable local Git fixtures, deterministic run IDs, fixed evidence bytes, and no network or provider calls
- No retries, sleeps, baseline updates, stashes, restores, or source cleanup
anti_cheat_rationale:
prevents:
- Treating exit zero as an exact-check pass after a tracked expectation changes
- Losing or overwriting generated evidence between sequential check runs
- Claiming provenance without retained bytes and a content hash
- Hiding source mutations through automatic restore or cleanup
relaxation:
did_relax_any_assertion: false
if_true_explain_spec_basis: ""
```
## 2026-09-30 — Exact observed-file ownership evidence
```yaml
+23 -17
View File
@@ -2,8 +2,14 @@ import assert from 'node:assert/strict';
import { mkdirSync } from 'node:fs';
import { once } from 'node:events';
import { chromium } from '@playwright/test';
import { join } from 'node:path';
import { createConsoleServer } from '../src/console/server.mjs';
const screenshotDirectory = process.env.TORCH_CHECK_ARTIFACT_DIR || 'reports/design-system';
function screenshotPath(name) {
return join(screenshotDirectory, name);
}
const server = createConsoleServer();
server.listen(0, '127.0.0.1');
await once(server, 'listening');
@@ -35,13 +41,13 @@ try {
assert.equal(await page.locator('#operation-outcomes button').count(), 0);
await page.locator('#operation-outcomes .check-evidence').last().locator('summary').click();
assert.match(await page.locator('#operation-outcomes').innerText(), /transient failure.*effects unknown/s);
mkdirSync('reports/design-system', { recursive: true });
await page.locator('#operation-outcomes').screenshot({ path: 'reports/design-system/torch-dashboard-operation-outcomes.png' });
mkdirSync(screenshotDirectory, { recursive: true });
await page.locator('#operation-outcomes').screenshot({ path: screenshotPath('torch-dashboard-operation-outcomes.png') });
await page.locator('#activity-review > summary').click();
assert.match(await page.locator('#activity-review').innerText(), /4 days without a named commit/);
assert.match(await page.locator('#activity-review').innerText(), /waiting may be expected/);
mkdirSync('reports/design-system', { recursive: true });
await page.locator('#activity-review').screenshot({ path: 'reports/design-system/torch-dashboard-activity-review.png' });
mkdirSync(screenshotDirectory, { recursive: true });
await page.locator('#activity-review').screenshot({ path: screenshotPath('torch-dashboard-activity-review.png') });
assert.match(await page.locator('#check-list').innerText(), /Running — completion unconfirmed/);
await page.locator('#check-list .check-evidence').filter({ hasText: 'interrupted-browser' }).locator('summary').click();
assert.match(await page.locator('#check-list').innerText(), /Abandoned — not qualified/);
@@ -51,14 +57,14 @@ try {
await page.locator('#check-list .check-evidence').filter({ hasText: 'recovery-scenario' }).locator('summary').click();
assert.match(await page.locator('#check-list').innerText(), /clockMoving.*true/s);
assert.match(await page.locator('#check-list').innerText(), /not independent sensor truth/);
mkdirSync('reports/design-system', { recursive: true });
await page.locator('#check-list').screenshot({ path: 'reports/design-system/torch-dashboard-check-evidence.png' });
mkdirSync(screenshotDirectory, { recursive: true });
await page.locator('#check-list').screenshot({ path: screenshotPath('torch-dashboard-check-evidence.png') });
assert.match(await page.locator('#owner-briefing').innerText(), /Choose the project home layout/);
assert.match(await page.locator('#owner-briefing').innerText(), /not independent live verification/);
await page.locator('#owner-briefing .briefing-detail').first().locator('summary').click();
assert.match(await page.locator('#owner-briefing').innerText(), /Landed — not necessarily deployed/);
mkdirSync('reports/design-system', { recursive: true });
await page.locator('#owner-briefing').screenshot({ path: 'reports/design-system/torch-dashboard-owner-briefing-desktop.png' });
mkdirSync(screenshotDirectory, { recursive: true });
await page.locator('#owner-briefing').screenshot({ path: screenshotPath('torch-dashboard-owner-briefing-desktop.png') });
assert.match(await page.locator('#attention-list').innerText(), /manager launch needs inspection/);
await page.locator('#attention-list a[href="#manager-wakes"]').click();
assert.equal(await page.locator('#manager-wakes').isVisible(), true);
@@ -93,8 +99,8 @@ try {
await conclusion.locator('[data-review-confirm]').waitFor();
await conclusion.getByText('Exact proposal and evidence in this decision', { exact: true }).click();
assert.match(await conclusion.locator('[data-review-scope]').innerText(), /schedulesRetained/);
mkdirSync('reports/design-system', { recursive: true });
await conclusion.screenshot({ path: 'reports/design-system/torch-dashboard-pilot-conclusion.png' });
mkdirSync(screenshotDirectory, { recursive: true });
await conclusion.screenshot({ path: screenshotPath('torch-dashboard-pilot-conclusion.png') });
await conclusion.locator('[data-review-confirm]').click();
await page.locator('#organization-action-status').filter({ hasText: 'recorded as reversed' }).waitFor();
assert.equal(await page.locator('.agent-row').count(), 6);
@@ -149,12 +155,12 @@ try {
await page.locator('[data-review-confirm]').click();
await page.locator('#organization-action-status').filter({ hasText: 'recorded as approved' }).waitFor();
mkdirSync('reports/design-system', { recursive: true });
mkdirSync(screenshotDirectory, { recursive: true });
await pilotPanel.locator(':scope > summary').click();
await pilotPanel.screenshot({ path: 'reports/design-system/torch-dashboard-pilot-review.png' });
await page.locator('#manager-wakes').screenshot({ path: 'reports/design-system/torch-dashboard-manager-wakes.png' });
await pilotPanel.screenshot({ path: screenshotPath('torch-dashboard-pilot-review.png') });
await page.locator('#manager-wakes').screenshot({ path: screenshotPath('torch-dashboard-manager-wakes.png') });
await page.evaluate(() => scrollTo(0, 0));
await page.screenshot({ path: 'reports/design-system/torch-dashboard-demo-desktop.png' });
await page.screenshot({ path: screenshotPath('torch-dashboard-demo-desktop.png') });
await page.setViewportSize({ width: 390, height: 844 });
// Let responsive styles and layout finish before measuring the new viewport.
await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))));
@@ -165,13 +171,13 @@ try {
const bounds = element.getBoundingClientRect();
return { tag: element.tagName, id: element.id, className: element.className, right: bounds.right, width: bounds.width };
}).filter((element) => element.right > innerWidth + 1).slice(-30) }))));
await page.screenshot({ path: 'reports/design-system/torch-dashboard-demo-mobile.png' });
await page.screenshot({ path: screenshotPath('torch-dashboard-demo-mobile.png') });
await page.locator('.console-rail a[href="#owner-briefing"]').click();
const briefingHeading = await page.locator('#owner-briefing-title').boundingBox();
const stickyRail = await page.locator('.console-rail').boundingBox();
assert.ok(briefingHeading.y >= stickyRail.y + stickyRail.height, 'briefing anchor must clear sticky mobile navigation');
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false);
await page.locator('#owner-briefing').screenshot({ path: 'reports/design-system/torch-dashboard-owner-briefing-mobile.png' });
await page.locator('#owner-briefing').screenshot({ path: screenshotPath('torch-dashboard-owner-briefing-mobile.png') });
await page.locator('#owner-briefing .briefing-detail').first().locator('summary').click();
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false, 'expanded briefing evidence must fit mobile width');
await page.locator('#reset-demo').click();
@@ -236,7 +242,7 @@ try {
assert.equal(await livePage.locator('#task-create-form [name="title"]').inputValue(), 'My unfinished task');
await livePage.locator('#refresh-console').click();
assert.equal(await livePage.locator('#runtime-profile-model').inputValue(), 'owner-draft-model');
await livePage.screenshot({ path: 'reports/design-system/torch-dashboard-live-refresh.png' });
await livePage.screenshot({ path: screenshotPath('torch-dashboard-live-refresh.png') });
await livePage.close();
assert.deepEqual(errors, []);
assert.deepEqual(apiRequests, [], 'demo must never contact live project APIs');
+8 -6
View File
@@ -18,7 +18,7 @@ function inspectReport(policy, report, subject) {
return null;
}
function probe({ definition, subject, cwd, executor, clock }) {
function probe({ definition, subject, cwd, executor, clock, environment = {} }) {
const policy = definition.conditions;
const startedAt = clock().toISOString();
let result;
@@ -26,7 +26,8 @@ function probe({ definition, subject, cwd, executor, clock }) {
result = executor(policy.command, policy.args ?? [], {
cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
timeout: (policy.timeout_seconds ?? 30) * 1000, maxBuffer: MAX_REPORT_BYTES,
env: { ...process.env, TORCH_CHECK_COMMIT: subject.commit, TORCH_CHECK_INPUT_DIGEST: subject.inputDigest ?? '' },
env: { ...process.env, ...environment,
TORCH_CHECK_COMMIT: subject.commit, TORCH_CHECK_INPUT_DIGEST: subject.inputDigest ?? '' },
});
} catch (error) {
return { valid: false, reason: 'condition-probe-executor-failed', startedAt,
@@ -60,7 +61,7 @@ export function validConditionEvidence({ definition, conditions, commit }) {
&& (before.runtimeId === undefined || after.runtimeId === before.runtimeId);
}
export function executeConditionedCheck({ definition, subject, cwd, executor, clock }) {
export function executeConditionedCheck({ definition, subject, cwd, executor, clock, environment = {} }) {
const conditions = definition.conditions ? {
provenance: 'project-reported', subject,
required: definition.conditions.required, before: null, after: null,
@@ -68,7 +69,7 @@ export function executeConditionedCheck({ definition, subject, cwd, executor, cl
let result;
let invalidReason = null;
if (conditions) {
conditions.before = probe({ definition, subject, cwd, executor, clock });
conditions.before = probe({ definition, subject, cwd, executor, clock, environment });
if (!conditions.before.valid) return {
result: { status: null, stdout: `TORCH_CHECK_CONDITIONS ${JSON.stringify(conditions.before)}\n`,
stderr: conditions.before.reason },
@@ -78,7 +79,8 @@ export function executeConditionedCheck({ definition, subject, cwd, executor, cl
try {
result = executor(definition.command, definition.args ?? [], {
cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
env: { ...process.env, TORCH_CHECK_COMMIT: subject.commit, TORCH_CHECK_INPUT_DIGEST: subject.inputDigest ?? '' },
env: { ...process.env, ...environment,
TORCH_CHECK_COMMIT: subject.commit, TORCH_CHECK_INPUT_DIGEST: subject.inputDigest ?? '' },
});
if (result.error || result.signal) invalidReason = 'executor-failed';
} catch (error) {
@@ -86,7 +88,7 @@ export function executeConditionedCheck({ definition, subject, cwd, executor, cl
invalidReason = 'executor-failed';
}
if (conditions) {
conditions.after = probe({ definition, subject, cwd, executor, clock });
conditions.after = probe({ definition, subject, cwd, executor, clock, environment });
if (conditions.after.valid && conditions.before.report.runtimeId !== undefined
&& conditions.after.report.runtimeId !== conditions.before.report.runtimeId) {
conditions.after = { ...conditions.after, valid: false, reason: 'condition-runtime-changed' };
+86 -15
View File
@@ -1,7 +1,7 @@
import { createHash, randomUUID } from 'node:crypto';
import { spawnSync } from 'node:child_process';
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { existsSync, lstatSync, mkdirSync, readdirSync, realpathSync, readFileSync, writeFileSync } from 'node:fs';
import { join, relative, resolve } from 'node:path';
import { TorchError } from '../kernel/errors.mjs';
import { readInstallManifest } from '../kernel/install.mjs';
import { loadFleetDefinition } from '../kernel/worktrees.mjs';
@@ -35,6 +35,55 @@ function definitionHash(definition) {
return createHash('sha256').update(JSON.stringify(definition)).digest('hex');
}
function createCheckArtifactDirectory(stateRoot, runId) {
const root = realpathSync(stateRoot);
const parent = join(root, 'checks');
mkdirSync(parent, { recursive: true, mode: 0o700 });
const ownedParent = realpathSync(parent);
if (relative(root, ownedParent).startsWith('..') || lstatSync(ownedParent).isSymbolicLink()
|| !lstatSync(ownedParent).isDirectory()) {
throw new TorchError('Check artifact parent is not an owned state directory', { code: 'CHECK_ARTIFACT_INVALID' });
}
const directory = join(ownedParent, runId);
if (resolve(directory) !== directory || relative(ownedParent, directory).startsWith('..')) {
throw new TorchError('Check artifact directory escapes local state', { code: 'CHECK_ARTIFACT_INVALID' });
}
mkdirSync(directory, { mode: 0o700 });
const generatedDirectory = join(directory, 'generated');
mkdirSync(generatedDirectory, { mode: 0o700 });
return { directory, generatedDirectory };
}
function captureGeneratedArtifacts({ generatedDirectory, commit, checkId, definition, runId }) {
const files = [];
function visit(current, path = '') {
for (const name of readdirSync(current).sort()) {
const absolute = join(current, name);
const entryPath = path ? `${path}/${name}` : name;
const stat = lstatSync(absolute);
if (stat.isSymbolicLink()) throw new TorchError('Generated check artifact cannot be a symlink', {
code: 'CHECK_ARTIFACT_INVALID', details: { path: entryPath },
});
if (stat.isDirectory()) visit(absolute, entryPath);
else if (stat.isFile()) {
const bytes = readFileSync(absolute);
files.push({ path: entryPath, bytes: bytes.length,
sha256: createHash('sha256').update(bytes).digest('hex') });
} else throw new TorchError('Generated check artifact must be a regular file', {
code: 'CHECK_ARTIFACT_INVALID', details: { path: entryPath },
});
}
}
visit(generatedDirectory);
return {
directory: generatedDirectory,
candidate: { commit },
check: { id: checkId, definitionHash: definitionHash(definition) },
run: { id: runId },
files,
};
}
export class CheckService {
constructor({
repositoryRoot, controlPlane, resourceService = null,
@@ -145,6 +194,7 @@ export class CheckService {
}
const receiptId = this.idFactory();
const startedAt = this.clock().toISOString();
const artifacts = createCheckArtifactDirectory(this.stateRoot, receiptId);
beginWorktreeGuard(this.controlPlane, {
areaId: plan.areaId, type: 'check', reason: `Running ${checkId}`,
clock: this.clock, idFactory: this.idFactory,
@@ -155,6 +205,7 @@ export class CheckService {
execution = executeConditionedCheck({
definition: plan.definition, subject: { commit: plan.commit }, cwd: plan.worktree,
executor: this.executor, clock: this.clock,
environment: { TORCH_CHECK_ARTIFACT_DIR: artifacts.generatedDirectory },
});
result = execution.result;
} finally {
@@ -174,19 +225,27 @@ export class CheckService {
status = 'incomplete';
invalidReason = 'worktree-changed-during-check';
}
const artifactDirectory = join(this.stateRoot, 'checks', receiptId);
mkdirSync(artifactDirectory, { recursive: false });
const artifactPath = join(artifactDirectory, 'output.json');
let generatedArtifacts;
try {
generatedArtifacts = captureGeneratedArtifacts({ generatedDirectory: artifacts.generatedDirectory,
commit: plan.commit, checkId, definition: plan.definition, runId: receiptId });
} catch (error) {
status = 'incomplete';
invalidReason = error.code ?? 'check-artifact-invalid';
generatedArtifacts = { directory: artifacts.generatedDirectory, error: invalidReason };
}
const artifactPath = join(artifacts.directory, 'output.json');
writeFileSync(artifactPath, `${JSON.stringify({
stdout: safeOutput(result.stdout), stderr: safeOutput(result.stderr),
conditions: execution.conditions, measurementsExecuted: execution.measurementsExecuted,
generatedArtifacts,
}, null, 2)}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
const receipt = {
id: receiptId, projectId: this.controlPlane.projectId, checkId, areaId: plan.areaId,
definition: plan.definition, worktree: plan.worktree, commit: plan.commit,
startedAt, finishedAt, exitStatus: result.status ?? null, result: status,
artifactPath, invalidReason,
conditions: execution.conditions,
conditions: execution.conditions, generatedArtifacts,
};
this.controlPlane.database.prepare(`
INSERT INTO check_receipts (
@@ -278,11 +337,14 @@ export class CheckService {
if (claimed.changes !== 1) throw new TorchError('Prepared check was claimed concurrently', {
code: 'PREPARED_CHECK_UNAVAILABLE',
});
const receiptId = this.idFactory();
const artifacts = createCheckArtifactDirectory(this.stateRoot, receiptId);
const startedAt = this.clock().toISOString();
const execution = executeConditionedCheck({
definition: item.definition,
subject: { commit: item.snapshot.commit, inputDigest: item.snapshot.digest },
cwd: item.snapshot.inputRoot, executor: this.executor, clock: this.clock,
environment: { TORCH_CHECK_ARTIFACT_DIR: artifacts.generatedDirectory },
});
const result = execution.result;
let invalidReason = execution.invalidReason;
@@ -296,11 +358,19 @@ export class CheckService {
invalidReason = 'resource-lease-lost-during-check';
}
const finishedAt = this.clock().toISOString();
const receiptId = this.idFactory();
const status = invalidReason ? 'incomplete' : result.status === 0 ? 'pass' : 'fail';
const artifactDirectory = join(this.stateRoot, 'checks', receiptId);
mkdirSync(artifactDirectory, { recursive: false });
const artifactPath = join(artifactDirectory, 'output.json');
let receiptStatus = status;
let receiptInvalidReason = invalidReason;
let generatedArtifacts;
try {
generatedArtifacts = captureGeneratedArtifacts({ generatedDirectory: artifacts.generatedDirectory,
commit: item.snapshot.commit, checkId: item.checkId, definition: item.definition, runId: receiptId });
} catch (error) {
receiptStatus = 'incomplete';
receiptInvalidReason = error.code ?? 'check-artifact-invalid';
generatedArtifacts = { directory: artifacts.generatedDirectory, error: receiptInvalidReason };
}
const artifactPath = join(artifacts.directory, 'output.json');
const snapshotEvidence = {
preparedId: item.id, inputDigest: item.snapshot.digest, commit: item.snapshot.commit,
files: item.snapshot.files, provenance: item.snapshot.provenance,
@@ -309,13 +379,14 @@ export class CheckService {
writeFileSync(artifactPath, `${JSON.stringify({
stdout: safeOutput(result.stdout), stderr: safeOutput(result.stderr), snapshot: snapshotEvidence,
conditions: execution.conditions, measurementsExecuted: execution.measurementsExecuted,
generatedArtifacts,
}, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
const receipt = {
id: receiptId, projectId: this.controlPlane.projectId, checkId: item.checkId, areaId: area,
definition: item.definition, worktree: item.snapshot.sourceWorktree, commit: item.snapshot.commit,
startedAt, finishedAt, exitStatus: result.status ?? null, result: status,
artifactPath, invalidReason, snapshot: snapshotEvidence,
conditions: execution.conditions,
startedAt, finishedAt, exitStatus: result.status ?? null, result: receiptStatus,
artifactPath, invalidReason: receiptInvalidReason, snapshot: snapshotEvidence,
conditions: execution.conditions, generatedArtifacts,
};
this.controlPlane.database.prepare(`INSERT INTO check_receipts
(id, project_id, check_id, definition, definition_hash, area_id, worktree, commit_sha,
@@ -323,13 +394,13 @@ export class CheckService {
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
.run(receipt.id, receipt.projectId, receipt.checkId, JSON.stringify(receipt.definition),
definitionHash(receipt.definition), area, receipt.worktree, receipt.commit,
startedAt, finishedAt, receipt.exitStatus, status, artifactPath, invalidReason,
startedAt, finishedAt, receipt.exitStatus, receiptStatus, artifactPath, receiptInvalidReason,
execution.conditions ? JSON.stringify(execution.conditions) : null);
this.controlPlane.database.prepare(`UPDATE prepared_checks SET state = 'finished', receipt_id = ? WHERE id = ?`)
.run(receiptId, item.id);
this.controlPlane.audit({
actorId: area, operation: 'check.run-prepared', entityType: 'check-receipt', entityId: receiptId,
details: { preparedId: item.id, checkId: item.checkId, commit: receipt.commit, result: status },
details: { preparedId: item.id, checkId: item.checkId, commit: receipt.commit, result: receiptStatus },
});
disposeCheckSnapshot(item.snapshot, { stateRoot: this.stateRoot });
return receipt;
@@ -0,0 +1,113 @@
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { basename, join } from 'node:path';
import test from 'node:test';
import { CheckService } from '../../src/checks/service.mjs';
import { openControlPlane } from '../../src/control-plane/service.mjs';
import { analyzeRepository } from '../../src/kernel/analyze.mjs';
import { proposeDomains } from '../../src/kernel/domains.mjs';
import { inspectRepository } from '../../src/kernel/git.mjs';
import { installProject } from '../../src/kernel/install.mjs';
import { createWorktrees } from '../../src/kernel/worktrees.mjs';
function fixture() {
const root = mkdtempSync(join(tmpdir(), 'torch-generated-artifacts-'));
execFileSync('git', ['init', '-b', 'main', root]);
execFileSync('git', ['-C', root, 'config', 'user.email', '[email protected]']);
execFileSync('git', ['-C', root, 'config', 'user.name', 'TORCH Test']);
writeFileSync(join(root, 'package.json'), `${JSON.stringify({
name: 'generated-artifacts-fixture', scripts: { 'verify:pass': 'node -e "process.exit(0)"' },
}, null, 2)}\n`);
writeFileSync(join(root, 'app.js'), 'export const app = true;\n');
mkdirSync(join(root, 'test'));
writeFileSync(join(root, 'test', 'scenario.js'), 'export const scenario = true;\n');
execFileSync('git', ['-C', root, 'add', '.']);
execFileSync('git', ['-C', root, 'commit', '-m', 'fixture']);
let repository = inspectRepository(root);
const proposal = proposeDomains({ repository, analysis: analyzeRepository(repository) });
proposal.review = { status: 'approved', reviewedAt: '2026-09-30T00:00:00Z', reviewedBy: 'fixture-owner', notes: [] };
const env = { ...process.env, XDG_DATA_HOME: join(tmpdir(), `${basename(root)}-state`) };
installProject({ repository, proposal, env, projectId: 'generated-artifacts-fixture' });
execFileSync('git', ['-C', root, 'add', '.torch']);
execFileSync('git', ['-C', root, 'commit', '-m', 'install torch']);
repository = inspectRepository(root);
createWorktrees({ repository, parentOverride: join(tmpdir(), `${basename(root)}-worktrees`) });
return { root, env, worker: proposal.domains.find((domain) => domain.id !== 'qa').id };
}
function addCheck(context, definition) {
const path = join(context.root, '.torch', 'torch.yaml');
const config = JSON.parse(readFileSync(path, 'utf8'));
config.checks.push(definition);
writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`);
}
function treeStatus(path) {
return execFileSync('git', ['-C', path, 'status', '--porcelain'], { encoding: 'utf8' });
}
test('SCN-generated-check-artifacts: sequential exact checks retain external hashed evidence without changing tracked inputs', () => {
const context = fixture();
addCheck(context, {
id: 'emit-proof', command: process.execPath,
args: ['-e', `const fs=require('node:fs');const path=require('node:path');
const output=process.env.TORCH_CHECK_ARTIFACT_DIR;if(!output)process.exit(44);
fs.mkdirSync(path.join(output,'screenshots'),{recursive:true});
fs.writeFileSync(path.join(output,'screenshots','proof.txt'),'captured boundary evidence');`],
});
const control = openControlPlane({ repositoryRoot: context.root, env: context.env });
let id = 0;
const checks = new CheckService({ repositoryRoot: context.root, controlPlane: control,
idFactory: () => `artifact-run-${++id}` });
const expectedHash = createHash('sha256').update('captured boundary evidence').digest('hex');
try {
const receipts = [
checks.run({ checkId: 'emit-proof', areaId: context.worker }),
checks.run({ checkId: 'emit-proof', areaId: context.worker }),
];
for (const receipt of receipts) {
assert.equal(receipt.result, 'pass');
assert.equal(treeStatus(receipt.worktree), '', 'a passing check must leave its tested tree unchanged');
const retained = JSON.parse(readFileSync(receipt.artifactPath, 'utf8')).generatedArtifacts;
assert.deepEqual(retained.candidate, { commit: receipt.commit });
assert.deepEqual(retained.check, { id: 'emit-proof',
definitionHash: createHash('sha256').update(JSON.stringify(receipt.definition)).digest('hex') });
assert.deepEqual(retained.run, { id: receipt.id });
assert.deepEqual(retained.files, [{ path: 'screenshots/proof.txt', bytes: 26, sha256: expectedHash }]);
assert.equal(readFileSync(join(retained.directory, 'screenshots', 'proof.txt'), 'utf8'), 'captured boundary evidence');
}
assert.notEqual(receipts[0].generatedArtifacts.directory, receipts[1].generatedArtifacts.directory,
'each run retains its own recoverable evidence directory');
assert.equal(checks.exactPasses({ commit: receipts[0].commit, requiredChecks: ['emit-proof'] }), true);
} finally {
control.close();
}
});
test('SCN-generated-check-mutation: exit zero plus a tested-tree mutation is incomplete even when external evidence was retained', () => {
const context = fixture();
addCheck(context, {
id: 'emit-and-mutate', command: process.execPath,
args: ['-e', `const fs=require('node:fs');const path=require('node:path');
fs.writeFileSync(path.join(process.env.TORCH_CHECK_ARTIFACT_DIR,'proof.txt'),'retained despite mutation');
fs.writeFileSync('app.js','source mutation');`],
});
const control = openControlPlane({ repositoryRoot: context.root, env: context.env });
const checks = new CheckService({ repositoryRoot: context.root, controlPlane: control,
idFactory: () => 'mutation-run' });
try {
const receipt = checks.run({ checkId: 'emit-and-mutate', areaId: context.worker });
assert.equal(receipt.exitStatus, 0);
assert.equal(receipt.result, 'incomplete');
assert.equal(receipt.invalidReason, 'worktree-changed-during-check');
const retained = JSON.parse(readFileSync(receipt.artifactPath, 'utf8')).generatedArtifacts;
assert.equal(existsSync(join(retained.directory, 'proof.txt')), true);
assert.notEqual(treeStatus(receipt.worktree), '');
assert.equal(checks.exactPasses({ commit: receipt.commit, requiredChecks: ['emit-and-mutate'] }), false);
} finally {
control.close();
}
});