diff --git a/test/autoplan-dual-voice-evidence.test.ts b/test/autoplan-dual-voice-evidence.test.ts index 6d7c12827..2c01b9221 100644 --- a/test/autoplan-dual-voice-evidence.test.ts +++ b/test/autoplan-dual-voice-evidence.test.ts @@ -335,3 +335,16 @@ test.each(['missing-native','foreign-outside-result','changed-prompt','changed-o expect(f.read().codexAttempted,kind).toBe(false); } }); +test('outside-voice failure reasons name the probe identity, mode and canonical match',()=>{ + const withoutOutside=()=>{const f=fixture();f.events.splice(6);return f;}; + const probeReason=(f:ReturnType)=>f.read().reasons.find(reason=>reason.startsWith('probeToolUseId=')); + let f=withoutOutside(); + expect(probeReason(f)).toBe('probeToolUseId=probe probeMode=ready canonicalMatch=yes (mode recorded; 0 non-canonical Bash call(s) mention CODEX_MODE)'); + f=withoutOutside();f.events[0]!.message.content[0].input.command='echo probing\n'+f.options.commands.probe; + expect(probeReason(f)).toBe('probeToolUseId=none probeMode=none canonicalMatch=no (no Bash call matched the canonical probe block; 1 non-canonical Bash call(s) mention CODEX_MODE)'); + f=withoutOutside();f.events[1]=ack('probe','CODEX_MODE: not_installed\nextra trailing output'); + expect(probeReason(f)).toBe('probeToolUseId=probe probeMode=none canonicalMatch=yes (probe output has 1 CODEX_MODE line(s) and does not end with it; 0 non-canonical Bash call(s) mention CODEX_MODE)'); + f=withoutOutside();f.events[1]=ack('probe','CODEX_MODE: not_installed',true); + expect(probeReason(f)).toContain('canonicalMatch=yes (probe result is an error;'); + expect(fixture().read().reasons).toEqual([]); +}); diff --git a/test/helpers/autoplan-dual-voice-evidence.ts b/test/helpers/autoplan-dual-voice-evidence.ts index 6263476ff..58fb1ec97 100644 --- a/test/helpers/autoplan-dual-voice-evidence.ts +++ b/test/helpers/autoplan-dual-voice-evidence.ts @@ -160,13 +160,22 @@ export function autoplanDualVoiceEvidence(transcript: unknown[], options: Autopl } return seen.size > 0; }; + let probeResult = 'no Bash call matched the canonical probe block'; + let nonCanonicalProbes = 0; for (const call of calls.values()) { - if (call.name !== 'Bash' || typeof call.input.command !== 'string' || !canonical(call.input.command, options.commands.probe)) continue; + if (call.name !== 'Bash' || typeof call.input.command !== 'string') continue; + if (!canonical(call.input.command, options.commands.probe)) { + if (call.input.command.includes('CODEX_MODE')) nonCanonicalProbes++; + continue; + } result.probeToolUseId = call.id; delete result.probeMode; - if (!call.result || call.result.error) continue; + if (!call.result || call.result.error) { probeResult = call.result ? 'probe result is an error' : 'probe has no result'; continue; } const modes = [...call.result.content.matchAll(/^CODEX_MODE: ([a-z_]+)\r?$/gm)]; - if (modes.length !== 1 || !call.result.content.trimEnd().endsWith(modes[0]![0])) continue; - result.probeToolUseId = call.id; result.probeMode = modes[0]![1]; + if (modes.length !== 1 || !call.result.content.trimEnd().endsWith(modes[0]![0])) { + probeResult = `probe output has ${modes.length} CODEX_MODE line(s) and ${modes.length === 1 ? 'does not end with it' : 'needs exactly one'}`; + continue; + } + result.probeToolUseId = call.id; result.probeMode = modes[0]![1]; probeResult = 'mode recorded'; } const native: Array<{ call: Call; snapshot: any; content: string }> = []; for (const call of calls.values()) { @@ -226,6 +235,10 @@ export function autoplanDualVoiceEvidence(transcript: unknown[], options: Autopl } result.codexUnavailable ||= result.claudeVoiceFired && ['not_installed', 'not_authed', 'broken_install', 'model_unusable'].includes(result.probeMode ?? ''); if (!result.claudeVoiceFired) result.reasons.push('No acknowledged current CEO phase dispatch'); - if (!result.codexVoiceFired && !result.codexUnavailable) result.reasons.push('No acknowledged outside execution or actual unavailable probe result'); + if (!result.codexVoiceFired && !result.codexUnavailable) { + result.reasons.push('No acknowledged outside execution or actual unavailable probe result'); + result.reasons.push(`probeToolUseId=${result.probeToolUseId ?? 'none'} probeMode=${result.probeMode ?? 'none'} ` + + `canonicalMatch=${result.probeToolUseId ? 'yes' : 'no'} (${probeResult}; ${nonCanonicalProbes} non-canonical Bash call(s) mention CODEX_MODE)`); + } return result; } diff --git a/test/helpers/shared-libs-eval-fixture.ts b/test/helpers/shared-libs-eval-fixture.ts index dd3734b08..67bf04807 100644 --- a/test/helpers/shared-libs-eval-fixture.ts +++ b/test/helpers/shared-libs-eval-fixture.ts @@ -477,6 +477,81 @@ export function isInternalClaudeGitRequest(request: SourceRequest, commands: str !commands.some(command => command.includes('core.safecrlf=false') || command.includes('protocol.ext.allow=never')); } +/** Older open PRs outside the 14-day window: more than the skill's five 100-item open-metadata pages. */ +export const SHARED_LIBS_OLDER_OPEN_PRS = 600; + +/** One finite PR world, newest update first. Self-contained so the fixture executable uses this exact table. */ +function sharedPullRequestTable(now: string, olderOpen: number) { + const hour = 3_600_000, newestOlder = Date.UTC(2025, 0, 1); + return [ + { number: 7, state: 'open', updated: now }, + ...Array.from({ length: olderOpen }, (_, i) => ({ number: 100 + olderOpen - 1 - i, state: 'open', + updated: new Date(newestOlder - i * hour).toISOString() })), + { number: 42, state: 'open', updated: '2020-01-01T00:00:00Z' }, + ...[5, 4, 3].map((number, i) => ({ number, state: 'closed', updated: new Date(Date.UTC(2019, 5, 1) - i * hour).toISOString() })), + ]; +} + +/** gh pr list, pulls?state= and search/issues as views of the same table; null for any other request. */ +function sharedPullRequestView(table: Array<{ number: number; state: string; updated: string; title: string; body: string }>, + args: string[], endpoint: string) { + const [route, search = ''] = endpoint.replace(/^\//, '').split('?'); + const params = new URLSearchParams(search); + const flag = (names: string[]) => { + for (let i = 0; i < args.length; i++) { + if (names.includes(args[i]!)) return args[i + 1]; + const joined = names.find(name => name.startsWith('--') && args[i]!.startsWith(name + '=')); + if (joined) return args[i]!.slice(joined.length + 1); + } + return undefined; + }; + const matches = (query: string) => { + let state = '', rest = query; + const dates: Array<(pr: { updated: string }) => boolean> = []; + const words: string[] = []; + for (const term of rest.split(/\s+/).filter(Boolean)) { + const qualifier = /^(is|state|type|updated|created|repo):(.+)$/i.exec(term); + if (!qualifier) { words.push(term.replace(/^"|"$/g, '').toLowerCase()); continue; } + const [, key, value] = qualifier as unknown as [string, string, string]; + if (/^(?:is|state)$/i.test(key) && /^(?:open|closed|merged)$/i.test(value)) state = value.toLowerCase(); + else if (/^(?:is|type)$/i.test(key) && /^issue$/i.test(value)) return () => false; + else if (/^repo$/i.test(key) && value.toLowerCase() !== 'fixture/shared-libs') return () => false; + else if (/^(?:updated|created)$/i.test(key)) { + const range = /^(.+)\.\.(.+)$/.exec(value), op = /^(>=|<=|>|<)?(.+)$/.exec(value)!; + const at = (text: string) => Date.parse(text); + if (range) dates.push(pr => at(pr.updated) >= at(range[1]!) && at(pr.updated) <= at(range[2]!) + 86_399_999); + else dates.push(pr => { const t = at(pr.updated), v = at(op[2]!); + return op[1] === '>=' ? t >= v : op[1] === '>' ? t > v : op[1] === '<=' ? t <= v + 86_399_999 : op[1] === '<' ? t < v : t >= v && t <= v + 86_399_999; }); + } + } + return (pr: { state: string; updated: string; title: string; body: string }) => + (!state || pr.state === state) && dates.every(check => check(pr)) && + words.every(word => (pr.title + ' ' + pr.body).toLowerCase().includes(word)); + }; + const page = (rows: typeof table, perPage: number, number: number) => { + const size = Math.min(100, Math.max(1, perPage || 30)); + return rows.slice((Math.max(1, number || 1) - 1) * size, Math.max(1, number || 1) * size); + }; + if (args[0] === 'pr' && args[1] === 'list') { + const state = (flag(['--state', '-s']) || 'open').toLowerCase(); + const rows = table.filter(pr => state === 'all' || pr.state === state).filter(matches(flag(['--search', '-S']) || '')); + return rows.slice(0, Math.max(1, Number(flag(['--limit', '-L']) || 30))); + } + if (/^repos\/fixture\/shared-libs\/pulls$/.test(route!)) { + const state = (params.get('state') || 'open').toLowerCase(); + const rows = table.filter(pr => state === 'all' || pr.state === state); + if (params.get('direction') === 'asc') rows.reverse(); + return page(rows, Number(params.get('per_page')), Number(params.get('page'))); + } + if (route === 'search/issues') { + const rows = table.filter(matches(params.get('q') || '')); + if (params.get('order') === 'asc') rows.reverse(); + return { total_count: rows.length, incomplete_results: false, + items: page(rows, Number(params.get('per_page')), Number(params.get('page'))) }; + } + return null; +} + export function installSourceShims(f: SharedLibsFixture, opts: { unsupportedGit?: boolean; unavailableApi?: boolean; prCoverage?: boolean; } = {}): void { @@ -567,24 +642,25 @@ const sources=${JSON.stringify(sources)}; const base={name:'main',sha:${JSON.stringify(f.tip)}}; const pr=(number,date,extra={})=>({number,state:'open',title:number===42?'Extract retry parsing into existing helper':'Routine documentation '+number,body:number===7?'Coordination: https://github.com/fixture/shared-libs/pull/42':'',created_at:date,updated_at:date,merged_at:null,createdAt:date,updatedAt:date,mergedAt:null,url:'https://github.com/fixture/shared-libs/pull/'+number,html_url:'https://github.com/fixture/shared-libs/pull/'+number,head:{sha:number===42?prHead:${JSON.stringify(f.tip)},ref:'feature-'+number},base:{sha:${JSON.stringify(f.tip)},ref:'main'},...extra}); const page=Number((endpoint.match(/[?&]page=(\\d+)/)||[])[1]||a[a.indexOf('-F')+1]?.match(/^page=(\\d+)/)?.[1]||1); -let out; +const view=${sharedPullRequestView.toString()}; +const prTable=${!!opts.prCoverage}?(${sharedPullRequestTable.toString()})(now,${SHARED_LIBS_OLDER_OPEN_PRS}).map(row=>({...row,title:pr(row.number,row.updated).title,body:pr(row.number,row.updated).body})):[]; +const toPr=row=>pr(row.number,row.updated,row.state==='closed'?{state:'closed',closed_at:row.updated}:{}); +let out,listing; if(a[0]==='auth')process.exit(0); else if(a[0]==='repo') out={nameWithOwner:'fixture/shared-libs',defaultBranchRef:base,url:'https://github.com/fixture/shared-libs'}; -else if(a[0]==='pr'&&a[1]==='list')out=${!!opts.prCoverage}?[pr(7,now),pr(42,old)]:[]; +else if((listing=view(prTable,a,endpoint))!==null)out=Array.isArray(listing)?listing.map(toPr):{...listing,items:listing.items.map(row=>({...toPr(row),pull_request:{url:'https://api.github.com/repos/fixture/shared-libs/pulls/'+row.number}}))}; else if(a[0]==='pr'&&a[1]==='view')out=pr(Number(a[2])||42,Number(a[2])===7?now:old,{files:[{path:Number(a[2])===7?'docs/unrelated.md':'src/retry-worker.ts'}]}); -else if(endpoint.includes('search/issues'))out={total_count:${opts.prCoverage ? 1 : 0},incomplete_results:false,items:${!!opts.prCoverage}?[pr(7,now)]:[]}; -else if(endpoint.includes('/contents/')) { const p=decodeURIComponent(endpoint.split('/contents/')[1].split('?')[0]); const ref=decodeURIComponent((endpoint.match(/[?&]ref=([^&]+)/)||[])[1]||'');if(!Object.hasOwn(sources,ref))apiError(404,'unsupported or unpinned fixture revision');const source=sources[ref];if(!Object.hasOwn(source.files,p))apiError(404,'source unavailable');out={path:p,encoding:'base64',content:source.files[p],sha:source.blobs[p]}; } +else if(/\\/contents(?:\\/|\\?|$)/.test(endpoint)) { const p=decodeURIComponent(endpoint.split(/\\/contents/)[1].split('?')[0]).replace(/^\\/+|\\/+$/g,''); const ref=decodeURIComponent((endpoint.match(/[?&]ref=([^&]+)/)||[])[1]||'');if(!Object.hasOwn(sources,ref))apiError(404,'unsupported or unpinned fixture revision');const source=sources[ref]; + if(Object.hasOwn(source.files,p))out={type:'file',name:p.split('/').pop(),path:p,encoding:'base64',content:source.files[p],sha:source.blobs[p]}; + else { const prefix=p?p+'/':'';const names=[...new Set(Object.keys(source.files).filter(file=>file.startsWith(prefix)).map(file=>file.slice(prefix.length).split('/')[0]))].sort();if(!names.length)apiError(404,'source unavailable');out=names.map(name=>{const file=prefix+name;return Object.hasOwn(source.files,file)?{type:'file',name,path:file,sha:source.blobs[file]}:{type:'dir',name,path:file};}); } } else if(/\\/pulls\\/42\\/files/.test(endpoint))out=page===1?Array.from({length:100},(_,i)=>({filename:'docs/coordination-'+i+'.md',status:'added',patch:'@@ -0,0 +1 @@\\n+Documentation coordination '+i+'.'})):page===2?[{filename:'src/retry-worker.ts',status:'modified',patch:${JSON.stringify(prPatch)}}]:[]; else if(/\\/pulls\\/\\d+\\/files/.test(endpoint))out=page===1?[{filename:'docs/unrelated.md',status:'modified',patch:'@@ -1 +1 @@\\n-old\\n+new'}]:[]; else if(/\\/pulls\\/42(?:\\?|$)/.test(endpoint))out=pr(42,old); -else if(endpoint.includes('/pulls')) { - if(!${!!opts.prCoverage})out=[]; - else if(endpoint.includes('state=open'))out=Array.from({length:100},(_,i)=>pr((page-1)*100+i+40,old)); - else out=page===1?[pr(7,now),pr(42,old)]:[]; -} +else if(/\\/pulls\\/\\d+(?:\\?|$)/.test(endpoint)){const row=prTable.find(row=>row.number===Number(endpoint.match(/\\/pulls\\/(\\d+)/)[1]));if(!row)apiError(404,'Not Found');out=toPr(row);} else if(endpoint.includes('/commits')){const isPrCommit=prHead!==${JSON.stringify(f.tip)}&&endpoint.includes(prHead);out=endpoint.includes('/commits/')?{sha:isPrCommit?prHead:${JSON.stringify(f.tip)},commit:{committer:{date:isPrCommit?old:now},message:'Fixture work'},files:Object.keys(isPrCommit?prFiles:files).map(filename=>({filename,status:'modified'}))}:[{sha:${JSON.stringify(f.tip)},commit:{committer:{date:now},message:'Fixture work'}}];} else if(endpoint.includes('/branches/'))out={name:'main',commit:{sha:${JSON.stringify(f.tip)}}}; -else out={default_branch:'main',full_name:'fixture/shared-libs',html_url:'https://github.com/fixture/shared-libs'}; +else if(/^\\/?repos\\/fixture\\/shared-libs\\/?(?:\\?|$)/.test(endpoint))out={default_branch:'main',full_name:'fixture/shared-libs',html_url:'https://github.com/fixture/shared-libs'}; +else apiError(404,'Not Found'); if(curl)curlResponse(out); const qi=a.findIndex(x=>x==='--jq'||x==='-q'); if(qi>=0) {const r=cp.spawnSync('jq',['-r',a[qi+1]],{input:JSON.stringify(out),encoding:'utf8',timeout:30_000});process.stdout.write(r.stdout||'');process.stderr.write(r.stderr||'');process.exit(r.status??1);} diff --git a/test/shared-libs-fixture.test.ts b/test/shared-libs-fixture.test.ts index 40ca87a41..292382b82 100644 --- a/test/shared-libs-fixture.test.ts +++ b/test/shared-libs-fixture.test.ts @@ -7,7 +7,7 @@ import { execFileSync, spawnSync } from 'node:child_process'; import { createSharedInteractiveToolHandler, createSharedLibsFixture, fixtureGit, fixtureWrite, installSourceShims, readRequests, seedOpportunitySources, sharedReadOnlyViolations, shellQuote, snapshotFixture, type SharedLibsFixture, - SharedCaptureAccumulator, type SharedCaptureAttempt, isInternalClaudeGitRequest, + SharedCaptureAccumulator, type SharedCaptureAttempt, isInternalClaudeGitRequest, SHARED_LIBS_OLDER_OPEN_PRS, } from './helpers/shared-libs-eval-fixture'; import { EvalCollector, type EvalTestEntry } from './helpers/eval-store'; import { collectorOutcomeCounts } from '../scripts/test-paid-shards'; @@ -955,6 +955,143 @@ function pinnedSource(f: SharedLibsFixture, revision: string, file: string) { return bytes; } +function ghCli(f: SharedLibsFixture, args: string[]) { + return spawnSync(path.join(f.bin, 'gh'), args, { + cwd: f.repo, env: { ...process.env, ...f.env }, encoding: 'utf8', timeout: 10_000, + }); +} + +describe('shared-code PR coverage world', () => { + const numbers = (stdout: string) => (JSON.parse(stdout) as Array<{ number: number }>).map(pr => pr.number); + const pulls = (query: string) => `repos/fixture/shared-libs/pulls?${query}`; + + test('gh pr list, pulls?state= and search/issues page one finite table in the same order', () => { + const f = createSharedLibsFixture('pr-world'); + cleanup.push(f.root); + seedOpportunitySources(f); + installSourceShims(f, { prCoverage: true }); + const scan = (state: string) => { + const rows: number[] = []; + for (let page = 1; page < 20; page++) { + const endpoint = pulls(`state=${state}&sort=updated&direction=desc&per_page=100&page=${page}`); + const response = gh(f, endpoint); + expect(response.status, response.stderr).toBe(0); + const batch = numbers(response.stdout); + // PR 7's activity timestamp is the request time; compare the table rows. + expect(numbers(curl(f, ['-sS', `https://api.github.com/${endpoint}`]).stdout)).toEqual(batch); + rows.push(...batch); + if (batch.length < 100) return { rows, pages: page }; + } + throw new Error(`state=${state} listing never produced a short last page`); + }; + const open = scan('open'), closed = scan('closed'), all = scan('all'); + const olderOpen = SHARED_LIBS_OLDER_OPEN_PRS; + expect(open.rows).toHaveLength(olderOpen + 2); + expect(new Set(open.rows).size).toBe(open.rows.length); + expect(open.rows[0]).toBe(7); + expect(open.rows.at(-1)).toBe(42); + expect(closed.rows).toEqual([5, 4, 3]); + expect(all.rows).toEqual([...open.rows, ...closed.rows]); + expect(open.pages).toBe(Math.floor((olderOpen + 2) / 100) + 1); + expect(numbers(gh(f, pulls('page=1')).stdout)).toEqual(open.rows.slice(0, 30)); + expect(numbers(gh(f, pulls('state=open&direction=asc&per_page=100&page=1')).stdout)).toEqual(open.rows.slice().reverse().slice(0, 100)); + + for (const [state, rows] of [['open', open.rows], ['closed', closed.rows], ['all', all.rows]] as const) { + const listed = ghCli(f, ['pr', 'list', '--state', state, '--limit', '10000']); + expect(listed.status, listed.stderr).toBe(0); + expect(numbers(listed.stdout)).toEqual([...rows]); + } + expect(numbers(ghCli(f, ['pr', 'list']).stdout)).toEqual(open.rows.slice(0, 30)); + expect(numbers(ghCli(f, ['pr', 'list', '-L', '5']).stdout)).toEqual(open.rows.slice(0, 5)); + + const searched: number[] = []; + for (let page = 1; ; page++) { + const response = gh(f, `search/issues?q=${encodeURIComponent('repo:fixture/shared-libs is:pr is:open')}&per_page=100&page=${page}`); + expect(response.status, response.stderr).toBe(0); + const result = JSON.parse(response.stdout); + expect(result.total_count).toBe(open.rows.length); + searched.push(...result.items.map((item: { number: number }) => item.number)); + if (result.items.length < 100) break; + } + expect(searched).toEqual(open.rows); + const since = new Date(Date.now() - 14 * 86_400_000).toISOString().slice(0, 10); + const recent = JSON.parse(gh(f, `search/issues?q=${encodeURIComponent(`repo:fixture/shared-libs is:pr updated:>=${since}`)}`).stdout); + expect(recent.items.map((item: { number: number }) => item.number)).toEqual([7]); + expect(recent.total_count).toBe(1); + }); + + test('the maximum authorized open-metadata scan still leaves older open PRs unchecked', () => { + const periodic = fs.readFileSync(path.join(import.meta.dir, 'skill-e2e-shared-libs-periodic.test.ts'), 'utf8'); + expect(periodic).toContain('expect(openPages.length).toBeLessThanOrEqual(5);'); + const f = createSharedLibsFixture('pr-world-budget'); + cleanup.push(f.root); + seedOpportunitySources(f); + installSourceShims(f, { prCoverage: true }); + const scanned: number[] = []; + for (let page = 1; page <= 5; page++) { + const batch = numbers(gh(f, pulls(`state=open&sort=updated&direction=desc&per_page=100&page=${page}`)).stdout); + expect(batch).toHaveLength(100); + scanned.push(...batch); + } + const everyOpen = numbers(ghCli(f, ['pr', 'list', '--state', 'open', '--limit', '10000']).stdout); + const unchecked = everyOpen.filter(number => !scanned.includes(number)); + expect(unchecked.length).toBeGreaterThan(0); + expect(unchecked).toContain(42); + // The linked PR stays reachable directly, outside the metadata budget. + const direct = gh(f, 'repos/fixture/shared-libs/pulls/42'); + expect(JSON.parse(direct.stdout)).toMatchObject({ number: 42, state: 'open', updated_at: '2020-01-01T00:00:00Z' }); + expect(JSON.parse(gh(f, 'repos/fixture/shared-libs/pulls/7').stdout).number).toBe(7); + }); + + test('pinned contents agree across gh and curl for files and directories; unknown endpoints 404', () => { + const f = createSharedLibsFixture('pr-world-contents'); + cleanup.push(f.root); + seedOpportunitySources(f); + installSourceShims(f, { prCoverage: true }); + const root = gh(f, `repos/fixture/shared-libs/contents/?ref=${f.tip}`); + expect(root.status, root.stderr).toBe(0); + expect(JSON.parse(root.stdout)).toContainEqual({ type: 'dir', name: 'src', path: 'src' }); + expect(gh(f, `repos/fixture/shared-libs/contents?ref=${f.tip}`).stdout).toBe(root.stdout); + for (const entry of ['src', 'src/retry-route.ts']) { + const endpoint = `repos/fixture/shared-libs/contents/${entry}?ref=${f.tip}`; + const viaGh = gh(f, endpoint), viaCurl = curl(f, ['-sS', `https://api.github.com/${endpoint}`]); + expect(viaGh.status, viaGh.stderr).toBe(0); + expect(viaCurl.status, viaCurl.stderr).toBe(0); + expect(viaCurl.stdout).toBe(viaGh.stdout); + } + const listing = JSON.parse(gh(f, `repos/fixture/shared-libs/contents/src?ref=${f.tip}`).stdout); + const file = listing.find((entry: { name: string }) => entry.name === 'retry-route.ts'); + expect(file).toMatchObject({ type: 'file', path: 'src/retry-route.ts' }); + expect(JSON.parse(gh(f, `repos/fixture/shared-libs/contents/src/retry-route.ts?ref=${f.tip}`).stdout).sha).toBe(file.sha); + expect(gh(f, 'repos/fixture/shared-libs/contents/src?ref=main').stderr).toContain('unsupported or unpinned'); + + for (const endpoint of [`repos/fixture/shared-libs/git/blobs/${'0'.repeat(40)}`, + `repos/fixture/shared-libs/git/trees/${f.tip}?recursive=1`, 'repos/fixture/shared-libs/pulls/999999', 'rate_limit']) { + const response = gh(f, endpoint); + expect(response.status, endpoint).not.toBe(0); + expect(response.stderr).toContain('HTTP 404'); + expect(response.stdout).toBe(''); + } + const missingPr = curl(f, ['-fsS', 'https://api.github.com/repos/fixture/shared-libs/pulls/999999']); + expect(missingPr.status).toBe(22); + expect(JSON.parse(gh(f, 'repos/fixture/shared-libs').stdout).default_branch).toBe('main'); + // The read-only detector still rejects a raw-host fallback. + const raw = curl(f, ['-sS', `https://raw.githubusercontent.com/fixture/shared-libs/${f.tip}/src/retry-route.ts`]); + expect(raw.status).toBe(2); + expect(sharedReadOnlyViolations([], readRequests(f))).toEqual(['curl: unsupported curl URL: fixture GitHub GET endpoints only']); + }); + + test('without PR coverage every listing view is the same successful empty world', () => { + const f = createSharedLibsFixture('pr-world-empty'); + cleanup.push(f.root); + installSourceShims(f); + expect(JSON.parse(ghCli(f, ['pr', 'list', '--state', 'all']).stdout)).toEqual([]); + expect(JSON.parse(gh(f, pulls('state=all&per_page=100&page=1')).stdout)).toEqual([]); + expect(JSON.parse(gh(f, 'search/issues?q=repo:fixture/shared-libs+is:pr').stdout)) + .toEqual({ total_count: 0, incomplete_results: false, items: [] }); + }); +}); + describe('shared-code Contents API revision fidelity', () => { test('default, branch and PR SHAs return their own exact committed bytes, excluding raw overlays', () => { const f = createSharedLibsFixture('source-fidelity');