fix: real-Edge test bounds follow NATIVE_CLEANUP_MS; Aside absence discounted only beside gstack's own browser (review pass 1)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-11-01 16:30:23 +00:00
1 parent e7951ea9bd
commit e48ba5a83e
4 files changed
+18 -14

No files matched your search

+11 -9
View File
@@ -334,6 +334,8 @@ afterAll(() => {
}
}, 15_000);
const NATIVE_SUPERVISOR_MS = 25_000 + NATIVE_CLEANUP_MS;
const request: NativeCookieRequest = {
nodeExecutable: 'C:\\fixture\\node.exe',
nodeArchitecture: process.arch,
@@ -1081,11 +1083,11 @@ function safeLaunchEvidence(file: string): object {
}
function nativeSupervisor(input: NativeCookieRequest, env: NodeJS.ProcessEnv) {
const cleanupDeadline = performance.now() + 25_000 + NATIVE_CLEANUP_MS;
const cleanupDeadline = performance.now() + NATIVE_SUPERVISOR_MS;
const child = ownFixtureChild(spawn(process.execPath, ['--no-env-file', '--no-install', '--no-macros', '--config=NUL', path.resolve(import.meta.dir, '../src/cookie-import-native-worker.ts')], { env, stdio: ['pipe', 'pipe', 'ignore'], windowsHide: true }));
let output = '';
child.stdout.on('data', chunk => { output += chunk; });
const timer = setTimeout(() => child.kill(), 25_000 + NATIVE_CLEANUP_MS);
const timer = setTimeout(() => child.kill(), NATIVE_SUPERVISOR_MS);
const done = new Promise<NativeCookieReply>((resolve, reject) => {
child.once('error', reject);
child.once('close', () => {
@@ -1183,7 +1185,7 @@ describe('native Windows process qualification', () => {
}
for (const parent of createdParents.reverse()) rmdirSync(parent);
}
}, 65_000);
}, 2 * NATIVE_SUPERVISOR_MS + 5_000);
test.skipIf(process.platform !== 'win32')('a locked real Edge profile leaves its existing owner alive', async () => {
const node = Bun.which('node');
@@ -1218,7 +1220,7 @@ describe('native Windows process qualification', () => {
await contender?.done.catch(() => {});
await owner.done.catch(() => {});
}
}, 65_000);
}, 2 * NATIVE_SUPERVISOR_MS + 5_000);
for (const mode of ['normal-close', 'stalled-close']) {
test.skipIf(process.platform !== 'win32')(`real Edge synthetic profile: ${mode} returns only after the owned browser exits`, async () => {
@@ -1236,7 +1238,7 @@ describe('native Windows process qualification', () => {
let output = '';
supervisor.stdout.on('data', chunk => { output += chunk; });
const started = Date.now();
const deadline = setTimeout(() => supervisor.kill(), 30_000);
const deadline = setTimeout(() => supervisor.kill(), NATIVE_SUPERVISOR_MS);
supervisor.stdin.write(JSON.stringify({ ...request, nodeExecutable: node, playwrightEntry, executablePath: edge, userDataDir: path.join(fixture, 'User Data'), deadline: started + 25_000, qualifiedBunVersions: [Bun.version] }) + '\n');
try {
await closed;
@@ -1250,13 +1252,13 @@ describe('native Windows process qualification', () => {
expect(browser.args.some((arg: string) => arg.startsWith('--remote-debugging-port'))).toBe(false);
expect(browser.args.some((arg: string) => /^--(?:no-sandbox|disable-setuid-sandbox)(?:=|$)/.test(arg))).toBe(false);
expect(alive(browser.pid)).toBe(false);
expect(Date.now() - started).toBeLessThan(30_000);
expect(Date.now() - started).toBeLessThan(NATIVE_SUPERVISOR_MS);
} finally {
clearTimeout(deadline);
supervisor.kill();
await closed;
}
}, 35_000);
}, NATIVE_SUPERVISOR_MS + 5_000);
}
for (const mode of ['timeout', 'owner-exit', 'worker-crash']) {
@@ -1637,7 +1639,7 @@ describe('native Windows launch diagnostics', () => {
expect(directObservation.observedCommandLine).toMatchObject({ available: true });
expect(containedObservation.folderEvidence.directoriesAfterProbe).toEqual(containedObservation.folderEvidence.directoriesBefore);
expect(directObservation.folderEvidence.directoriesAfterProbe).toEqual(directObservation.folderEvidence.directoriesBefore);
}, 65_000);
}, 2 * NATIVE_SUPERVISOR_MS + 5_000);
}
for (const state of ['preserved', 'fresh']) {
@@ -1692,6 +1694,6 @@ describe('native Windows launch diagnostics', () => {
expect(secondObservation.observedCommandLine).toMatchObject({ available: true });
expect('error' in second ? second.error : null).not.toBe('native_cleanup_failed');
if (state === 'fresh') expect(firstObservation.folderEvidence.directoriesBefore).toEqual(secondObservation.folderEvidence.directoriesBefore);
}, 65_000);
}, 2 * NATIVE_SUPERVISOR_MS + 5_000);
}
});
+2 -2
View File
@@ -1,13 +1,13 @@
**Native cookie import now waits for its browser to actually exit before it reports cleanup, and four more flaky tests from the 2026-11-01 flaky pass are repaired.** Windows drops a Job's process count to zero while terminated Edge processes are still shutting down and still hold the profile's files, so cleanup that trusted the count returned early and the next profile reset failed with a sharing violation.
### Fixed
- **`browse/src/cookie-import-native-job.ts`, `browse/src/cookie-import-native-worker.ts`, `browse/src/cookie-import-native.ts`**: the supervisor holds a handle to every process it sees in the import's Job and counts a process as active until that handle signals exit, not only until the Job's counter drops. The cleanup budget is now 20 s (was 5 s), because terminated Edge took up to 17.8 s to exit on a loaded Windows runner and 2 to 57 ms idle; an idle import returns as soon as its processes are gone. Cleanup that still cannot confirm the exit reports `native_cleanup_failed`, as before.
- **`browse/src/cookie-import-native-job.ts`, `browse/src/cookie-import-native-worker.ts`, `browse/src/cookie-import-native.ts`**: the supervisor holds a handle to every process it sees in the import's Job and counts a process as active until that handle signals exit, not only until the Job's counter drops. The cleanup budget is now 20 s (was 5 s), because terminated Edge took up to 17.8 s to exit on a loaded Windows runner and 2 to 57 ms idle; an idle import returns as soon as its processes are gone. Cleanup that still cannot confirm the exit reports `native_cleanup_failed`, as before. The real-Edge tests take their kill timers and time limits from the same constant.
- **`test/skill-e2e-autoplan-guard-pty.test.ts`**: the live actor is Sonnet 5.5 instead of Haiku 4.5. Haiku rewrote the 64-character content hash when it retyped the Phase 2 dispatch prompt, so the publication guard correctly refused a prompt the actor had changed. The guard, the case kind and its single trial are unchanged.
- **`.gstack/pregate.json`**: the local pregate lane runs `bun test --timeout=30000 {files}`, the 30 s per-test timeout CI and the remote pregate already use, so a case that takes 5 s on a busy machine no longer fails only locally.
- **`test/gstack-ship-receipt.test.ts`**: the full `write` case, about 62 git calls, gets a 15 s timeout instead of bun's 5 s default.
- **`test/helpers/coverage-audit-evidence.ts`**: the plan-eng coverage audit detector credits complete reads proved by a literal `wc -c` byte count, and by a bounded display (`grep | head`) when exactly one copy of the reads follows; an unbounded display, a second copy or a tampered read still gets no credit (train gates 41209768188, 41238366045).
- **`test/helpers/third-party-actions.ts`**: a statement that Aside is not available ("Recommended, since Aside isn't available") no longer counts as offering an Aside drive in the tpa-broken check; any other Aside mention in the option still does (gate 41246728073).
- **`test/helpers/third-party-actions.ts`**: a statement that Aside is not available ("Recommended, since Aside isn't available") on an option that names gstack's own browser no longer counts as offering an Aside drive in the tpa-broken check; any other Aside mention still does, and so does an absence clause followed by an offer to launch and drive Aside (gate 41246728073).
### Changed
- `test/helpers/docsync-contract.ts` names the missing `documentation_section` when a document-release completion fails the contract, so the next paid red says which field was absent.
+3 -2
View File
@@ -37,14 +37,15 @@ export function letteredOptions(text: string): string[] {
/**
* A statement that Aside is absent, the probe's result, names no Aside drive: "(Recommended, since Aside isn't
* available)" on the gstack drive option (gate 41246728073). Only Aside as the subject of a negated availability
* predicate is discounted, so any other Aside mention in the option still counts.
* predicate on an option that names gstack's own browser is discounted, so any other Aside mention still counts and
* an absence clause before an offer to launch and drive Aside is still an Aside drive.
*/
const ASIDE_ABSENCE = /\bAside(?: browser| app)?\s+(?:(?:is|was)(?:n['’]t|\s+not)\s+(?:available|installed|detected|running|ready|reachable)|is\s+unavailable)\b/gi;
/** Inspect lettered consent options, not narration that quotes an unavailable option. */
export function asideDriveOptions(text: string): string[] {
return letteredOptions(text).filter(option => {
const named = option.replace(ASIDE_ABSENCE, '');
const named = /\bgstack['’]s own\b/i.test(option) ? option.replace(ASIDE_ABSENCE, '') : option;
const currentOffer = named.replace(
/\b(?:I|we)(?:['’]ll| will) (?:re[- ]?ask|ask again)(?: (?:you|this question))? with (?:the )?(?:"[^"]+"|“[^”]+”|[\w -]+?) option(?: included)?\b/gi,
'',
+2 -1
View File
@@ -634,7 +634,8 @@ describe('consent offer extraction: an Aside-absence reason on the gstack drive
for (const option of ["A) I drive it in your Aside browser, even though Aside isn't running yet.",
"B) I drive it in gstack's own browser, or in Aside once Aside is not busy.", "B) Since Aside isn't available, I drive it in Aside's fallback window.",
"A) Aside isn't available yet, so open it; then I drive it in your Aside browser.", "A) I drive it in Aside; gstack isn't available.",
"B) I drive it in gstack's own browser and switch to Aside if it's running."])
"B) I drive it in gstack's own browser and switch to Aside if it's running.",
"A) Aside isn't running, so I'll launch it and drive it there.", "A) Aside app is not running yet; I'll start it and drive the dashboard for you."])
expect(asideDriveOptions(option), option).toHaveLength(1);
});
});