From 23e3636e4cdadc67c61549ed99a44732a73ad2e3 Mon Sep 17 00:00:00 2001 From: garrytan Date: Wed, 30 Sep 2026 22:06:06 +0000 Subject: [PATCH] test(shared-libs): the plan actor reads scheduler parity and unchanged-scope lists Census 36776104571's question preserved the contract ('behaving exactly like the scheduler', 'scheduler parity holds by construction') and excluded work with 'Existing copies and helper hardening stay unchanged'. Accept exactly/parity as preservation (negated forms refuse) and a bare noun list that stays unchanged as an exclusion for the expansion scan only; verb-led clauses still refuse. --- ...-libs-plan-callers-parity-36776104571.json | 1 + test/helpers/shared-libs-plan-actor.ts | 10 ++++-- test/shared-libs-plan-actor.test.ts | 36 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/shared-libs-plan-callers-parity-36776104571.json diff --git a/test/fixtures/shared-libs-plan-callers-parity-36776104571.json b/test/fixtures/shared-libs-plan-callers-parity-36776104571.json new file mode 100644 index 000000000..6efde334e --- /dev/null +++ b/test/fixtures/shared-libs-plan-callers-parity-36776104571.json @@ -0,0 +1 @@ +{"questions":[{"question":"D1 — Parser source for the two new Retry-After callers\nProject/branch/task: repo on main; plan review of PLAN.md for the future src/import-worker.ts and src/sync-route.ts.\nELI10: Both new callers need to turn a Retry-After header into a wait time, capped at an hour, with a fallback when the header is missing or junk, behaving exactly like the scheduler. The scheduler already gets that from a tested shared function in lib/retry-after.ts. The plan instead writes a fresh copy of that parser inside each new file. Copies drift: probing the real parser shows quirks a fresh copy would probably handle differently (for example \"-5\" and \"1.5\" are treated as dates and cap at 3600 instead of falling back).\nStakes if we pick wrong: two new parsers that silently disagree with the scheduler on edge cases, with no test that would notice.\nRecommendation: A because it reuses proven code with zero semantic risk, and the extra contract assertions cost a few lines while closing the gaps the callers actually depend on.\nCompleteness: A=10/10, B=7/10, C=4/10\nPros / cons:\nA) Reuse lib, extend contract test (recommended) (human: ~1.5 h / CC: ~5 min)\n ✅ Callers import retrySeconds(value, now, fallback) exactly as src/scheduler.ts does, so scheduler parity holds by construction rather than by test.\n ✅ Adds about seven assertions to test/retry-after.test.ts for empty/whitespace, '0', the 3600/3601 ceiling, past date → 0, unsafe integer, and sub-second ceil; each catches a real regression.\n ✅ Saves about 24 implementation lines versus two local copies (about 4 added instead of 28).\n ❌ A defect in lib/retry-after.ts now reaches three callers instead of one; the contract test is the mitigation.\nB) Reuse lib, existing test only (human: ~1 h / CC: ~3 min)\n ✅ Same import wiring and line savings as A with no change to the shared test file.\n ✅ Smallest possible diff: two imports plus the two integration tests already planned.\n ❌ The five existing assertions leave empty string, past dates, the exact ceiling boundary and unsafe integers unasserted, so a later helper edit could break the callers unnoticed.\nC) Local parser per caller (human: ~3 h / CC: ~10 min)\n ✅ Each caller is self-contained; a change to the shared helper cannot affect it.\n ✅ Matches the current draft, so the plan text needs no rewrite.\n ❌ Adds two more ~14-line copies (src/retry-route.ts and src/retry-worker.ts are already copies) and needs parity tests to hold the fixed contract, which the probe shows is easy to get subtly wrong.\nNet: Reuse trades a slightly larger shared blast radius for guaranteed scheduler parity and a smaller diff; the only real question is whether to close the contract-test gaps now (A) or leave them (B).","header":"Parser reuse","multiSelect":false,"options":[{"label":"Reuse lib, extend contract test (recommended)","description":"✅ Callers import retrySeconds(value, now, fallback) exactly as src/scheduler.ts does, so scheduler parity holds by construction rather than by test.\n✅ Adds about seven assertions to test/retry-after.test.ts for empty/whitespace, '0', the 3600/3601 ceiling, past date → 0, unsafe integer, and sub-second ceil; each catches a real regression.\n✅ Saves about 24 implementation lines versus two local copies (about 4 added instead of 28).\n❌ A defect in lib/retry-after.ts now reaches three callers instead of one; the contract test is the mitigation.\nCompleteness 10/10. Effort human: ~1.5 h / CC: ~5 min. Integration tests per caller prove import wiring, injected now, fallback pass-through and the 3600 ceiling. Existing copies and helper hardening stay unchanged."},{"label":"Reuse lib, existing test only","description":"✅ Same import wiring and line savings as A with no change to the shared test file.\n✅ Smallest possible diff: two imports plus the two integration tests already planned.\n❌ The five existing assertions leave empty string, past dates, the exact ceiling boundary and unsafe integers unasserted, so a later helper edit could break the callers unnoticed.\nCompleteness 7/10. Effort human: ~1 h / CC: ~3 min. Same integration-test obligations as A. Existing copies and helper hardening stay unchanged."},{"label":"Local parser per caller","description":"✅ Each caller is self-contained; a change to the shared helper cannot affect it.\n✅ Matches the current draft, so the plan text needs no rewrite.\n❌ Adds two more ~14-line copies (src/retry-route.ts and src/retry-worker.ts are already copies) and needs parity tests to hold the fixed contract, which the probe shows is easy to get subtly wrong.\nCompleteness 4/10. Effort human: ~3 h / CC: ~10 min. Each caller needs a parity test against lib/retry-after.ts over the same edge-case list plus its integration test. Existing copies and helper hardening stay unchanged."}]}]} \ No newline at end of file diff --git a/test/helpers/shared-libs-plan-actor.ts b/test/helpers/shared-libs-plan-actor.ts index 1254c8fb5..21d6d736c 100644 --- a/test/helpers/shared-libs-plan-actor.ts +++ b/test/helpers/shared-libs-plan-actor.ts @@ -81,13 +81,17 @@ export function createSharedPlanReuseSelector(): SharedQuestionSelector { // The supplied PLAN owns the two future caller identities and fixed scope. // Native questions may refer to them without repeating file names, and an // option may inherit unchanged semantics from its complete decision brief. - if (/\b(?:not|never|no longer)\s+(?:identical|the same|unchanged|preserv\w*|match\w*)\b/i.test(commitment) || - !/\b(?:identical|same|unchanged|preserv\w*|match\w*|keep\w*)\b[^.!?\n]{0,120}\b(?:scheduler|semantics|behavior|contract)\b|\b(?:scheduler|semantics|behavior|contract)\b[^.!?\n]{0,120}\b(?:identical|same|unchanged|preserv\w*|match\w*|keep\w*)\b/i.test(affirmativeCommitments(context + '\n' + commitment))) { + if (/\b(?:not|never|no longer)\s+(?:identical|the same|unchanged|preserv\w*|match\w*|exact\w*)\b|\b(?:no|not|without|break\w*|los(?:e|es|ing))\s+(?:\w+\s+){0,2}parity\b/i.test(commitment) || + !/\b(?:identical|same|unchanged|preserv\w*|match\w*|keep\w*|exactly|parity)\b[^.!?\n]{0,120}\b(?:scheduler|semantics|behavior|contract)\b|\b(?:scheduler|semantics|behavior|contract)\b[^.!?\n]{0,120}\b(?:identical|same|unchanged|preserv\w*|match\w*|keep\w*|exactly|parity)\b/i.test(affirmativeCommitments(context + '\n' + commitment))) { refuse('the selected option must explicitly preserve the current scheduler contract'); } // Inspect the question as well as the selected option: a harmless label must // not authorize an extra commitment hidden in its brief or description. - const proposed = affirmativeCommitments(context + '\n' + commitment); + // "Existing copies and helper hardening stay unchanged" names excluded work. + // Only a bare list of those nouns qualifies; a verb such as "Harden" does not. + const item = String.raw`(?:(?:the|existing|current|its|all|both|helper|parser|lib|shared|caller|scheduler)\s+)*(?:copies|callers|hardening|migrations?|semantics|behaviou?r|contract|helper|parser)`; + const unchangedScope = new RegExp(String.raw`^${item}(?:\s*,\s*${item})*(?:,?\s+and\s+${item})?\s+(?:stays?|remains?)\s+(?:unchanged|untouched)[.!]?$`, 'i'); + const proposed = affirmativeCommitments(context + '\n' + commitment).split('\n').filter(clause => !unchangedScope.test(clause.trim())).join('\n'); const expansions = [ /\b(?:harden\w*|tighten\w*|strict(?:er)?|saniti[sz]\w*|coerc\w*)\b/i, /\b(?:add(?:s|ing)?|insert(?:s|ing)?|introduc(?:e|es|ing)|implement(?:s|ing)?|appl(?:y|ies|ying)|enabl(?:e|es|ing)|creat(?:e|es|ing))\s+(?:(?:a|an|the|one|new|shared|extra|explicit|validation|numeric|malformed|input|parser)\s+)*(?:guard|validator|validation|normalization)\b/i, diff --git a/test/shared-libs-plan-actor.test.ts b/test/shared-libs-plan-actor.test.ts index f93104f11..61a7a3af5 100644 --- a/test/shared-libs-plan-actor.test.ts +++ b/test/shared-libs-plan-actor.test.ts @@ -3,6 +3,7 @@ import { describe, expect, test } from 'bun:test'; import { createSharedPlanReuseSelector } from './helpers/shared-libs-plan-actor'; import { createSharedInteractiveToolHandler } from './helpers/shared-libs-eval-fixture'; import capturedNoHardening from './fixtures/shared-libs-plan-callers-no-hardening-36633323521.json'; +import capturedParity from './fixtures/shared-libs-plan-callers-parity-36776104571.json'; // Exact native R1 from the September 22 timeout. R2 was saved in an Edit, but // never sent as a native AUQ; its public draft fields are reconstructed below. @@ -348,4 +349,39 @@ describe('bounded shared-code planning actor', () => { expect(run.answers).toEqual([]); expect(run.controller.signal.aborted).toBe(true); }); + + test('actual census parity wording preserves the contract; an unchanged-scope list is an exclusion', async () => { + // Exact native question from census 36776104571: "behaving exactly like the scheduler", + // "scheduler parity holds by construction", "Existing copies and helper hardening stay unchanged". + const run = actor(); + const result = await run.callback('AskUserQuestion', capturedParity); + expect(result.updatedInput.answers).toEqual({ [capturedParity.questions[0].question]: capturedParity.questions[0].options[0].label }); + expect(run.refusals).toEqual([]); + }); + + test.each([ + ['exactly like the scheduler', 'not exactly like the scheduler', 'scheduler parity holds', 'breaks scheduler parity'], + ['exactly as src/scheduler.ts does', 'differently from src/scheduler.ts', 'scheduler parity holds', 'without scheduler parity'], + ] as const)('negated parity wording cannot supply preservation: %s', async (a, b, c, d) => { + const input = structuredClone(capturedParity); + const q = input.questions[0]; + q.question = q.question.replaceAll('behaving exactly like the scheduler', 'behaving ' + b.replace('exactly as src/scheduler.ts does', '')).replaceAll(a, b).replaceAll(c, d).replaceAll('guaranteed scheduler parity', 'lost scheduler parity'); + for (const option of q.options) option.description = option.description.replaceAll(a, b).replaceAll(c, d); + const run = actor(); + await expect(run.callback('AskUserQuestion', input)).rejects.toThrow('shared-libs-plan-callers actor:'); + expect(run.answers).toEqual([]); + }); + + test.each([ + 'Harden helper parsing so behavior stays unchanged.', + 'Existing copies stay unchanged and tighten helper validation.', + 'Migrate existing retry-worker.ts; helper hardening stays unchanged.', + ])('an unchanged-scope clause cannot hide an expansion: %s', async extra => { + const input = structuredClone(capturedParity); + input.questions[0].options[0].description += '\n' + extra; + const run = actor(); + await expect(run.callback('AskUserQuestion', input)).rejects.toThrow('question expands'); + expect(run.answers).toEqual([]); + }); }); +