TASK-pilot-executor-outcomes converge artifact checks

This commit is contained in:
thePR0M3TH3AN committed 2026-10-01 02:30:00 -04:00
commit 286bfa72e3
6 files changed
+294 -38

No files matched your search

+33
View File
@@ -2,6 +2,39 @@
Repository state outranks conversation memory. Query ownership before crossing a domain boundary.
## Fresh owner authorization: guarded integration and continued development (2026-10-01)
The owner explicitly answered the arbiter's two held decisions: "yes do whatever
you need to do. And you can push commits to." This supersedes the earlier
no-landing/no-push restrictions ONLY for continued TORCH development on
rewrite/portable-agent-fleet and task-owned specialist branches. It is fresh
human consent, not an automatic goal continuation.
Session Manager may resolve owner approval 2b2dfb35-46ac-4adb-ac14-5680f7a85e80,
authorize and land exact request cce22c51-1fa8-4324-8e28-bf8cbfc56165 through
TORCH's guarded serialized integration path after checking clean canonical state,
exact candidate, ancestry and required PASS receipts. Subsequent QA-accepted
development candidates may use that same checked path; never waive gates.
The arbiter may push verified commits to origin/rewrite/portable-agent-fleet
without force, after checking the remote boundary and test evidence.
QA may retry interrupted decision 406ff30e-b317-4733-b39e-f63c9a57506d. QA must
independently recheck the exact five PNG paths, candidate HEAD, current/archive
hashes and clean-before-run provenance. Only Project Kernel may apply an approved
five-path restoration; keep the archive and old unqualified receipts. The earlier
c2a1702b rejection stays rejected. This is not arbitrary cleanup authority.
Continue resolving the existing development backlog under domain ownership,
assignment continuity, peer consent and independent QA. Finish the reliability
dependencies first. Manager coordinates and records actual outcomes; the arbiter
still controls provider starts and installation changes. Automatic wakes remain
paused until executor qualification and an explicit safe activation decision.
No default-main/legacy changes, deployment, package publication, spending/model
changes, destructive cleanup, arbitrary new identities or hierarchy activation
are granted. Preserve existing unique commits and user changes. Read this current
canonical brief before acting; old conversation restrictions cannot conceal this
new consent, and this consent cannot conceal failed checks.
## Routine work stays inside the fleet
The owner authorizes routine coordination within existing task/role authority:
+31
View File
@@ -140,6 +140,37 @@ test_integrity_note:
if_true_explain_spec_basis: ""
```
## 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();
}
});