@@ -92,3 +92,76 @@ test('artifact identity checks do not mix handle and path stat implementations',
9292 const result = await runNode ( source ) ;
9393 assert . equal ( result . code , 0 , result . stderr || result . stdout ) ;
9494} ) ;
95+
96+ test ( 'artifact writes retain handle-bound identity checks' , async ( ) => {
97+ const source = `
98+ import { mkdtemp, open, rm } from 'node:fs/promises';
99+ import { tmpdir } from 'node:os';
100+ import { join } from 'node:path';
101+ import { writeResultArtifact } from ${ JSON . stringify ( reviewModule ) } ;
102+ const directory = await mkdtemp(join(tmpdir(), 'zcode-write-identity-'));
103+ const probe = await open(join(directory, 'probe'), 'a+');
104+ const prototype = Object.getPrototypeOf(probe);
105+ await probe.close();
106+ const originalStat = prototype.stat;
107+ let statCalls = 0;
108+ prototype.stat = async function patchedStat(...args) {
109+ const stats = await originalStat.call(this, ...args);
110+ statCalls += 1;
111+ if (statCalls !== 2) return stats;
112+ return new Proxy(stats, { get(target, property) {
113+ if (property === 'dev') return target.dev + 1;
114+ if (property === 'ino') return target.ino + 1;
115+ return Reflect.get(target, property);
116+ } });
117+ };
118+ try {
119+ await writeResultArtifact({ dataRoot: directory, workspace: directory, jobId: 'c'.repeat(64), contents: 'done' });
120+ throw new Error('artifact write unexpectedly accepted a destination identity mismatch');
121+ } catch (error) {
122+ if (error?.code !== 'ARTIFACT_WRITE_FAILED') throw error;
123+ } finally {
124+ prototype.stat = originalStat;
125+ await rm(directory, { recursive: true, force: true });
126+ }
127+ ` ;
128+ const result = await runNode ( source ) ;
129+ assert . equal ( result . code , 0 , result . stderr || result . stdout ) ;
130+ } ) ;
131+
132+ test ( 'artifact reads retain handle-bound identity checks' , async ( ) => {
133+ const source = `
134+ import { mkdtemp, open, rm } from 'node:fs/promises';
135+ import { tmpdir } from 'node:os';
136+ import { join } from 'node:path';
137+ import { readResultArtifact, writeResultArtifact } from ${ JSON . stringify ( reviewModule ) } ;
138+ const directory = await mkdtemp(join(tmpdir(), 'zcode-read-identity-'));
139+ const probe = await open(join(directory, 'probe'), 'a+');
140+ const prototype = Object.getPrototypeOf(probe);
141+ await probe.close();
142+ const artifact = await writeResultArtifact({ dataRoot: directory, workspace: directory, jobId: 'd'.repeat(64), contents: 'done' });
143+ const originalStat = prototype.stat;
144+ let statCalls = 0;
145+ prototype.stat = async function patchedStat(...args) {
146+ const stats = await originalStat.call(this, ...args);
147+ statCalls += 1;
148+ if (statCalls !== 2) return stats;
149+ return new Proxy(stats, { get(target, property) {
150+ if (property === 'dev') return target.dev + 1;
151+ if (property === 'ino') return target.ino + 1;
152+ return Reflect.get(target, property);
153+ } });
154+ };
155+ try {
156+ await readResultArtifact({ dataRoot: directory, workspace: directory, artifact });
157+ throw new Error('artifact read unexpectedly accepted a path identity mismatch');
158+ } catch (error) {
159+ if (error?.code !== 'RESULT_READ_FAILED') throw error;
160+ } finally {
161+ prototype.stat = originalStat;
162+ await rm(directory, { recursive: true, force: true });
163+ }
164+ ` ;
165+ const result = await runNode ( source ) ;
166+ assert . equal ( result . code , 0 , result . stderr || result . stdout ) ;
167+ } ) ;
0 commit comments