diff --git a/CHANGELOG.md b/CHANGELOG.md index bcf32cc99..59ffff2bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,11 @@ This release closes the follow-ups filed by the v1.91.15.0 prompt cleanup. The c - Parity caps lowered to measured sizes for land-and-deploy, design-consultation and ship. - The codex consult section drops issue numbers from its runtime prose. +#### Fixed +- /review's specialist header counted advisory findings in its total; the header's N is now defined as critical plus informational. +- Free-lane shards failed with "browser ownership deadline exceeded" when a browse daemon took more than 5 seconds to exit after SIGINT. Reaching the force-kill point now ends the graceful phase instead of recording a cleanup failure; a probe cut off at that point still fails the shard. +- `bun run audit:manifest` printed backslash paths on Windows. + #### For contributors - Five safety rules and the qa workflow, review and cookie judges keep their wording; TODOS.md lists what each needs before it can change. diff --git a/review/sections/review-army.md b/review/sections/review-army.md index 2cc0465ec..254a2700a 100644 --- a/review/sections/review-army.md +++ b/review/sections/review-army.md @@ -175,7 +175,8 @@ Core findings keep the core Confidence Calibration gates. #### 5. Score and present specialists Only specialist findings enter this header and `quality_score`; core findings do not. -Use the merged NON-advisory specialist findings for both counts and score: +Use the merged NON-advisory specialist findings for both counts and score; +the header's N is X + Y, so advisory findings never add to it: `quality_score = max(0, 10 - (critical_count * 2 + informational_count * 0.5))` Cap at 10 and retain for the review-log entry in Step 5.8. These are not final unresolved-defect totals. Print only this block: the stage 6 activity object and `test_stub` bodies are log and Fix-First data. diff --git a/scripts/resolvers/review-army.ts b/scripts/resolvers/review-army.ts index 3af34dcb8..b3ceda52b 100644 --- a/scripts/resolvers/review-army.ts +++ b/scripts/resolvers/review-army.ts @@ -197,7 +197,8 @@ Core findings keep the core Confidence Calibration gates. #### 5. Score and present specialists Only specialist findings enter this header and \`quality_score\`; core findings do not. -Use the merged NON-advisory specialist findings for both counts and score: +Use the merged NON-advisory specialist findings for both counts and score; +the header's N is X + Y, so advisory findings never add to it: \`quality_score = max(0, 10 - (critical_count * 2 + informational_count * 0.5))\` Cap at 10 and retain for ${persistRef}. These are not final unresolved-defect totals. Print only this block: the stage 6 activity object and \`test_stub\` bodies are log and Fix-First data. diff --git a/scripts/test-free-shards.ts b/scripts/test-free-shards.ts index aa28ff511..bef1f0f75 100755 --- a/scripts/test-free-shards.ts +++ b/scripts/test-free-shards.ts @@ -1432,7 +1432,7 @@ function trackShardBrowser(stateDir: string, env: NodeJS.ProcessEnv) { let alive = true; const check = (capture: Capture) => { - if (closed || capture.abort.signal.aborted || active !== capture) throw new CaptureStopped(); + if (closed || capture.abort.signal.aborted || active !== capture || (!forced && performance.now() >= forceAt)) throw new CaptureStopped(); if (performance.now() >= Math.min(capture.deadline, deadline)) throw new BrowserCleanupError('browser ownership deadline exceeded'); }; const probe = async (capture: Capture, command: string, args: string[], timeout: number) => { @@ -1462,7 +1462,7 @@ function trackShardBrowser(stateDir: string, env: NodeJS.ProcessEnv) { killProcessGroup(child, 'SIGKILL'); reaper = setTimeout(() => finish(null), 100); }; - const timer = setTimeout(stop, Math.max(1, remaining)); + const timer = setTimeout(() => { if (!closed) errors.add('browser identity probe did not complete'); stop(); }, Math.max(1, Math.min(remaining, forced ? Infinity : forceAt - performance.now()))); capture.abort.signal.addEventListener('abort', stop, { once: true }); child.once('error', () => { failed = true; finish(null); }); child.once('close', finish); @@ -1693,7 +1693,7 @@ function trackShardBrowser(stateDir: string, env: NodeJS.ProcessEnv) { }; const enqueue = () => { if (closed || pending || process.platform === 'win32') return; - const operation: Capture = { abort: new AbortController(), deadline: Math.min(performance.now() + 10000, deadline, forced ? Infinity : forceAt), probes: new Set() }; + const operation: Capture = { abort: new AbortController(), deadline: Math.min(performance.now() + 10000, deadline), probes: new Set() }; active = operation; pending = (async () => { try { diff --git a/ship/sections/review-army.md b/ship/sections/review-army.md index 5c0ca0c0b..ee852eda0 100644 --- a/ship/sections/review-army.md +++ b/ship/sections/review-army.md @@ -396,7 +396,8 @@ Core findings keep the core Confidence Calibration gates. #### 5. Score and present specialists Only specialist findings enter this header and `quality_score`; core findings do not. -Use the merged NON-advisory specialist findings for both counts and score: +Use the merged NON-advisory specialist findings for both counts and score; +the header's N is X + Y, so advisory findings never add to it: `quality_score = max(0, 10 - (critical_count * 2 + informational_count * 0.5))` Cap at 10 and retain for the review-log persist. These are not final unresolved-defect totals. Print only this block: the stage 6 activity object and `test_stub` bodies are log and Fix-First data. diff --git a/test/fixtures/golden/factory-ship-SKILL.md b/test/fixtures/golden/factory-ship-SKILL.md index fbfdc30b5..54341009f 100644 --- a/test/fixtures/golden/factory-ship-SKILL.md +++ b/test/fixtures/golden/factory-ship-SKILL.md @@ -2219,7 +2219,8 @@ Core findings keep the core Confidence Calibration gates. #### 5. Score and present specialists Only specialist findings enter this header and `quality_score`; core findings do not. -Use the merged NON-advisory specialist findings for both counts and score: +Use the merged NON-advisory specialist findings for both counts and score; +the header's N is X + Y, so advisory findings never add to it: `quality_score = max(0, 10 - (critical_count * 2 + informational_count * 0.5))` Cap at 10 and retain for the review-log persist. These are not final unresolved-defect totals. Print only this block: the stage 6 activity object and `test_stub` bodies are log and Fix-First data. diff --git a/test/test-free-shards.test.ts b/test/test-free-shards.test.ts index 3ac204405..a56a13153 100644 --- a/test/test-free-shards.test.ts +++ b/test/test-free-shards.test.ts @@ -87,6 +87,7 @@ if (role === 'leaf') { const current = JSON.parse(fs.readFileSync(path.join(path.dirname(process.env.BROWSE_STATE_FILE), 'terminal-agent-pid'), 'utf8')); try { process.kill(current.pid, 'SIGTERM'); } catch {} for (const child of children) try { child.kill('SIGTERM'); } catch {} + if (mode === 'settle-slow-exit') return; const finish = () => { fs.rmSync(process.env.BROWSE_STATE_FILE, {force:true}); fs.rmSync(path.join(path.dirname(process.env.BROWSE_STATE_FILE), 'terminal-agent-pid'), {force:true}); @@ -126,10 +127,11 @@ if (role === 'leaf') { } else if (role === 'harness') { if (mode === 'settle-cold-probes') Object.defineProperty(process, 'platform', {value:'darwin'}); let spy; - if (['replaced-start', 'exit-environment-race', 'unavailable-environment'].includes(mode)) { + if (['replaced-start', 'exit-environment-race', 'unavailable-environment', 'settle-slow-exit'].includes(mode)) { const original = fs.readFileSync; spy = spyOn(fs, 'readFileSync').mockImplementation((filename, ...args) => { const value = original(filename, ...args); + if (mode === 'settle-slow-exit' && String(filename).endsWith('/environ') && fs.existsSync(file('.interrupted'))) Bun.sleepSync(300); if (String(filename).endsWith('/environ') && fs.existsSync(file('.ready')) && (mode === 'unavailable-environment' || mode === 'exit-environment-race' && fs.existsSync(file('.interrupted')))) { const own = JSON.parse(original(file('.ready'), 'utf8')); @@ -186,8 +188,8 @@ function ownershipProcessAlive(identity: { pid: number; start: string; ticks: st describe('test-free-shards: owned detached browser settlement', () => { for (const mode of ['success', 'failure', 'timeout', 'cancel', 'cancel-force', 'endpoint-mix', 'full-state-mix', 'terminal-mix', 'chromium-mix', 'replaced-pid', 'stale-child-start', 'replaced-start', 'exit-environment-race', - 'unavailable-environment', 'directory-remove-failure', 'cancel-cold-probes', 'settle-cold-probes']) { - test.skipIf(process.platform === 'win32' || ((['replaced-start', 'exit-environment-race', 'unavailable-environment'].includes(mode) || mode.endsWith('cold-probes')) && process.platform !== 'linux'))(mode, async () => { + 'unavailable-environment', 'directory-remove-failure', 'cancel-cold-probes', 'settle-cold-probes', 'settle-slow-exit']) { + test.skipIf(process.platform === 'win32' || ((['replaced-start', 'exit-environment-race', 'unavailable-environment', 'settle-slow-exit'].includes(mode) || mode.endsWith('cold-probes')) && process.platform !== 'linux'))(mode, async () => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'free-owned-browser-')); const actor = path.join(directory, 'actor.ts'); fs.writeFileSync(actor, OWNERSHIP_ACTOR);