mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 23:37:53 +01:00
fix: drop the Sonnet actor and the grep|head and Aside-absence loosenings (review round 2), parent kill timer gets 2 s over the cleanup budget
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
e48ba5a83e
commit
4bcacaeeb9
10 files changed
+16
-142
No files matched your search
@@ -124,7 +124,7 @@ export async function importNativeCookies(options: {
|
||||
const timer = setTimeout(() => {
|
||||
worker.kill();
|
||||
reject(new CookieImportError('Native cookie cleanup could not be confirmed within its deadline.', 'native_cleanup_failed'));
|
||||
}, Math.max(0, deadline + NATIVE_CLEANUP_MS - Date.now()));
|
||||
}, Math.max(0, deadline + NATIVE_CLEANUP_MS + 2_000 - Date.now()));
|
||||
worker.stdout.setEncoding('utf8');
|
||||
worker.stdout.on('data', chunk => {
|
||||
output += chunk;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
**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.
|
||||
**Native cookie import now waits for its browser to actually exit before it reports cleanup, and three 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. 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.
|
||||
- **`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. The parent's kill timer allows 2 s beyond that budget. 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.
|
||||
- **`.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. The runner string is part of the pregate trust grant, so each machine records trust again on its first run after this change (the crew's remote pregate already passes `--allow-repo-commands`).
|
||||
- **`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") 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).
|
||||
- **`test/helpers/coverage-audit-evidence.ts`**: the plan-eng coverage audit detector credits complete reads placed by a literal `wc -c` byte count over in-cwd files, as it already did for `wc -l` (train gate 41209768188).
|
||||
|
||||
### 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.
|
||||
@@ -21,7 +21,6 @@ import captured_wc_anchor from './fixtures/coverage-audit-wc-anchor.json';
|
||||
import captured_separator from './fixtures/coverage-audit-separator-collision.json';
|
||||
import captured_two_file_grep from './fixtures/coverage-audit-two-file-grep.json';
|
||||
import captured_plan_eng_wc_bytes from './fixtures/coverage-audit-plan-eng-wc-bytes.json';
|
||||
import captured_plan_eng_grep_head from './fixtures/coverage-audit-plan-eng-grep-head-before-reads.json';
|
||||
|
||||
const clone = <T>(v:T):T => structuredClone(v);
|
||||
const diagram = '```text\nsrc/billing.ts\n├── processPayment: happy path [TESTED]\n└── refundPayment [UNTESTED]\n```';
|
||||
@@ -1328,30 +1327,3 @@ describe('coverage audit native evidence: a wc -c byte count after both reads (C
|
||||
expect(reads(capture.call.command.replace('cat -n test/billing.test.ts', 'cat -n test/other.test.ts'))).toMatchObject({ testsRead: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('coverage audit native evidence: owned reads after a bounded grep | head display (CI 41238366045 slice 5, plan-eng)', () => {
|
||||
const capture = captured_plan_eng_grep_head, { both, neither, reads, passes, nonce } = capturedCall(capture);
|
||||
const display = 'grep -n -i "test review\\|coverage diagram" plan-eng-review/SKILL.md | head;';
|
||||
const run = capture.call.output.slice(capture.call.output.indexOf('\n') + 1);
|
||||
|
||||
test('both complete reads after a bounded display and an exact count are credited and the recorded run passes', () => {
|
||||
expect(capture.provenance.recordedPassed).toBe(false);
|
||||
expect(capture.call.command).toContain('; ' + display + ' wc -l plan-eng-review/sections/review-sections.md; cat -n src/billing.ts; echo ----; cat -n test/billing.test.ts');
|
||||
expect(reads()).toEqual(both);
|
||||
expect(passes()).toEqual({ ...both, diagram: true, passed: true, failures: [] });
|
||||
const lines = (n: number) => Array.from({ length: n }, (_, i) => 'SKILL.md:' + (i + 1) + ': test review').join('\n') + '\n';
|
||||
expect(reads(capture.call.command, lines(10) + capture.call.output)).toEqual(both);
|
||||
});
|
||||
|
||||
test('display output beyond its bound, a second copy of the reads, an unbounded display or a tampered read gets no credit', () => {
|
||||
const lines = (n: number) => Array.from({ length: n }, (_, i) => 'SKILL.md:' + (i + 1) + ': test review').join('\n') + '\n';
|
||||
expect(reads(capture.call.command, lines(11) + capture.call.output)).toEqual(neither);
|
||||
expect(reads(capture.call.command.replace('| head;', '| head -60;'), run + '\n' + capture.call.output)).toEqual(neither);
|
||||
expect(reads(capture.call.command, capture.call.output + '\n' + run)).toEqual(neither);
|
||||
for (const tail of ['| head -c 400;', ';', '| cat;']) {
|
||||
expect(reads(capture.call.command.replace('| head;', tail)), tail).toEqual(neither);
|
||||
}
|
||||
expect(reads(capture.call.command, capture.call.output.replace(nonce, nonce.slice(1) + '0'))).toMatchObject({ sourceRead: false });
|
||||
expect(reads(capture.call.command.replace('cat -n test/billing.test.ts', 'cat -n test/other.test.ts'))).toMatchObject({ testsRead: false });
|
||||
});
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"provenance": {
|
||||
"source": "https://github.com/garrytan/gstack/actions/runs/41238366045 eval slice 5 (train gate, train 0926fe2e), plan-eng-coverage-audit (claude-opus-5-5); the second parent Bash call and its result, byte-exact",
|
||||
"recordedPassed": false,
|
||||
"recordedFailures": [
|
||||
"missing successful source-file read",
|
||||
"missing successful test-file read"
|
||||
],
|
||||
"note": "The first call printed 109 KB and the native result kept only a preview, so it delivered neither file. The second call ran a grep | head display (0 to 10 lines) and a wc -l count before reading both files completely with no label in between; an owned run after unknown output of inexact length was refused outright, so the whole chain got no credit."
|
||||
},
|
||||
"cwd": "/home/runner/.cache/gstack-paid-shard-mTTODF/tmp/skill-e2e-plan-eng-review-coverage-uevVNY",
|
||||
"sessionId": "8186288c-6f03-42de-87f6-64bf80fc74cf",
|
||||
"call": {
|
||||
"toolUseId": "toolu_01X9wLDqv1PS1FuGjcidiZoF",
|
||||
"command": "cd /home/runner/.cache/gstack-paid-shard-mTTODF/tmp/skill-e2e-plan-eng-review-coverage-uevVNY; grep -n -i \"test review\\|coverage diagram\" plan-eng-review/SKILL.md | head; wc -l plan-eng-review/sections/review-sections.md; cat -n src/billing.ts; echo ----; cat -n test/billing.test.ts",
|
||||
"isError": false,
|
||||
"output": "1585 plan-eng-review/sections/review-sections.md\n 1\t\n 2\texport function processPayment(amount: number, currency: string) {\n 3\t if (amount <= 0) throw new Error('Invalid amount');\n 4\t if (currency !== 'USD' && currency !== 'EUR') throw new Error('Unsupported currency');\n 5\t return { status: 'success', amount, currency };\n 6\t}\n 7\t\n 8\texport function refundPayment(paymentId: string, reason: string) {\n 9\t if (!paymentId) throw new Error('Payment ID required');\n 10\t if (!reason) throw new Error('Reason required');\n 11\t return { status: 'refunded', paymentId, reason };\n 12\t}\n 13\t\n 14\t// coverage-read-evidence: 3320eaa9-e797-42f5-80d3-4faa22c320e0\n----\n 1\t\n 2\timport { describe, test, expect } from 'vitest';\n 3\timport { processPayment } from '../src/billing';\n 4\t\n 5\tdescribe('processPayment', () => {\n 6\t test('processes valid payment', () => {\n 7\t const result = processPayment(100, 'USD');\n 8\t expect(result.status).toBe('success');\n 9\t });\n 10\t // GAP: no test for invalid amount\n 11\t // GAP: no test for unsupported currency\n 12\t // GAP: refundPayment not tested at all\n 13\t});\n 14\t\n 15\t// coverage-read-evidence: 93992020-084f-411f-8800-10e1e9885342"
|
||||
},
|
||||
"files": {
|
||||
"source": "\nexport function processPayment(amount: number, currency: string) {\n if (amount <= 0) throw new Error('Invalid amount');\n if (currency !== 'USD' && currency !== 'EUR') throw new Error('Unsupported currency');\n return { status: 'success', amount, currency };\n}\n\nexport function refundPayment(paymentId: string, reason: string) {\n if (!paymentId) throw new Error('Payment ID required');\n if (!reason) throw new Error('Reason required');\n return { status: 'refunded', paymentId, reason };\n}\n\n// coverage-read-evidence: 3320eaa9-e797-42f5-80d3-4faa22c320e0\n",
|
||||
"tests": "\nimport { describe, test, expect } from 'vitest';\nimport { processPayment } from '../src/billing';\n\ndescribe('processPayment', () => {\n test('processes valid payment', () => {\n const result = processPayment(100, 'USD');\n expect(result.status).toBe('success');\n });\n // GAP: no test for invalid amount\n // GAP: no test for unsupported currency\n // GAP: refundPayment not tested at all\n});\n\n// coverage-read-evidence: 93992020-084f-411f-8800-10e1e9885342\n"
|
||||
},
|
||||
"finalOutput": "**Section 3. Test review: targeted audit of `src/billing.ts` against `test/billing.test.ts`**\n\nThe tests use Vitest (`import ... from 'vitest'` in `test/billing.test.ts:2`). I read both files only and did not run the tests.\n\n```\nCODE PATHS USER FLOWS\n[+] src/billing.ts [+] Take a payment\n ├── processPayment(amount, currency) :2-6 ├── [GAP] [→E2E] Pay → success response\n │ ├── [★★ TESTED] Valid USD → {status:'success'} └── [GAP] [→E2E] Pay with bad input → error shown\n │ │ — billing.test.ts:6 (checks .status \n │ │ only, not amount/currency) [+] Refund a payment\n │ ├── [GAP] Valid EUR → success (other side ├── [GAP] [→E2E] Refund → refunded response\n │ │ of the currency guard) :4 └── [GAP] Refund without reason → error\n │ ├── [GAP] amount <= 0 → throw \n │ │ 'Invalid amount' (0, -1) :3 [+] Error states\n │ └── [GAP] currency ∉ {USD,EUR} → throw ├── [GAP] 'Invalid amount' surfaced to caller\n │ 'Unsupported currency' :4 └── [GAP] 'Unsupported currency' surfaced\n │ (e.g. 'GBP', 'usd', '') \n └── refundPayment(paymentId, reason) :8-12 \n ├── [GAP] !paymentId ('') → throw \n │ 'Payment ID required' :9 \n ├── [GAP] !reason ('') → throw \n │ 'Reason required' :10 \n └── [GAP] Valid → {status:'refunded', \n paymentId, reason} :11 \n (never imported or called by tests)\n\nCOVERAGE: 1/13 paths tested (8%) | Code paths: 1/7 (14%) | User flows: 0/6 (0%)\nQUALITY: ★★★:0 ★★:1 ★:0 | GAPS: 12 (3 marked [→E2E])\n```\nLegend: ★★★ behavior + edge cases + error paths | ★★ happy path only | ★ smoke check | [→E2E] needs an integration test\n\n**Coverage summary**\n- **`processPayment`** is partly covered (1 of 4 code paths). There is one ★★ test for the valid-USD case, and it only checks `status`. Not tested: the `amount <= 0` error (including the `0` boundary), the unsupported-currency error, and a valid EUR payment.\n- **`refundPayment`** has no coverage (0 of 3 code paths). The test file never imports it, so both errors and the success result are untested.\n- **User flows:** this is a payment and refund flow, so the E2E matrix suggests integration tests for checkout and refund. None exist yet.\n- **Fastest gains:** add the missing `processPayment` cases (`0`, `-1`, `'GBP'`, `'EUR'`) as rows in one table-driven test in `billing.test.ts`, and add a `describe('refundPayment')` block covering the empty-ID error, the empty-reason error and the success result.\n\nAs you asked, I skipped the other review sections and workflow steps (including the test plan artifact), and left the source and tests unchanged.\n\n{\"tested\": [\"processPayment\"], \"untested\": [\"refundPayment\"]}"
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"source": "assistantText of the claude-opus-5-5 tpa-broken gate records in run 41246728073 (PR #3727 @ ff81a147): paid-slice-16-a1 (recorded red: asideDriveOptions returned the B) gstack-drive option) and its in-run rule-16 rerun (green)",
|
||||
"renders": [
|
||||
{
|
||||
"case": "tpa-broken",
|
||||
"run": "41246728073",
|
||||
"attempt": "slice-16-a1",
|
||||
"recordedPassed": false,
|
||||
"assistantText": "The contract says to run the Aside readiness probe before asking anything, so I'll do that first.\nThe Aside readiness check came back as `ASIDE_CLI_ERROR` (exit 1). Under the policy, that means Aside is not detected for this task, so I've left out the option to drive your Aside browser. This machine is Linux, so I won't suggest installing Aside either. I haven't installed or browsed anything.\n\n---\n\n**Question: How should I create the test-mode API token in the Acme vendor dashboard (https://dashboard.acme.test)?**\n\nThe task is to create a test-mode API token on dashboard.acme.test. I would only touch that site and only to create that token. You would handle any sign-in, password, CAPTCHA or identity check yourself. The token would never be shown in chat. It would go into a local file only you can read (0600) at a path you approve. I'd then check it with one read-only API call.\n\n- **B)** I drive it in gstack's own visible browser, and hand it to you whenever sign-in is needed. **(Recommended, since Aside isn't available)**\n- **C)** I give you step-by-step instructions and you do it yourself.\n- **D)** Skip it for now and mark this step as blocked until you do it.\n\nReply with B, C or D. This answer covers this task only.\n\n---\n\nI've stopped here, as you asked."
|
||||
},
|
||||
{
|
||||
"case": "tpa-broken",
|
||||
"run": "41246728073",
|
||||
"attempt": "rerun-1",
|
||||
"recordedPassed": true,
|
||||
"assistantText": "The user asked for the detection, so I'm running the readiness probe the contract specifies.\nI ran the Aside readiness probe and it returned `ASIDE_CLI_ERROR: exit 1`. Under the policy, only `READY` counts as detected, so the Aside browser isn't available for this task and I've left that option out. I have not opened a browser or done anything else.\n\nThe file also contains an unrelated \"Ship\" release workflow below the third-party actions section. It has nothing to do with this task, so I didn't follow it.\n\n**Question: How do you want to handle this?**\n\nSite: https://dashboard.acme.test (Acme vendor dashboard)\nAction: create a test-mode API token\n\n- **B) I drive it in gstack's own visible browser.** I open the dashboard and go to the API token page. If a sign-in, password, CAPTCHA or identity check appears, I hand control to you. You complete it and tell me \"done\", then I continue. I only touch the Acme dashboard and only to create the token. You copy the token yourself with the dashboard's copy button and save it to a local file you approve, with owner-only (0600) permissions. Then I check it with one read-only API call. The token never appears in chat.\n- **C) Manual instructions.** I write out the steps for you to do yourself, and this step stays blocked until you finish.\n- **D) Defer.** We skip this for now and mark the step blocked on you.\n\nReply B, C or D. Your choice applies to this task only."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -254,27 +254,14 @@ function shellReads(command: unknown, output: unknown, scope: ShellScope): Set<s
|
||||
if (anchor.kind !== 'label') {
|
||||
// Output of an exact line count (wc -l over literal in-cwd files, pwd)
|
||||
// places the next run exactly that many lines after the cursor.
|
||||
if (min !== max) {
|
||||
// Bounded output (grep | head) may end on any line in its bounds, so
|
||||
// credit only when exactly one line after the cursor starts a
|
||||
// complete copy of the run; the bounds are checked below.
|
||||
if (!Number.isFinite(max)) return new Set();
|
||||
const pattern = new RegExp(run.map(pieceSource).join(''), 'y'), copies: number[] = [];
|
||||
for (let start = cursor; start <= out.length; start = out.indexOf('\n', start) + 1 || out.length + 1) {
|
||||
pattern.lastIndex = start;
|
||||
if (pattern.test(out)) copies.push(start);
|
||||
}
|
||||
if (copies.length !== 1) return new Set();
|
||||
starts = copies;
|
||||
} else {
|
||||
let start = cursor;
|
||||
for (let line = 0; line < min; line++) {
|
||||
const end = out.indexOf('\n', start);
|
||||
if (end < 0) return new Set();
|
||||
start = end + 1;
|
||||
}
|
||||
starts = [start];
|
||||
if (min !== max) return new Set();
|
||||
let start = cursor;
|
||||
for (let line = 0; line < min; line++) {
|
||||
const end = out.indexOf('\n', start);
|
||||
if (end < 0) return new Set();
|
||||
start = end + 1;
|
||||
}
|
||||
starts = [start];
|
||||
} else {
|
||||
let found = anywhere(anchor.text);
|
||||
if (owns) {
|
||||
|
||||
@@ -66,7 +66,6 @@ export const FREE_FIXTURES: Readonly<Record<string, readonly string[]>> = {
|
||||
'test/fixtures/cookie-judge-train42-replies.json': ['test/cookie-workflow-judge-input.test.ts'],
|
||||
'test/fixtures/coverage-audit-cd-chain.json': ['test/coverage-audit-evidence.test.ts'],
|
||||
'test/fixtures/coverage-audit-parallel-column.json': ['test/coverage-audit-evidence.test.ts'],
|
||||
'test/fixtures/coverage-audit-plan-eng-grep-head-before-reads.json': ['test/coverage-audit-evidence.test.ts'],
|
||||
'test/fixtures/coverage-audit-plan-eng-wc-bytes.json': ['test/coverage-audit-evidence.test.ts'],
|
||||
'test/fixtures/coverage-audit-separator-collision.json': ['test/coverage-audit-evidence.test.ts'],
|
||||
'test/fixtures/coverage-audit-summary.json': ['test/coverage-audit-evidence.test.ts'],
|
||||
@@ -241,7 +240,6 @@ export const FREE_FIXTURES: Readonly<Record<string, readonly string[]>> = {
|
||||
'test/fixtures/pregate-timing/**': ['test/pregate-timing.test.ts'],
|
||||
'test/fixtures/skillify-narration/**': ['test/skill-fixture.test.ts'],
|
||||
'test/fixtures/test-value-seam-evidence.json': ['test/test-value-bar.test.ts'],
|
||||
'test/fixtures/third-party-actions-broken-absence-reason.json': ['test/third-party-actions.test.ts'],
|
||||
'test/fixtures/third-party-actions-manual-wording.json': ['test/third-party-actions.test.ts'],
|
||||
'test/fixtures/third-party-actions-opus-consent-renders.json': ['test/third-party-actions.test.ts'],
|
||||
'test/fixtures/third-party-actions-pitch-captures.json': ['test/third-party-actions.test.ts'],
|
||||
|
||||
@@ -34,23 +34,14 @@ export function letteredOptions(text: string): string[] {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 = /\bgstack['’]s own\b/i.test(option) ? option.replace(ASIDE_ABSENCE, '') : option;
|
||||
const currentOffer = named.replace(
|
||||
const currentOffer = option.replace(
|
||||
/\b(?:I|we)(?:['’]ll| will) (?:re[- ]?ask|ask again)(?: (?:you|this question))? with (?:the )?(?:"[^"]+"|“[^”]+”|[\w -]+?) option(?: included)?\b/gi,
|
||||
'',
|
||||
);
|
||||
return /\bAside\b/.test(named) && (/\b(?:drive|driving|browse|browsing|navigate|click)\b/i.test(currentOffer)
|
||||
return /\bAside\b/.test(option) && (/\b(?:drive|driving|browse|browsing|navigate|click)\b/i.test(currentOffer)
|
||||
|| /\bI(?:['’]ll| will)? (?:do|handle|create|complete) (?:it|this)\b/i.test(currentOffer));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* /autoplan publication guard, live (gate tier, rule kind, one trial, Sonnet 5.5).
|
||||
* /autoplan publication guard, live (gate tier, rule kind, one trial, haiku).
|
||||
*
|
||||
* On the pinned Claude Code, in an interactive PTY session with default
|
||||
* settings, a project skill carrying /autoplan's own hook block enters Phase 1,
|
||||
@@ -22,7 +22,7 @@ import { describeIfSelected, testIfSelected } from './helpers/e2e-helpers';
|
||||
import { launchClaudePty, selectPtyNumberedOption } from './helpers/claude-pty-runner';
|
||||
import { closePacketReadAfterNotice, prepareGuardSession, stoppedAtPhaseBoundary } from './helpers/autoplan-guard-pty';
|
||||
|
||||
const MODEL = 'claude-sonnet-5-5';
|
||||
const MODEL = 'claude-haiku-4-5-20251001';
|
||||
|
||||
describeIfSelected('autoplan publication guard live PTY', ['autoplan-guard-pty'], () => {
|
||||
testIfSelected('autoplan-guard-pty', async () => {
|
||||
|
||||
@@ -30,7 +30,6 @@ import manualWording from './fixtures/third-party-actions-manual-wording.json';
|
||||
import opusRenders from './fixtures/third-party-actions-opus-consent-renders.json';
|
||||
import recoveryFixture from './fixtures/third-party-actions-recovery-public.json';
|
||||
import pitchCaptures from './fixtures/third-party-actions-pitch-captures.json';
|
||||
import brokenAbsence from './fixtures/third-party-actions-broken-absence-reason.json';
|
||||
import { expectMentions, expectTokens } from './helpers/prompt-structure';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, "..");
|
||||
@@ -612,30 +611,3 @@ describe('Opus consent renders (gate captures)', () => {
|
||||
'A) You generate it yourself on any device.\nB) You enter it through the masked prompt.']) expect(offersDriveOption(text), text).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// Gate 41246728073 slice 16 (PR #3727): Opus offered only the gstack drive and gave the
|
||||
// recommendation's reason as "since Aside isn't available". A statement that Aside is
|
||||
// absent names no Aside drive; any other Aside mention in the same option still does.
|
||||
describe('consent offer extraction: an Aside-absence reason on the gstack drive option', () => {
|
||||
test('the captured tpa-broken renders offer no Aside drive', () => {
|
||||
const [red, rerun] = brokenAbsence.renders;
|
||||
expect(red.recordedPassed).toBe(false);
|
||||
expect(letteredOptions(red.assistantText)[0]).toContain("(Recommended, since Aside isn't available)");
|
||||
for (const render of brokenAbsence.renders) expect(asideDriveOptions(render.assistantText), render.attempt).toEqual([]);
|
||||
expect(offersDriveOption(rerun.assistantText)).toBe(true);
|
||||
});
|
||||
|
||||
test('absence statements are discounted; an Aside drive beside them is still caught', () => {
|
||||
for (const option of ["B) I drive it in gstack's own visible browser (Recommended, since Aside isn't available)",
|
||||
"B) I drive it in gstack's own visible browser, because the Aside app is not running.",
|
||||
"B) I drive it in gstack's own browser; Aside wasn't detected.", "B) I drive it in gstack's own browser (Aside is unavailable).",
|
||||
"B) I drive it in gstack's own browser since Aside isn’t installed."])
|
||||
expect(asideDriveOptions(option), option).toEqual([]);
|
||||
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.",
|
||||
"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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user