Files
gstack/test/autoplan-public-narration.test.ts
T
garrytan 7cbb5e04e4 test: derive paid touchfiles from each eval's static closure (E)
touchfiles.test.ts now checks, per key, that the paid file's static
test/helpers and test/fixtures closure (plus fixture paths it names in string
literals) is covered, and names the file, path, chain and key to fix when it is
not. Free *.test.ts files are no longer touchfiles, so editing a free replay
test stops selecting paid evals: 950 entries removed, 653 real closure paths
added. The hand-copied inventories go: periodic-fixture-selection,
fake-impeccable-touchfiles and 45 per-file selection examples. Selection for
the sample edits (plan-eng-review template, claude-pty-runner,
plan-count-fixture, gstack-config) loses no case under either profile.
CONTRIBUTING documents the rule and its lower bound.
2026-09-29 07:00:28 +00:00

125 lines
7.8 KiB
TypeScript

import {expect,test} from 'bun:test';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import {readPlanCountTranscript,type NativePublicToolEvent} from './helpers/plan-count-transcript';
import {autoplanPhaseCompletions} from './helpers/autoplan-phase-observer';
import fixture from './fixtures/autoplan-public-narration-ad.json';
const at=Date.parse(fixture.provenance.timestamp);
function read(blocks: unknown[]= [fixture.block],delta: any={},complete=true) {
const dir=fs.mkdtempSync(path.join(os.tmpdir(),'public-narration-')),cwd=path.join(dir,'repo');
const project=path.join(dir,'projects','owned');fs.mkdirSync(project,{recursive:true});
const record={cwd,isSidechain:false,sessionId:fixture.provenance.sessionId,
timestamp:fixture.provenance.timestamp,uuid:fixture.provenance.uuid,
message:{role:'assistant',content:blocks},...delta};
const file=path.join(project,fixture.provenance.sessionId+'.jsonl');
fs.writeFileSync(file,JSON.stringify(record)+(complete?'\n':''));
const tools:NativePublicToolEvent[]=[];
try{return {transcript:readPlanCountTranscript(dir,cwd,event=>tools.push(event)),tools};}
finally{fs.rmSync(dir,{recursive:true,force:true});}
}
test('actual public server narration is projected at its original session and timestamp',()=>{
const {transcript,tools}=read();
expect(transcript.assistantMessages).toEqual([{sessionId:fixture.provenance.sessionId,
timestamp:fixture.provenance.timestamp,text:fixture.block.thinking}]);
expect(transcript.calls).toEqual([]);expect(tools).toEqual([]);
expect(autoplanPhaseCompletions(transcript,at-1)).toEqual([{phase:1,ts:at}]);
});
test('actual Phase 1 is done declaration independently matches the phase marker',()=>{
expect(autoplanPhaseCompletions({status:'ready',calls:[],assistantMessages:[{
sessionId:fixture.provenance.sessionId,timestamp:fixture.provenance.timestamp,
text:fixture.block.thinking}]},at-1)).toEqual([{phase:1,ts:at}]);
});
// Minimal synthetic protobuf envelopes exercise public-tag classification only.
// No opaque native signature or untagged model text is stored in this fixture.
const vi=(n:number):number[]=>{const bytes:number[]=[];do{const b=n%128;n=Math.floor(n/128);bytes.push(b+(n?128:0));}while(n);return bytes;};
const field=(n:number,body:Uint8Array)=>Buffer.from([...vi(n*8+2),...vi(body.length),...body]);
const tagged=(kind='narration')=>field(2,field(1,field(8,Buffer.from(kind))));
const summary=(signature=fixture.block.signature,thinking=fixture.block.thinking)=>({type:'thinking',thinking,signature});
const encode=(bytes:Uint8Array)=>Buffer.from(bytes).toString('base64');
test('unknown or malformed signature envelopes never become public narration',()=>{
const invalid:unknown[]=[undefined,null,'',4,'narration','%%%',' '+fixture.block.signature,
fixture.block.signature+'=',encode(tagged('reasoning')),encode(tagged('Narration')),
encode(field(1,field(1,field(8,Buffer.from('narration'))))),
encode(field(2,field(2,field(8,Buffer.from('narration'))))),
encode(field(2,field(1,field(7,Buffer.from('narration'))))),
encode(tagged().subarray(0,-1)),encode(Buffer.from([0x12,0x80])),
encode(Buffer.from([0x12,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f])),
encode(Buffer.concat([tagged(),Buffer.from([0])])),
encode(Buffer.concat([tagged(),Buffer.from([0x0f])])),
encode(Buffer.concat([tagged(),tagged('reasoning')])),
encode(field(2,Buffer.concat([field(1,field(8,Buffer.from('narration'))),field(1,field(8,Buffer.from('reasoning')))]))),
encode(field(2,field(1,Buffer.concat([field(8,Buffer.from('narration')),field(8,Buffer.from('reasoning'))])))),
encode(field(2,field(1,Buffer.concat([field(8,Buffer.from('narration')),field(8,Buffer.from('narration'))])))),
encode(Buffer.concat([tagged(),field(3,Buffer.alloc(64*1024))])),
];
for(const signature of invalid){
const {transcript,tools}=read([{...summary(),signature}]);
expect(transcript.assistantMessages,JSON.stringify(signature)?.slice(0,80)).toEqual([]);
expect(transcript.calls).toEqual([]);expect(tools).toEqual([]);
}
});
test('supported unrelated envelope fields preserve an exact public tag',()=>{
const bytes=Buffer.concat([Buffer.from([0x08,0x01]),tagged(),field(3,Buffer.from('opaque'))]);
expect(read([summary(encode(bytes))]).transcript.assistantMessages[0]?.text).toBe(fixture.block.thinking);
});
test('private, empty and wrong-kind blocks remain outside the public projection',()=>{
for(const block of [{type:'thinking',thinking:'SYNTHETIC_PRIVATE_TEXT'},
{...summary(),signature:encode(tagged('reasoning')),thinking:'SYNTHETIC_PRIVATE_TEXT'},
{...summary(),thinking:''},{...summary(),thinking:' '},{...summary(),thinking:7},
{...summary(),type:'redacted_thinking'},{...summary(),type:'tool_result'},
{type:'thinking',thinking:'SYNTHETIC_PRIVATE_TEXT',block_kind:'narration'}]){
expect(read([block]).transcript.assistantMessages).toEqual([]);
}
});
test('parent role, cwd, native filename and complete valid timestamp remain required',()=>{
for(const delta of [{cwd:'/foreign'},{sessionId:'foreign'},
{isSidechain:true},{isSidechain:undefined},{timestamp:'invalid'},
{timestamp:null},{message:{role:'user',content:[fixture.block]}},
{message:{role:'system',content:[fixture.block]}}]){
expect(read([fixture.block],delta).transcript.assistantMessages).toEqual([]);
}
expect(read([fixture.block],{},false).transcript.assistantMessages).toEqual([]);
});
test('public narration does not manufacture questions, replies or plan approval',()=>{
const {transcript,tools}=read([summary(),{type:'text',text:'Ordinary assistant prose.'},
{type:'thinking',thinking:'SYNTHETIC_PRIVATE_TEXT'},
{type:'tool_use',id:'actual-read',name:'Read',input:{file_path:'/owned/PLAN.md'}}]);
expect(transcript.assistantMessages.map(m=>m.text)).toEqual([fixture.block.thinking,'Ordinary assistant prose.']);
expect(transcript.calls).toEqual([]);expect(transcript.planReadyRequests).toBeUndefined();
expect(tools.map(e=>[e.kind,e.toolUseId,e.name])).toEqual([['use','actual-read','Read']]);
});
function hits(text:string,start=at-1,timestamp=fixture.provenance.timestamp){
return autoplanPhaseCompletions({status:'ready',calls:[],assistantMessages:[{
sessionId:fixture.provenance.sessionId,timestamp,text}]},start);
}
test('new done declarations retain exact phase numbers, punctuation and timestamps',()=>{
for(const phase of [1,2,2.5,3])for(const tail of ['', '.', ': Work retained.', '. Work retained.']){
expect(hits(`Phase ${phase} is done${tail}`)).toEqual([{phase,ts:at}]);
}
expect(hits('**Phase 1 is done.**')).toEqual([{phase:1,ts:at}]);
expect(hits(fixture.block.thinking,at+1)).toEqual([]);
expect(hits(fixture.block.thinking,at-1,'invalid')).toEqual([]);
expect(hits('Phase 1 complete. Work retained.')).toEqual([{phase:1,ts:at}]);
});
test('source examples, questions, promises and quoted done markers are not phase completion',()=>{
for(const text of ['Phase 1 is done?', 'Phase 1 is done eventually', 'Phase 1 is not done.',
'Once Phase 1 is done, continue.', 'Phase 1 will be done.', 'Phase 4 is done.',
'Phase 2.1 is done.', '# Phase 1 is done.', '> Phase 1 is done.',
'- Phase 1 is done.', '| Phase 1 is done. |', ' Phase 1 is done.',
'```text\nPhase 1 is done.\n```', '~~~text\nPhase 1 is done.\n~~~',
'Example:\nPhase 1 is done.\nPhase 2 is done.',
'Emit phase-transition summary: Phase 1 is done.',
'**Phase 1 is done** if the tests pass.']) expect(hits(text),text).toEqual([]);
});
test('phase ordering and duplicate collapse use native time rather than polling order',()=>{
const t={status:'ready' as const,calls:[],assistantMessages:[
{sessionId:'parent',timestamp:new Date(at+20).toISOString(),text:'Phase 2 is done.'},
{sessionId:'parent',timestamp:new Date(at+10).toISOString(),text:fixture.block.thinking},
{sessionId:'parent',timestamp:new Date(at+30).toISOString(),text:'Phase 1 is done.'}]};
expect(autoplanPhaseCompletions(t,at)).toEqual([{phase:1,ts:at+10},{phase:2,ts:at+20}]);
});