diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f91ed86b..9652e71fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [1.91.186.0] - 2026-10-24 + +**Approving an autoplan run no longer fails one time in ten because its run id looks like a card number.** Today's daily flaky-fix pass found `test/capy-captain-flow.test.ts` failing once and passing on the serial retry in 7 of the 57 Free Tests runs from 2026-10-21 to 2026-10-23, one of them a push to main (39878113535). Every failure had the same assertion: `gstack-approvals import` refused the approval row with "field ask: pii.cc". The default autoplan run id is `run-YYYYMMDDHHMMSS`, the import writes it into the row's ask and source, and the credit-card rule saw those 14 digits as a card number whenever they passed the Luhn check, which a timestamp does about one time in ten. A real user's approval import failed the same way. + +### Fixed +- **`lib/redact-patterns.ts`**: `pii.cc` skips a separator-free 14-digit span that reads as a valid YYYYMMDDHHMMSS stamp, using the `looksLikeCompactTimestamp` check that `pii.phone.e164` already uses. The only card numbers 14 digits long (Diners Club) start with 3, and an accepted stamp starts with 1 or 2 (years 1900–2999), so no real card loses coverage. + +### Tests +- `test/approval-record.test.ts` imports an autoplan run whose run id `run-20261024131504` passes Luhn. It failed before the fix with the captain-flow error, and passes now. +- `test/redact-context-exemptions.test.ts` has a new `pii.cc` table. The ask, the source and a log line carrying a Luhn-valid stamp come back clean. These still report MEDIUM: a 14-digit Diners test number, a Luhn-valid 14-digit run that is not a date (month 13), a 15-digit stamp-prefixed run, the same stamp with spaces, and a 16-digit Visa test number. + +### Gate integrity +- One accepted-input change to review: `pii.cc` no longer reports a separator-free 14-digit date-time stamp. The negative controls above are unchanged findings. No threshold, budget, timeout, trial count or case kind changed. + ## [1.91.185.0] - 2026-10-23 **Three flaky paid eval cases get repairs: three harness gaps closed and one fixture rule moved to where the agent reads it.** Today's daily flaky-fix pass took the two repeat flakes and one periodic timeout from the flaky ledger. plan-design-with-ui-scope failed its first attempt in train gate 39852003387 (its fourth flake) because the Opus 5.5 agent rated the plan "4/10 on design" in plain words and the focus check wanted the words "design completeness". ship-docsync-completion failed in train gate 39904966815 and in GSTA-63's focused run with the same slip both times: the agent added `gstack-docs-candidate --help` to the end of its first line of reads, which the fixture interface forbids. plan-design-finding-floor timed out at 610 s in periodic run 39209106033 because nothing answered the owned plan-file Create menu after its title scrolled off screen; two focused trials then showed a second stall, at the Step 0D focus menu the floor actor answers only when it matches one captured wording. diff --git a/VERSION b/VERSION index e63cb230a..ab330a4ad 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.91.185.0 +1.91.186.0 diff --git a/agents-digest/gstack-AGENTS.md b/agents-digest/gstack-AGENTS.md index 26600c1d9..177cc23f8 100644 --- a/agents-digest/gstack-AGENTS.md +++ b/agents-digest/gstack-AGENTS.md @@ -1,4 +1,4 @@ -# gstack digest v1.91.185.0 — regenerate/re-copy after upgrading gstack +# gstack digest v1.91.186.0 — regenerate/re-copy after upgrading gstack Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed for agent hosts without a full skill install. The full skills add workflows, diff --git a/lib/redact-patterns.ts b/lib/redact-patterns.ts index 7b45e17a4..6ece3ff4b 100644 --- a/lib/redact-patterns.ts +++ b/lib/redact-patterns.ts @@ -991,8 +991,11 @@ export const PATTERNS: RedactPattern[] = [ autoRedactable: true, redactToken: "", // A 13-19 digit slice of a digit-only UUID or of a decimal number passes - // Luhn often enough to matter; both context checks run before Luhn. - validate: (span, match) => !insideUuid(match) && !insideDecimalNumber(match) && luhnValid(span), + // Luhn often enough to matter, and so does a compact YYYYMMDDHHMMSS stamp + // (an autoplan run id); the only 14-digit cards start with 3, a stamp with + // 1 or 2. All three context checks run before Luhn. + validate: (span, match) => + !insideUuid(match) && !insideDecimalNumber(match) && !looksLikeCompactTimestamp(span.replace(/[ -]$/, "")) && luhnValid(span), }, { id: "pii.ip_public", diff --git a/package.json b/package.json index 9a137d6a1..e1959b316 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gstack", - "version": "1.91.185", + "version": "1.91.186", "description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.", "license": "MIT", "type": "module", diff --git a/test/approval-record.test.ts b/test/approval-record.test.ts index 3cbec30f7..a38424384 100644 --- a/test/approval-record.test.ts +++ b/test/approval-record.test.ts @@ -135,6 +135,16 @@ describe('import --autoplan', () => { expect(bin(['import', '--autoplan', run, '--by', 'Garry'], { cwd, env: { GSTACK_STATE_ROOT: state } }).out).toMatch(/^APPROVAL_IMPORTED: n=0 skipped=2 /); }); + test('a default run id whose YYYYMMDDHHMMSS stamp passes Luhn still imports (capy-captain-flow flake)', () => { + const run = tmp('appr-run-'); + autoplanRun(run, [{ by: 'agent:GSTA-1', words: 'rec', at: '2026-10-24T13:15:04.000Z', p1: 'a' }]); + fs.writeFileSync(path.join(run, 'run.json'), JSON.stringify({ run: 'run-20261024131504', status: 'complete' })); + const rows = importAutoplan(run, 'b', 'Garry'); + if ('code' in rows) throw new Error(rows.message); + expect(rows[0]!.ask).toContain('(autoplan run run-20261024131504, 2 decisions)'); + expect(rows[0]!.source).toBe('autoplan:run-20261024131504@gate_rev=1'); + }); + test('an unanswered gate imports nothing and says why', () => { const run = tmp('appr-run-'); fs.writeFileSync(path.join(run, 'decisions.jsonl'), JSON.stringify({ schema_version: 1, run: 'run-1', id: 'run-1-p1', label: 'P1', status: 'pending', gate_rev: 1 }) + '\n'); diff --git a/test/redact-context-exemptions.test.ts b/test/redact-context-exemptions.test.ts index 5215943d9..0fd131100 100644 --- a/test/redact-context-exemptions.test.ts +++ b/test/redact-context-exemptions.test.ts @@ -192,6 +192,27 @@ describe("db.url_with_password: postgres:postgres only on a loopback host (#2913 ); }); +// Free Tests flaked test/capy-captain-flow.test.ts in 7 of 57 runs (2026-10-21..23): the autoplan run id +// run-YYYYMMDDHHMMSS put a 14-digit stamp in the approval row, and one stamp in ten passes Luhn. +describe("pii.cc: a separator-free YYYYMMDDHHMMSS stamp is not a card number", () => { + table( + "pii.cc", + "MEDIUM", + [ + 'Approve the plan "Retry backoff" (autoplan run run-20261024131504, 2 decisions)', + "autoplan:run-20261024131504@gate_rev=1", + "backup stamp 20261024131512 ran late", + ], + [ + "card 30569309025904", + "ref 20261324131501", + "stamp 202610241315045", + "card 2026 1024 1315 04", + "card 4111111111111111", + ], + ); +}); + describe("pii.phone.e164: GitHub Actions run and job ids in their id positions", () => { table( "pii.phone.e164",