Merge remote-tracking branch 'origin/main' into garrytan/pr-title-version-fix

# Conflicts:
#	CHANGELOG.md
#	VERSION
#	package.json
This commit is contained in:
Garry Tan
2026-06-07 21:40:39 -07:00
70 changed files with 2023 additions and 199 deletions
+7
View File
@@ -300,6 +300,13 @@ export async function runAgentSdkTest(
const queryImpl: QueryProvider = opts.queryProvider ?? query;
const model = opts.model ?? 'claude-opus-4-7';
// NOTE on GSTACK_HEADLESS: the SDK child inherits process.env, so headless
// classification for eval/E2E runs is set by the `test:gate` / `test:evals`
// package.json scripts (scoped to that invocation), NOT mutated here. We must not
// pass sdkOpts.env (it breaks the SDK auth pipeline — see CLAUDE.md) and must not
// mutate process.env ambiently (it would leak headless into later interactive-path
// tests in the same Bun process — Codex review finding).
let attempt = 0;
let lastErr: unknown = null;
+11
View File
@@ -87,6 +87,12 @@ export interface CarveGuard {
minUnionBytes: number;
/** Parity: content phrases the union must preserve. */
mustContain: string[];
/**
* Parity: optional per-skill override for the union size-growth ceiling vs the
* v1.53.0.0 baseline (default 1.05). Bumped only when a deliberate cross-cutting
* preamble feature legitimately grows a smaller carved skeleton past 5%.
*/
maxSizeRatio?: number;
}
export const CARVE_GUARDS: Record<string, CarveGuard> = {
@@ -222,6 +228,11 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
maxSkeletonBytes: 50_000,
minUnionBytes: 55_000,
mustContain: ['CHANGELOG', 'Diataxis', 'coverage'],
// The AUQ-failure prose fallback (v1.57.2.0) adds ~2KB to every skill's
// always-loaded preamble; on this small carved skeleton that lands at ~5.9%
// over the pre-carve/pre-AUQ v1.53.0.0 baseline. Headroom for the
// cross-cutting addition; all other skills keep the strict 1.05 ceiling.
maxSizeRatio: 1.08,
},
'design-consultation': {
skill: 'design-consultation',
+1 -1
View File
@@ -252,7 +252,7 @@ const CARVED_INVARIANTS: ParityInvariant[] = Object.values(CARVE_GUARDS).map((g)
minBytes: g.minUnionBytes,
mustContain: g.mustContain,
mustHaveHeadings: ['## Preamble', '## When to invoke'],
maxSizeRatio: 1.05,
maxSizeRatio: g.maxSizeRatio ?? 1.05,
}));
export const PARITY_INVARIANTS: ParityInvariant[] = [
+3
View File
@@ -52,6 +52,9 @@ export class ClaudeAdapter implements ProviderAdapter {
timeout: opts.timeoutMs,
encoding: 'utf-8',
maxBuffer: 32 * 1024 * 1024,
// Default GSTACK_HEADLESS=1 so a benchmark run classifies as headless (an
// AskUserQuestion failure BLOCKs rather than emitting unanswerable prose).
env: { ...process.env, GSTACK_HEADLESS: '1' },
});
const parsed = this.parseOutput(out);
return {
+5 -1
View File
@@ -176,7 +176,11 @@ export async function runSkillTest(options: {
const proc = Bun.spawn(['sh', '-c', `cat "${promptFile}" | claude ${args.map(a => `"${a}"`).join(' ')}`], {
cwd: workingDirectory,
env: extraEnv ? { ...process.env, ...extraEnv } : undefined,
// Default GSTACK_HEADLESS=1 so eval/E2E runs classify as headless (BLOCK on an
// AskUserQuestion failure rather than emit a prose question no human reads). A
// suite exercising the INTERACTIVE prose-fallback path opts out by passing
// `env: { GSTACK_HEADLESS: '' }` — extraEnv wins because it spreads last.
env: { ...process.env, GSTACK_HEADLESS: '1', ...extraEnv },
stdout: 'pipe',
stderr: 'pipe',
});