mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-16 18:05:31 +02:00
* feat: add a restricted and supervised Claude Code runner Preserve configured authentication and models while enforcing tool access, strict completion JSON, bounded output and process cleanup. Cover argv, failure handling, session metadata and Windows process containment. * feat: route outside reviews by harness and migrate wrapper installs Use Claude Code from Codex and Codex from other supported hosts, with shared invocation rendering, positive gate validation and per-phase provenance. Rename /claude to /claude-code, repair managed shared and copied installations safely, and generate native Kiro skills. Add installed-workflow, failure-injection and live cross-harness regression coverage. * test: recognize CEO mode labels without terminal spacing The paid workflow rendered SCOPEEXPANSION at option 4, but its driver required a literal space. Match the leading mode title without cursor-spacing artifacts and ignore adjacent preview text. Preserve missing-target failures and downstream posture assertions. * test: isolate plan-count fixtures before starting review workflows Seed the complete test plan in a private git repository before launching Claude, so a bare slash command cannot review the live workspace while a delayed fixture message remains queued. Preserve count thresholds, parsers and budgets. Add initial-context and installed-discovery tests, and retain startup/terminal diagnostics on failed evaluations. * test: stabilize review fixtures and Claude eval startup Preserve source boundaries in workflow judge inputs, isolate CEO mode plans, and wait for interactive trust input readiness. Keep startup failure evidence and retain existing models, budgets, and assertions. Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: classify collapsed review modes and isolate seeded findings Keep review questions out of the setup count when terminal cursor positioning removes spaces. State existing webhook safeguards so the five-finding control measures its seeded defects without accidental extra security and concurrency gaps. Preserve question bands and the paired control. Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: isolate browser daemon state across free shards Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: stabilize native review counting and interactive navigation Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: prepare v1.82.0.0 release Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: eliminate browser and process-cleanup test flakes Pin every CI surface to Bun 1.4.0 to avoid extra-stdio finalizers closing reused live sockets. Add an isolated GC/listener regression that fails on Bun 1.3.13, and prevent coordinated rollback to an affected CI runtime. Check renderer cleanup against the render's own staging directory so concurrent renders cannot invalidate the assertion. Make the no-pgrep process-tree walk tolerate disappearing /proc entries, and synchronize its test fixture through child readiness and pipe EOF instead of sleeps. Validation: 9,157 passed, 31 skipped, zero failures across 556 files with retries disabled. Build, all-host generation freshness, and skill checks passed. All three races have failing-before/passing-after regressions. * fix: count completed native review questions in evals * fix: drive review navigation from confirmed native choices * fix: require complete section-loading eval reports * test: isolate telemetry HTTP transport from local assertions * fix: keep review input on the active native question * test: let tunnel revocation daemon choose an available port * test: allocate available ports for pairing and watchdog fixtures * fix: stabilize planning eval navigation and phase reporting * test: isolate installed runtime paths in planning evals * test: stabilize review evidence and concurrent refresh fixtures * fix: resolve design findings before editing the plan * fix: honor and persist disabled outside plan reviews * fix: preserve planning decisions and terminal evidence Load installed host reviews at autoplan phase entry and wait for completed reviewers and saved artifacts. Reuse approved remedies while preserving individual finding decisions. Drive interactive evals from the current terminal viewport, bind native questions across scrolling, and require complete native report evidence. Cover captured stale menus, permission lifecycles, setup classification, and disabled-review tool availability with deterministic regressions. Advance release metadata and the upgrade migration to the unclaimed 1.83.0.0 slot. * fix: drive native review questions and preserve current plans Use the native single-choice keyboard protocol and current terminal viewport, with per-question navigation inside packets and completed-call coverage. Keep permissions, multi-select menus, and Submit controls distinct. Send Autoplan reviewers the amended implementation plan, keep its review record separate, and supply retained application contracts in the chain fixture. Clarify individual DevEx decisions and complete CEO fix options; use one active plan destination for the section-loading report. * fix: preserve complete plan-review decisions * fix: recognize native plan dialogs and reviewer controls * fix: preserve review decisions and phase completion * fix: recognize completed reviews without losing findings * fix: preserve review continuity and native eval completion * test: fix native review completion and eval retry isolation * test: handle native review menus and complete eval fixtures * test: fix native review setup, completion, and isolation failures * test: limit native skill discovery to runtime assets * fix: bind Autoplan reviews to full ordered phase inputs * test: fix planning eval routing, counting, and timeout handling * chore: advance queued release to v1.84.0.0 * fix: preserve complete review inputs and planning decisions * fix: reconcile review approvals and preserve phase obligations * fix: preserve review obligations and unblock eval permissions Carry recorded Autoplan requirements into blind phase inputs, require Eng review approvals before exit, and exercise combined asynchronous flows in CEO reviews. Correct native finding and handoff classification and unblock repeated report edits using scoped request identities. * fix: retain plan requirements and complete native review dialogs * fix: complete native review prompts and retain plan references * fix: preserve review inputs and classify native eval evidence * fix: check competing completion orders in CEO reviews * fix: recognize review decisions and require phase methodology Require the current phase methodology before Autoplan snapshots. Correct substantive decision, closed handoff, and cache-finding classification, and honor the recommended implementation approach in native review dialogs. Add captured-transcript regressions without changing review thresholds, provider models, retries, or deadlines. * test: bind native review decisions and close completed handoffs * fix: complete review dialogs and verify methodology delivery * fix: preserve review evidence and unblock native eval prompts * fix: handle native review question completions * fix: recognize native review narration and controls * fix: count native review decisions and isolate eval fixtures * test: verify seeded review coverage and current artifact permissions * test: isolate model and brain-aware skill renders * fix: repair native workflow evaluation and clarify review steps * fix: stabilize workflow eval evidence and review guidance * test: repair native workflow observation and fixture isolation * fix: recognize completed workflow evidence and owned skill reads * test: repair seeded workflow delivery and completion evidence * test: recognize current review evidence across native forms * test: handle native review variants and permission redraws * fix: honor review preferences and recognize native eval evidence * test: recognize completed review decisions and queued permissions * test: match current review contracts and partial-line edits * test: recognize completed workflow evidence and bounded human waits * fix: preserve review entry gates and native eval interactions * fix: recognize native workflow evidence and preserve review gates * test: recognize current review evidence and preconfigure workflow fixtures * test: recognize completed review findings and scoped artifact permissions * fix: stabilize native workflow review and permission evidence * fix: recognize current review evidence and scoped edit confirmations Clarify Design and engineering review entry instructions and Design scoring. Recognize required legacy coverage and public Autoplan completion recaps. Bind the pending Edit confirmation to its exact file, ordered digest, and one-request approval when a preceding command display remains visible. Keep reviews within their existing size limits and preserve scope gates when extracting workflow fixtures from either supported preamble header. Keep failure outcomes, review thresholds, provider choices, and eval budgets. * fix: recover review workflow progress and eval evidence * fix: recognize valid review evidence and scope selection * test: fix review evidence parsing and repeated artifact prompts * test: recognize valid review decisions and pending native cards * fix(plan-eng-review): keep final navigation consistent with approved tasks * test: recognize valid review evidence and bind legacy diff requests * fix: stabilize review eval evidence and harness repair guidance * docs: update project documentation for v1.85.0.0 Co-Authored-By: OpenAI Codex <noreply@openai.com> * test: fix Windows CI fixtures and credential scan Rebase captured JSON values and filesystem evidence using the appropriate path convention. Compile native fake CLIs on Windows and synchronize pipe holder readiness, with cleanup retained when assertions fail. Assemble synthetic credential fixtures at runtime so the added-line scan keeps enforcing the same gate without flagging its own rejection controls. Discover generated skills directly for the empty-find regression check, avoiding a recursive scan through saved evaluation artifacts and dependencies. * fix: preserve source renders on Windows Compare canonical generator paths using native separators so an output sidecar pointing at the source cannot overwrite its skill or metadata. Keep the regression fixture isolated from the real checkout and expose freshness diagnostics before asserting subprocess status. Detach Windows drain-test pipe holders from the fake provider's automatic child cleanup while preserving the enclosing runner job and its assertions. * fix: clarify outside review fallback and CEO decisions Render one applicable own-harness fallback path and retain native review, disabled policy, and missing-coverage semantics. Align report field names and mode labels, and make the existing per-cut scope approval explicit. Regenerate skill outputs and keep the workflow judge's model, thresholds, and retry policy unchanged. * chore: move release to free version slot (v1.86.0.0) PR #2852 now claims v1.85.0.0. Align the release metadata and rename migration so upgrades from that version still receive it. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix: include engineering review prerequisites and restore branch context * fix: recognize coverage diagrams and clarify design review instructions * fix: preserve file identities and join Windows test processes --------- Co-authored-by: OpenAI Codex <noreply@openai.com>
1291 lines
123 KiB
TypeScript
1291 lines
123 KiB
TypeScript
import type { NativePlanQuestionCall, PlanCountTranscript } from './plan-count-transcript';
|
||
import type { AskUserQuestionFingerprint } from './claude-pty-runner';
|
||
import { hasRetainedLegacyCorpus } from './eng-retained-corpus';
|
||
|
||
/** Evidence for this fixture's four decision seeds; regression coverage is auto-added by the skill. */
|
||
export const ENG_DECISION_SEEDS = ['complexity', 'shared-cache', 'swallowed-errors', 'sequential-idp'] as const;
|
||
type Seed = typeof ENG_DECISION_SEEDS[number];
|
||
|
||
// Ignore displayed examples/code, while retaining inline code identifiers.
|
||
function prose(text: string, omitLiteralProse = false): string {
|
||
let fence: string | undefined;
|
||
const lines = text.split('\n').filter(line => {
|
||
const mark = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
||
if (mark) {
|
||
if (!fence) fence = mark[1];
|
||
else if (mark[1][0] === fence[0] && mark[1].length >= fence.length) fence = undefined;
|
||
return false;
|
||
}
|
||
return !fence && !/^(?: {0,3}>| {4}|\t)/.test(line);
|
||
}).join('\n');
|
||
return (omitLiteralProse ? lines.replace(/`([^`]+)`/g, (span, body: string) => /\s/.test(body) ? '' : span) : lines).replace(/[`*]/g, '');
|
||
}
|
||
|
||
function seedSubjects(q: NativePlanQuestionCall['questions'][number]): Seed[] {
|
||
// The actual issue subject, not cross-references in recommendations or other options,
|
||
// assigns credit. ELI10 can identify what a terse Promise.all title operates on.
|
||
const subject = q.question.split('\n').find(line => line.trim())?.trim() ?? '';
|
||
if (/^(?:>|`{3}|~{3}|example\b|quote\b|["“])|\b(?:no (?:issue|defect)|already (?:fixed|resolved)|hypothetical)\b/i.test(subject)) return [];
|
||
const title = subject.replace(/[`*]/g, '');
|
||
// A concise decision title can name the design choice while its own
|
||
// current explanation states the defect. Keep these three forms bound to
|
||
// that explanation and the same native option's concrete repair.
|
||
const decisionTitle = title.replace(/^D[1-9]\d*\s*[—–:-]\s*/, '');
|
||
const tenantWriters = /^Architecture issue [1-9]\d*: two services write the same tenant-keyed cache with no serialized mutations\. Who owns writes\?$/i.test(decisionTitle);
|
||
const decomposition = /^Reduce the ([2-9]\d*)-component decomposition or proceed as-is\?$/.exec(decisionTitle);
|
||
const rewrittenErrors = /^How should validateAndDispatch\(\) handle errors after the rewrite\?$/.test(decisionTitle);
|
||
const ownedRewriteChoice = Boolean(decomposition || rewrittenErrors);
|
||
if (ownedRewriteChoice && /^`[^`]*`[.?]?$/.test(subject.replace(/^D[1-9]\d*\s*[—–:-]\s*/, ''))) return [];
|
||
const explainedSeed: Seed | undefined = /^Reduce the new-class count before we review the rest\?$/.test(decisionTitle) || decomposition ? 'complexity'
|
||
: /^Who is allowed to write to the auth cache\?$/.test(decisionTitle) || tenantWriters ? 'shared-cache'
|
||
: /^How should validateAndDispatch\(\) handle errors\?$/.test(decisionTitle) || rewrittenErrors ? 'swallowed-errors' : undefined;
|
||
if (explainedSeed) {
|
||
const ordinal = /^D([1-9]\d*)\s*[—–:-]/.exec(title)?.[1];
|
||
const status = '(?:withdrawn|rejected|cancelled|canceled|superseded|resolved|fixed|optional|hypothetical|unproven|not current|no longer current)';
|
||
const owner = `(?:(?:this|the|that) (?:finding|issue|decision|gap|defect|assessment|explanation)${ordinal ? `|D${ordinal}` : ''})`;
|
||
const current = (value: string, option = false) => {
|
||
const subject = option ? `(?:${owner}|(?:this|the|that) (?:option|action|remedy|correction))` : owner;
|
||
const scalar = new RegExp(`((?:^|[.!?;]\\s+|\\n)[\\t ]*(?:Correction:\\s*)?${subject} (?:is|was|has been) )["“'‘\x60](${status})["”'’\x60]`, 'gim');
|
||
const formatted = ownedRewriteChoice ? value.replace(/\*\*/g, '') : value;
|
||
return prose(formatted.replace(scalar, '$1$2'), true).replace(/"[^"\n]*"|“[^”\n]*”|(?<![A-Za-z0-9])'[^'\n]*'(?![A-Za-z0-9])|‘[^’\n]*’/g, '');
|
||
};
|
||
const framed = /(?:^|[.!?;:]\s+|\n)(?:(?:Project\/branch\/task|ELI10):\s*)?(?:Source(?: excerpt| material)?|Quoted(?: source)?|Historical(?: assessment| example)?|If approved|Once approved|When approved|Pending approval|Assuming approval|Provided approval)[,:.]?\s/i;
|
||
const active = (value: string, option = false) => {
|
||
const text = current(value, option), subject = option ? `(?:${owner}|(?:this|the|that) (?:option|action|remedy|correction))` : owner;
|
||
if (ownedRewriteChoice && (/\b(?:if|once|when|unless) (?:approved|accepted)|\b(?:after|pending) approval\b/i.test(text) ||
|
||
/(?:^|[.!?;]\s+|\n)(?:Correction:\s*)?(?:this|the|that) (?:finding|issue|option|action|remedy|correction) (?:applies|proceeds|will proceed) only if\b/i.test(text))) return false;
|
||
return !framed.test(text) && !new RegExp(`(?:^|[.!?;]\\s+|\\n)(?:Correction:\\s*)?${subject} (?:is|was|has been) ${status}\\b`, 'i').test(text) &&
|
||
!(option && /(?:^|[.!?;]\s+|\n)(?:Correction:\s*)?(?:do not|don't|never|skip|cancel|withdraw) (?:drop|reduce|inject|flatten|rethrow|make|apply)\b/i.test(text)) &&
|
||
!(ownedRewriteChoice && option && /(?:^|[.!?;]\s+|\n)(?:Correction:\s*)?(?:do not|don't|never|skip|cancel|withdraw) (?:cut|remove|split|propagate)\b/i.test(text));
|
||
};
|
||
const text = current(q.question), explanations = [...text.matchAll(/^ELI10: (.+)$/gm)];
|
||
// A current correction about the named component/function overrides its
|
||
// earlier defect assertion; quoted history has already been removed.
|
||
if (decomposition && /(?:^|[.!?;]\s+|\n)(?:Correction:\s*)?(?:AuthCache (?:now |already )?has independent policy rules|TokenStore (?:now |already )?has a documented independent purpose)\b/i.test(text) ||
|
||
rewrittenErrors && /(?:^|[.!?;]\s+|\n)(?:Correction:\s*)?validateAndDispatch\(\) (?:now |already )?(?:rethrows every error|no longer swallows failures)\b/i.test(text)) return [];
|
||
const prefix = text.slice(text.indexOf('\n') + 1, explanations[0]?.index ?? 0).trim().split('\n').filter(Boolean);
|
||
if (explanations.length !== 1 || (prefix.length !== 1 && !(tenantWriters && prefix.length === 2 && /^\[P[0-3]\]/.test(prefix[1]!))) ||
|
||
!/^Project\/branch\/task: \S/.test(prefix[0]!) || !active(q.question)) return [];
|
||
const explanation = explanations[0]![1]!;
|
||
const options = q.options.filter(o => active(`${o.label}\n${o.description ?? ''}`, true))
|
||
.map(o => ({ label: current(o.label).replace(/^[1-9]\d*[A-D]\s+/, ''), description: current(o.description ?? '') }));
|
||
// A numbered decomposition choice owns its inventory and redundant-wrapper
|
||
// explanation. Keep the cut and injected-adapter remedy in the same option.
|
||
if (decomposition) {
|
||
const inventory = /, ([A-Za-z][\w]*(?: \+ [A-Za-z][\w]*)+)\.$/.exec(prefix[0]!)?.[1]?.split(' + ') ?? [];
|
||
const pieces = /^The plan builds (five|[1-9]\d*) new pieces, but one working cache already does the storing and invalidating\./.exec(explanation);
|
||
return new Set(inventory).size === Number(decomposition[1]) && inventory.length === Number(decomposition[1]) &&
|
||
['AuthBroker', 'SessionMint', 'AuthCache', 'TokenStore'].every(name => inventory.includes(name)) &&
|
||
pieces && Number(pieces[1] === 'five' ? 5 : pieces[1]) === Number(decomposition[1]) &&
|
||
/\bAuthCache is described as a facade over that adapter with no new rules, and TokenStore is never described at all\./.test(explanation) &&
|
||
options.some(o => {
|
||
const cut = /^(?:[A-D][):.]\s*)?(?:Cut|Drop|Remove) AuthCache \+ TokenStore, keep ([1-9]\d*)\b/.exec(o.label);
|
||
return cut && Number(cut[1]) === inventory.length - 2 &&
|
||
/^(?:✅\s*)?AuthBroker and SessionMint depend on the existing adapter through one small injected interface; no facade, no second store\./.test(o.description);
|
||
}) ? ['complexity'] : [];
|
||
}
|
||
// The future repair concerns a currently swallowing function, established
|
||
// by its own metadata; a general error-handling question is insufficient.
|
||
if (rewrittenErrors) return /\bvalidateAndDispatch\(\) is [1-9]\d* lines with (?:three|[1-9]\d*) nested try\/catch blocks that each swallow a different error class\b/.test(prefix[0]!) &&
|
||
/^When an auth function catches an error and quietly moves on, the request continues as if the check passed or never mattered\./.test(explanation) &&
|
||
options.some(o => /^(?:[A-D][):.]\s*)?Split into validate\(\) \+ dispatch\(\); one typed error boundary, deny-by-default\b/.test(o.label) &&
|
||
/^(?:✅\s*)?Each error class maps to an explicit AuthOutcome \(denied\/retryable\/misconfigured\) with a reason; nothing is swallowed, unknown errors deny\b/.test(o.description) ||
|
||
/^(?:[A-D][):.]\s*)?Keep one function; flatten the three catches into one that logs and rethrows$/.test(o.label) &&
|
||
/\bErrors are no longer silent; every failure is logged and surfaced\./.test(o.description)) ? ['swallowed-errors'] : [];
|
||
if (tenantWriters) return /\bAuthBroker\b/.test(prefix[0]!) && /\bSessionMint\b/.test(prefix[0]!) &&
|
||
/^Two services writing the same cache entry at the same time is a race\./.test(explanation) &&
|
||
!/(?:^|[.!?]\s+|\n)(?:Correction:\s*)?(?:the|this) (?:cache|writes|writers) (?:is|are|have been) (?:now ordered|now serialized|no longer shared)\b/i.test(text) &&
|
||
options.some(option => {
|
||
const actors = /\b(AuthBroker|SessionMint) is the only service that writes validated entries;\s*(AuthBroker|SessionMint) reads\b/.exec(option.description);
|
||
return /^[1-9]\d*[A-D][):.]\s*Single writer \+ generation check\b/.test(option.label) && actors && actors[1] !== actors[2] &&
|
||
/\bEach write carries the tenant generation read at validation start; the adapter rejects a write whose generation is stale\b/.test(option.description);
|
||
}) ? ['shared-cache'] : [];
|
||
if (explainedSeed === 'complexity') {
|
||
const wrapper = /^The plan invents a new cache wrapper \(([A-Za-z][\w]*)\) and a new token store on top of a cache adapter that already does tenant keying, expiry, and invalidation\./.exec(explanation);
|
||
return wrapper && options.some(o => /^Reduce\b/.test(o.label) &&
|
||
new RegExp(`\\bdrop ${wrapper[1]}\\/TokenStore classes\\.`).test(o.description)) ? [explainedSeed] : [];
|
||
}
|
||
if (explainedSeed === 'shared-cache') return /\bAuthBroker and SessionMint both mutating one backing cache\b/.test(prefix[0]!) &&
|
||
/^Two services write to the same cache and nothing orders their writes\./.test(explanation) &&
|
||
!/(?:^|[.!?]\s+|\n)(?:Correction:\s*)?(?:the|this) (?:cache|writes|writers) (?:is|are|have been) (?:now ordered|now serialized|no longer shared)\b/i.test(text) &&
|
||
options.some(o => /^Single writer\b/.test(o.label) && /^SessionMint writes\b/.test(o.description) && /\bAuthBroker reads\b/.test(o.description)) ? [explainedSeed] : [];
|
||
return /\bvalidateAndDispatch\(\) is [1-9]\d* lines, (?:three|[1-9]\d*) nested try\/catch blocks, each catch swallows a different error class\b/.test(prefix[0]!) &&
|
||
/^Right now when something goes wrong inside validation, the code catches the problem and keeps going as if nothing happened\./.test(explanation) &&
|
||
options.some(o => /^Flatten \+ typed errors\b/.test(o.label) && /\bLinear pipeline, typed error classes, one fail-closed boundary\b/.test(o.description) ||
|
||
/^Rethrow, one outer catch$/.test(o.label) && /\brethrow typed errors; outer catch denies\b/.test(o.description)) ? [explainedSeed] : [];
|
||
}
|
||
const offered = q.options.map(o => `${o.label} ${o.description ?? ''}`).join('\n');
|
||
const directAction = title.match(/\b(?:should|shall|can|do|would)\s+(?:we|I)\s+([^?]+)\?\s*$/i)?.[1];
|
||
const action = (re: RegExp) => re.test(offered) || Boolean(directAction && new RegExp(`^(?:${re.source})`, re.flags).test(directAction));
|
||
// A shared adapter title may name its cache in the issue's own asserted
|
||
// explanation. Options alone or a neighboring source excerpt cannot do so.
|
||
const adapterPublic = prose(q.question, true);
|
||
const adapterExplanations = [...adapterPublic.matchAll(/^ELI10:\s*(.+)$/gm)];
|
||
const adapterPrefix = adapterPublic.slice(0, adapterExplanations[0]?.index ?? 0).split('\n').filter(line => line.trim()).slice(1);
|
||
const adapterMetadata = adapterPrefix.join(' ').replace(/"[^"\n]*"|“[^”\n]*”/g, '');
|
||
const adapterExplanation = adapterExplanations.length === 1 && adapterPrefix.every(line => /^(?:Project\/branch\/task:|\[P[0-3]\])/.test(line))
|
||
&& !/\b(?:copied|quoted|source|hypothetical|historical)\s+(?:(?:source|quoted)\s+)?(?:example|excerpt|text|material)\b|\b(?:ELI10|assessment|finding)\s+is\s+not\s+(?:a\s+)?current\b/i.test(adapterMetadata)
|
||
? adapterExplanations[0]![1]! : '';
|
||
const adapterAssessment = adapterPublic.replace(/"[^"\n]*"|“[^”\n]*”/g, '');
|
||
const sharedAdapter = /\bwrite-after-invalidate race\b/i.test(title)
|
||
&& /\bSessionMint\b/.test(title) && /\bAuthBroker\b/.test(title) && /\bshared adapter\b/i.test(title)
|
||
&& /^(?:Even after injection,\s*)?(?:both|the two) services (?:write into|mutate) the same cache\./i.test(adapterExplanation)
|
||
&& !/\b(?:(?:this|that|the) (?:issue|finding|race)|Issue\s+[1-9]\d*)\s+(?:is|was|has been)\s+(?:withdrawn|retracted|rejected|resolved|fixed)\b|\bno (?:current )?shared-cache race\b/i.test(adapterAssessment);
|
||
const ids: Seed[] = [];
|
||
// The inventory alias needs its own current action; an inline quoted title
|
||
// or actions reproduced only as source material cannot establish this seed.
|
||
const inventoryProse = prose(q.question, true)
|
||
.replace(/(\b(?:this|the) class[ -]inventory decision is )['"“](withdrawn|rejected|cancelled|canceled|resolved)['"”]/gi, '$1$2')
|
||
.replace(/"[^"\n]*"|“[^”\n]*”/g, '');
|
||
const inventoryAction = q.options.some(o => {
|
||
const label = prose(o.label, true), description = prose(o.description ?? '', true).replace(/"[^"\n]*"|“[^”\n]*”/g, '');
|
||
return /^(?:[A-D][).:—–-]\s*)?(?:reduce|cut|simplify|remove|collapse|merge)\b[^.!?\n]{0,160}\b(?:classes|types|abstractions)\b/i.test(label)
|
||
&& !/^(?:source|quoted|historical|example|if approved|hypothetical)\b|\b(?:this|the) (?:option|action|remedy) is (?:withdrawn|rejected|cancelled|canceled)\b/i.test(description);
|
||
});
|
||
const classInventory = /^(?:D[1-9]\d*\s*[—–:-]\s*)?(?:Reduce|Simplify|Trim) the class inventory(?: before building)?\?$/i.test(prose(subject, true))
|
||
&& inventoryAction && !/\b(?:this|the) class[ -]inventory decision (?:is|was|has been) (?:withdrawn|rejected|cancelled|canceled|resolved|not current)\b/i.test(inventoryProse);
|
||
if (classInventory || /\b(?:scope|complexity|classes|types|abstractions)\b/i.test(title) &&
|
||
/\b(?:files|classes|types|abstractions)\b/i.test(title) &&
|
||
action(/\b(?:reduce|cut|simplify|remove|collapse|merge|pure function)\b/i)) ids.push('complexity');
|
||
if ((sharedAdapter || /\b(?:AuthCache|cache)\b/i.test(title) &&
|
||
/\b(?:global|module[ -]level|mutab\w*|both|shar\w*|ownership|writers|same\s+(?:AuthCache|cache))\b/i.test(title)) &&
|
||
action(/\b(?:inject\w*|DI|serializ\w*|single[ -]writer|ownership|composition root)\b/i)) ids.push('shared-cache');
|
||
if (/\b(?:validateAndDispatch|catch\w*)\b/i.test(title) &&
|
||
/\b(?:swallow\w*|nested|silent\w*|hidden|suppres\w*)\b/i.test(title) &&
|
||
action(/\b(?:split|rethrow|typed|flatten|propagat\w*)\b/i)) ids.push('swallowed-errors');
|
||
if (/\b(?:sequential|parallel\w*|Promise\.all(?:Settled)?)\b/i.test(title) &&
|
||
/\b(?:IDP|identity provider)\b/i.test(prose(q.question)) &&
|
||
action(/\b(?:parallel\w*|Promise\.all(?:Settled)?)\b/i)) ids.push('sequential-idp');
|
||
return ids.length ? ids : explainedSeedSubjects(q);
|
||
}
|
||
|
||
/** A decision may put its current defect in its own metadata/ELI10, not its title. */
|
||
function explainedSeedSubjects(q: NativePlanQuestionCall['questions'][number]): Seed[] {
|
||
const rawTitle = q.question.split('\n').find(line => line.trim())?.trim() ?? '';
|
||
const ordinal = /^D([1-9]\d*)\s*[—–:-]/.exec(rawTitle)?.[1];
|
||
const owner = `(?:(?:this|the|that) (?:finding|issue|decision|gap|defect|assessment|explanation|option|action|remedy)${ordinal ? `|D${ordinal}` : ''})`;
|
||
const inactive = '(?:withdrawn|retracted|rejected|cancelled|canceled|resolved|fixed|superseded|optional|hypothetical|unproven|not current|no longer current)';
|
||
const current = (value: string) => prose(value.replace(/\*\*/g, '').replace(
|
||
new RegExp(`(${owner} (?:is|was|has been) )["“'‘\x60](${inactive})["”'’\x60]`, 'gi'), '$1$2'), true)
|
||
.replace(/"[^"]*"|“[^”]*”|(?<!\w)'[^'\n]*'(?!\w)|‘[^’]*’/g, '');
|
||
const framed = /(?:^|[.!?;:]\s+|\n)(?:(?:Project\/branch\/task|ELI10):\s*)?(?:source|quoted|historical|example|hypothetical|if approved|once approved|when approved|pending approval|assuming approval|provided approval)\b/i;
|
||
const active = (value: string) => !framed.test(value)
|
||
&& !/\b(?:copied|quoted|historical)\s+(?:(?:source|quoted)\s+)?(?:example|excerpt|text|material)\b/i.test(value)
|
||
&& !new RegExp(`\\b${owner} (?:is|was|has been) ${inactive}\\b`, 'i').test(value)
|
||
&& !/\b(?:if|once|when|unless) (?:approved|accepted)|\b(?:after|pending) approval\b/i.test(value)
|
||
&& !/(?:^|[.!?;]\s+|\n)(?:Correction:\s*)?(?:do not|don't|never|skip|cancel|withdraw) (?:reduce|cut|remove|keep|flatten|split|map|rethrow|parallelize|run|apply)\b/i.test(value);
|
||
// Inline literal sentences and a non-current title cannot own the packet.
|
||
if (/^[`"“>]/.test(rawTitle.replace(/^D[1-9]\d*\s*[—–:-]\s*/, ''))) return [];
|
||
const text = current(q.question), lines = text.split('\n').filter(line => line.trim());
|
||
const explanations = lines.filter(line => /^ELI10:/.test(line));
|
||
const metadata = lines.filter(line => /^Project\/branch\/task:/.test(line));
|
||
const explanationIndex = /^\[P[0-3]\] /.test(lines[2] ?? '') ? 3 : 2;
|
||
if (explanations.length !== 1 || metadata.length !== 1 || lines[1] !== metadata[0] || lines[explanationIndex] !== explanations[0]
|
||
|| explanationIndex === 3 && !active(lines[2]!.replace(/^\[P[0-3]\] /, ''))
|
||
|| !/^Project\/branch\/task: \S/.test(metadata[0]!) || !active(text)) return [];
|
||
const title = lines[0]!.replace(/^D[1-9]\d*\s*[—–:-]\s*/, '');
|
||
if (!active(title)) return [];
|
||
const explanation = explanations[0]!, subject = metadata[0] + ' ' + explanation;
|
||
const options = q.options.map(o => current(`${o.label}\n${o.description ?? ''}`)).filter(active);
|
||
const ids: Seed[] = [];
|
||
// Step 0 may name the complexity decision in its title and put the concrete
|
||
// inventory in its own explanation. The reduction and retained backing
|
||
// store must belong to one current option, opposed by that same inventory.
|
||
if (/^(?:Step 0 )?complexity (?:check|decision):\s*(?:reduce|simplify|trim)\b/i.test(title)) {
|
||
const fileCounts = [...explanation.matchAll(/\b(?:touches|changes|modifies|spans) ([1-9]\d*) files\b/gi)];
|
||
const inventories = [...explanation.matchAll(/\b(?:adds|introduces) ([1-9]\d*) (?:new )?(?:classes|types) \(([^)]+)\)/gi)];
|
||
const names = inventories[0]?.[2]?.split(/,\s*(?:and )?| and /) ?? [];
|
||
const conditional = /\b(?:this|the|that) (?:finding|issue|decision|option|action|remedy) (?:(?:applies|proceeds|will proceed) (?:only )?(?:if|once|when)|(?:is|was|has been) conditional on (?:user )?approval)\b|(?:^|\n|[.!?;]\s+)(?:(?:ELI10|Project\/branch\/task):\s*)?(?:if|once|when|assuming|provided) (?:the )?(?:user|owner|reviewer) (?:approves|agrees|accepts)\b/i;
|
||
const currentOptions = options.filter(o => !conditional.test(o));
|
||
if (fileCounts.length === 1 && inventories.length === 1 && names.length === Number(inventories[0]![1])
|
||
&& new Set(names).size === names.length && (Number(fileCounts[0]![1]) >= 8 || names.length >= 2)
|
||
&& ['TokenStore', 'SessionMint', 'AuthCache', 'RequestPolicy'].every(name => names.includes(name)) && /\bAuthBroker\b/.test(explanation)
|
||
&& /\b(?:existing|current) (?:cache )?adapter (?:already )?keys tokens by tenant\b[^.!?]{0,40}\bevicts?\b[^.!?]{0,40}\binvalidates?\b/i.test(explanation)
|
||
&& /\bAuthCache (?:is|remains) (?:just |only )?a facade (?:over (?:it|the (?:existing |current )?(?:cache )?adapter)|for (?:that|the) adapter)\b/i.test(explanation)
|
||
&& /\bTokenStore (?:looks like|is|adds) (?:a |another )?(?:second|duplicate) (?:token )?store\b|\bTokenStore duplicates (?:the )?(?:existing |current )?adapter(?:'s)? token storage\b/i.test(explanation)
|
||
&& /\bRequestPolicy\b[^.!?]{0,90}\b(?:currently )?(?:has|serves) (?:only )?(?:one|a single) consumer\b/i.test(explanation)
|
||
&& !conditional.test(text)
|
||
&& !/\bTokenStore (?:now |already )?has (?:a documented )?independent purpose|\bRequestPolicy (?:now |already )?has (?:two|multiple|a second) consumers?\b/i.test(text)
|
||
&& currentOptions.some(o => /^(?:[A-D][):.]\s*)?(?:Reduce:\s*)?(?:cut|remove|drop) TokenStore\b/i.test(o)
|
||
&& /\b(?:demote|inline|flatten) RequestPolicy\b|\b(?:make|turn) RequestPolicy (?:into )?(?:a )?(?:plain|pure) function\b/i.test(o)
|
||
&& /\b(?:one|single|only) (?:(?:token|backing) )?(?:store|storage layer|cache)\b[^.!?\n]{0,80}\b(?:existing|current) (?:cache )?adapter\b|\b(?:existing|current) (?:cache )?adapter (?:as|is|remains|provides) (?:the )?(?:one|single|only) (?:(?:token|backing) )?(?:store|storage layer|cache)\b/i.test(o)
|
||
&& /\bAuthCache\b/.test(o))
|
||
&& currentOptions.some(o => /^(?:[A-D][):.]\s*)?(?:Proceed as-is|keep|retain)\b/i.test(o)
|
||
&& new RegExp(`\\b${inventories[0]![1]} (?:new )?(?:classes|types)\\b`, 'i').test(o)
|
||
&& new RegExp(`\\b${fileCounts[0]![1]} files\\b`, 'i').test(o)
|
||
&& /\b(?:two|2|separate) invalidation (?:paths|stories)\b/i.test(o))) ids.push('complexity');
|
||
}
|
||
// The overlapping stores, reduced component count and single backing store
|
||
// must be this question's finding and one option's complete repair.
|
||
if (/\b(?:scope|components?|pieces|classes|decomposition)\b/i.test(title)
|
||
&& ['AuthBroker', 'SessionMint', 'AuthCache', 'TokenStore'].every(name => subject.includes(name))
|
||
&& /\b(?:plan (?:adds|builds)|new)\b/i.test(explanation)
|
||
&& /\b(?:all|both) store tokens\b|\b(?:overlapping|redundant|duplicate) (?:token )?(?:stores|caches|storage)\b/i.test(explanation)
|
||
&& !/\b(?:now|already) (?:have|has) (?:independent|distinct)|\b(?:no longer|not) (?:overlapping|redundant|duplicate)\b/i.test(text)
|
||
&& options.some(o => /^(?:[A-D][):.]\s*)?(?:reduce|cut|remove|drop|fewer|simplify)\b/i.test(o)
|
||
&& /\b(?:keep|retain) AuthBroker\b/i.test(o) && /\bSessionMint\b/.test(o)
|
||
&& /\binjected AuthCache\b|\binject(?:ed)? (?:the )?(?:existing |shared )?(?:cache )?adapter\b/i.test(o)
|
||
&& /\b(?:one|single) (?:backing store|cache|storage layer)\b/i.test(o))) ids.push('complexity');
|
||
if (/\bvalidateAndDispatch\b/.test(title)
|
||
&& /\bcatch(?:es)?\b[^.!?]{0,100}\bswallow\w*\b[^.!?]{0,60}\b(?:error|failure)/i.test(subject)
|
||
&& /\b(?:quietly|silent|nothing is logged|keeps? going|carries on)\b/i.test(explanation)
|
||
&& !/\bvalidateAndDispatch\(\) (?:now |already )?(?:rethrows every error|no longer swallows failures)\b/i.test(text)
|
||
&& options.some(o => /\b(?:flatten|split|named helpers)\b/i.test(o)
|
||
&& /\b(?:typed (?:error )?boundary|one (?:error )?(?:boundary|catch))\b/i.test(o)
|
||
&& /\bmaps?\b[^.!?]{0,140}\b(?:[45]\d\d|response|outcome)/i.test(o)
|
||
&& /\brethrows? (?:unknowns|unknown errors)|\bpropagates? (?:unknown|all) (?:errors|failures)\b/i.test(o))) ids.push('swallowed-errors');
|
||
if (/\b(?:IDP|identity provider) calls?\b/i.test(title)
|
||
&& /\bsequential\b[^.!?]{0,60}\b(?:IDP|identity provider) calls?\b/i.test(metadata[0]!)
|
||
&& /\bindependent\b/i.test(metadata[0]!)
|
||
&& /\b(?:at once|parallel\w*|concurrent\w*)\b/i.test(explanation)
|
||
&& !/\b(?:calls|requests) (?:are |now |already )*(?:parallel|concurrent|no longer sequential)\b/i.test(text)
|
||
&& options.some(o => /\b(?:Promise\.all|paralleliz\w*|concurrent\w*)\b/i.test(o)
|
||
&& /\b(?:calls|requests|siblings)\b/i.test(o))) ids.push('sequential-idp');
|
||
return ids;
|
||
}
|
||
|
||
function completedDecision(call: NativePlanQuestionCall, startedAt: number, finishedAt: number): boolean {
|
||
const answeredAt = Date.parse(call.answeredAt ?? '');
|
||
if (!call.sessionId || !call.toolUseId || call.answered !== true || call.failed !== false ||
|
||
!Number.isFinite(answeredAt) || answeredAt < startedAt || answeredAt > finishedAt ||
|
||
call.questions.length < 1 || call.questions.length > 4 || !Array.isArray(call.unansweredQuestionIndices) ||
|
||
call.unansweredQuestionIndices.length !== 0) return false;
|
||
return new Set(call.questions.map(q => q.question)).size === call.questions.length &&
|
||
Object.keys(call.answers ?? {}).length === call.questions.length &&
|
||
call.questions.every(q => q.question.trim() && !q.multiSelect && q.options.length >= 2 && q.options.length <= 4 &&
|
||
q.options.every(o => o.label.trim()) && new Set(q.options.map(o => o.label)).size === q.options.length &&
|
||
q.options.some(o => call.answers?.[q.question] === o.label));
|
||
}
|
||
|
||
/** Structural eligibility for this distinct-issue counter, not seed quality. */
|
||
function batchingIssueNumber(call: NativePlanQuestionCall): string | undefined {
|
||
if (!completedDecision(call, 0, Date.now()) || call.questions.length !== 1) return;
|
||
const q = call.questions[0]!;
|
||
const issue = /^(?:D[1-9]\d*\s*[—–:-]\s*)?Issue ([1-9]\d*)\s*:\s*\S[^\n]*$/i.exec(q.question.split('\n')[0]!)?.[1];
|
||
if (!issue || !new RegExp(`^(?:Arch(?:itecture)?|Code quality|Tests?|Testing|Performance|Security)(?: ${issue})?$`, 'i').test(q.header.trim())) return;
|
||
const optionIds = q.options.map(o => /^([1-9]\d*)([A-D])[.):]\s+\S/i.exec(o.label));
|
||
if (optionIds.some(id => id?.[1] !== issue) || new Set(optionIds.map(id => id![2]!.toUpperCase())).size !== q.options.length) return;
|
||
// Owned scalar statuses remain current prose; a whole code example does not.
|
||
const owner = `(?:(?:this|the|that) (?:issue|finding|decision)|Issue ${issue})`;
|
||
const statusPrefix = `(?:^|[.!?;]\\s+|\\n)(?:Correction:\\s*)?${owner} (?:is|was|has been) `;
|
||
const scalarOwner = new RegExp(`${statusPrefix}$`, 'i');
|
||
const text = q.question.replace(/`([^`\n]+)`/g, (span, body: string, at: number, source: string) =>
|
||
scalarOwner.test(source.slice(0, at)) ? body : span);
|
||
const inactive = new RegExp(`${statusPrefix}["“'‘]?(?:withdrawn|cancelled|canceled|rejected|superseded|resolved|closed|hypothetical|not current|no longer current)\\b`, 'i');
|
||
return inactive.test(prose(text, true)) ? undefined : issue;
|
||
}
|
||
|
||
/** Batching measures separate native issue decisions; seed quality is checked separately. */
|
||
export function isEngBatchingIssueAUQ(fp: AskUserQuestionFingerprint, priorCalls: readonly NativePlanQuestionCall[] = []): boolean {
|
||
const call = fp.nativeCall;
|
||
if (!call || fp.signature !== `${call.sessionId}:${call.toolUseId}` ||
|
||
(fp.nativeQuestionIndex !== undefined && fp.nativeQuestionIndex !== 0) ||
|
||
priorCalls.some(prior => prior.sessionId !== call.sessionId || prior.toolUseId === call.toolUseId)) return false;
|
||
const issue = batchingIssueNumber(call);
|
||
if (!issue) return false;
|
||
const q = call.questions[0]!;
|
||
if (fp.options.length !== q.options.length || !fp.options.every((o, i) => o.index === i + 1 && o.label === q.options[i]!.label)) return false;
|
||
// Re-asking an eligible issue cannot inflate the floor; setup and batches do not suppress later separate decisions.
|
||
return !priorCalls.some(prior => batchingIssueNumber(prior) === issue);
|
||
}
|
||
|
||
/** A named required test can specify characterization without an "Add" prefix. */
|
||
function requiredLegacyCharacterization(task: string): boolean {
|
||
const text = task.replace(/\s+/g, ' ');
|
||
return /^legacyAuthFlow(?:\(\))?\s+(?:regression|characterization)\s+tests?\.\s+Before\s+(?:the\s+)?(?:rewrite|refactor|change),\s+(?:capture|pin|record)\s+(?:the\s+)?(?:current|existing|prior)\b[^.;!?]{0,240}\bbehavior\s+of\s+legacyAuthFlow(?:\(\))?\b[^.;!?]*\.\s+The\s+rewritten\s+(?:path|flow|implementation)\s+must\s+pass\s+the\s+same\s+assertions\./i.test(text)
|
||
&& !/["“”]|\b(?:not|never|skip\w*|defer\w*|maybe|might|could|if|unless|optional|hypothetical|unproven)\b/i.test(text)
|
||
&& !/\bno\s+(?:(?:regression|characterization)\s+)?(?:tests?|fixtures?)\s+(?:are\s+)?(?:needed|required)\b/i.test(text);
|
||
}
|
||
|
||
/** Required suites bind a numbered task to an untouched legacy baseline or parity oracle. */
|
||
function declaredLegacyCharacterization(text: string): boolean {
|
||
const sections: Array<{ title: string; body: string[]; asserted: boolean }> = [];
|
||
const owners: Array<{ level: number; asserted: boolean }> = [];
|
||
let preamble = '', sourcePreamble = false;
|
||
const sourceFrame = (body: string) => {
|
||
const text = body.replace(/"[^"\n]*"|“[^”\n]*”/g, '').replace(/\s+/g, ' ');
|
||
return /\b(?:(?:hypothetical|historical) example|unproven hypothesis|(?:source|quoted) (?:material|text) only|(?:are|is) not requirements? of this plan)\b/i.test(text)
|
||
|| /^\s*(?:(?:quoted )?source(?: (?:excerpt|text|material))?|(?:historical|earlier|previous) (?:review )?assessment):(?:\s|$)/i.test(text);
|
||
};
|
||
for (const line of prose(text).split('\n')) {
|
||
const heading = /^(#{1,6})\s+(.+)$/.exec(line);
|
||
if (heading) {
|
||
while (owners.length && owners.at(-1)!.level >= heading[1]!.length) owners.pop();
|
||
if (/^Current reviewed plan$/i.test(heading[2]!) && owners.length === 0) sourcePreamble = false;
|
||
const asserted = !sourcePreamble && owners.every(owner => owner.asserted)
|
||
&& !/\b(?:source|example|hypothetical|proposed|optional|quoted|historical|template|unproven)\b/i.test(heading[2]!);
|
||
owners.push({ level: heading[1]!.length, asserted });
|
||
sections.push({ title: heading[2]!, body: [], asserted });
|
||
} else if (sections.length) {
|
||
const section = sections.at(-1)!;
|
||
section.body.push(line);
|
||
if (sourceFrame(section.body.join(' '))) section.asserted = owners.at(-1)!.asserted = false;
|
||
} else {
|
||
preamble += ' ' + line;
|
||
sourcePreamble = sourceFrame(preamble);
|
||
}
|
||
}
|
||
const current = sections.filter(section => section.asserted);
|
||
const mandatory = current.filter(section => /^CRITICAL regression \(mandatory, regression rule\)$/i.test(section.title));
|
||
const declaration = /^legacyAuthFlow(?:\(\))? is (?:existing|current) behavior being (?:modified|refactored)\b[^!?]{0,240}\.\s+(?:A|The) characterization test suite for legacyAuthFlow(?:\(\))? is (?:added|required) as a (?:critical|mandatory) requirement:\s*(?:capture|pin|record) (?:current|existing|prior)\b[^.!?]{1,400}\.\s+This suite runs against the flag-OFF path and is the oracle the new path is compared to during rollout\./i;
|
||
const withdrawn = (body: string, task?: string) => new RegExp(
|
||
`\\b(?:${task ? `${task}|` : ''}(?:this|the|that)\\s+(?:(?:characterization|regression|contract)\\s+)?(?:suite|task|test|requirement)|(?:characterization|regression)\\s+(?:suite|tests?))\\s+(?:(?:is|was|has been)\\s+)?(?:(?:not|no longer)\\s+(?:required|needed)|cancelled|canceled|withdrawn|rejected|deferred|optional)\\b`, 'i').test(body)
|
||
|| /\b(?:do not|never|skip|defer|cancel|withdraw)\s+(?:run(?:ning)?\s+)?(?:the|this)\s+(?:characterization\s+)?suite\b/i.test(body);
|
||
const declared = mandatory.some(section => {
|
||
const body = section.body.join(' ').replace(/\s+/g, ' ').trim();
|
||
const claim = declaration.exec(body)?.[0];
|
||
return claim && !/["“”]|\b(?:maybe|might|could|if|unless|optional|hypothetical|unproven)\b/i.test(claim)
|
||
&& !withdrawn(body);
|
||
});
|
||
if (declared) for (const section of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const tasks = section.body.join('\n').split(/\n(?=-\s)/);
|
||
for (const task of tasks) {
|
||
const match = /^\s*-\s+(?:\[[ xX]\]\s*)?(T[1-9]\d*)(?:\s+\([^\n)]*\))?\s+[—–:-]\s+(?:[A-Za-z][\w-]*(?:\/[A-Za-z][\w-]*)+(?:\s+tests)?\s+[—–]\s+)?CRITICAL regression:\s+characterization suite for legacyAuthFlow(?:\(\))? prior behavior[\t ]*(?:\n|$)/i.exec(task);
|
||
if (!match || withdrawn(task, match[1])) continue;
|
||
const baseline = new RegExp(`^[1-9]\\d*\\. Run the characterization suite \\(${match[1]}\\) against the untouched legacyAuthFlow(?:\\(\\))? first and commit it green\\. This is the baseline\\.`, 'i');
|
||
if (current.some(s => s.title === 'Verification' && baseline.test(s.body.join(' ').replace(/\s+/g, ' ').trim())
|
||
&& !withdrawn(s.body.join(' '), match[1]))) return true;
|
||
}
|
||
}
|
||
for (const section of current.filter(s => /^CRITICAL: regression contract test for legacyAuthFlow\(\) \(iron rule, no decision needed\)$/.test(s.title))) {
|
||
const body = section.body.join(' ').replace(/\s+/g, ' ').trim();
|
||
const parity = /^The rewrite modifies existing behavior with no covering test \([^)]{1,120}\)\. Add ([A-Za-z][\w/-]*\.contract\.test\.[jt]s): a fixture table of \(tenant, token, policy\) cases covering [^.!?]{1,300}\. Run each fixture through legacyAuthFlow\(\) and ([A-Za-z][\w]*)\.authenticate\(\) and assert identical ([A-Za-z][\w]*) shape on success and identical error code on failure\. This test is also the gate for flipping any tenant's flag and for TODO [1-9]\d* removal\./.exec(body);
|
||
if (!parity || withdrawn(body) || /["“”]|\b(?:maybe|might|could|if|unless|optional|hypothetical|unproven)\b/i.test(parity[0])) continue;
|
||
const unchanged = current.some(s => {
|
||
if (!s.title.endsWith(`: Per-tenant flag routes legacy vs ${parity[2]}`)
|
||
|| !/^Issue [1-9]\d* \(D[1-9]\d*, chose [1-9]\d*[A-D]\): /.test(s.title)) return false;
|
||
const body = s.body.join('\n');
|
||
const release = /(?:^|\n)- A tenant-keyed flag [A-Za-z][\w.]*\[tenantId\] \(default off\) selects the path at the\s+login entry point\. legacyAuthFlow\(\) stays callable and unchanged this release\./.exec(body);
|
||
const prefix = release ? body.slice(0, release.index).trim().split(/\n\s*\n/).at(-1) ?? '' : '';
|
||
return Boolean(release) && !/^(?:if|unless|maybe|perhaps|proposed|optional)\b/i.test(prefix)
|
||
&& !/\blegacyAuthFlow(?:\(\))?\s+(?:(?:is|was|will be|has been)\s+)?(?:changed|modified|rewritten|removed|withdrawn|not unchanged|no longer unchanged)\b/i.test(s.body.join(' '));
|
||
});
|
||
if (!unchanged) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
for (const task of tasks.body.join('\n').split(/\n(?=-\s)/)) {
|
||
const match = /^\s*-\s+(?:\[[ xX]\]\s*)?(T[1-9]\d*)(?:\s+\([^\n)]*\))?\s+[—–:-]\s+Tests\s+[—–]\s+CRITICAL regression contract test: same fixtures through legacyAuthFlow\(\) and ([A-Za-z][\w]*), identical ([A-Za-z][\w]*) \/ error codes[\t ]*(?:\n|$)/.exec(task);
|
||
if (!match || match[2] !== parity[2] || match[3] !== parity[3] || withdrawn(task, match[1])) continue;
|
||
const files = /^\s+- Files: ([^\n]+)$/m.exec(task);
|
||
if (files?.[1] === parity[1] && /^\s+- Verify: contract suite green on both paths[\t ]*$/m.test(task)) return true;
|
||
}
|
||
}
|
||
}
|
||
// A mandatory snapshot can state the legacy oracle as a required test
|
||
// list item, then bind it to the numbered task's unchanged-code verification.
|
||
const snapshotSource = prose(text, true).replace(/\s+/g, ' ');
|
||
const unquoted = (body: string) => body.replace(/"[^"\n]*"|“[^”\n]*”/g, '');
|
||
const suiteWithdrawn = current.some(s => {
|
||
// A named foreign suite owns its generic withdrawal; it cannot cancel
|
||
// the legacy obligation in another section of the same report.
|
||
const namedSuite = /^(.*?)\b(?:regression|characterization)\s+(?:suite|tests?)\b/i.exec(s.title);
|
||
const foreignSuite = Boolean(namedSuite?.[1]?.trim() && !/^(?:legacy(?:AuthFlow(?:\(\))?)?|final|current|updated)[\s:—–-]*$/i.test(namedSuite[1]));
|
||
return unquoted(s.body.join('\n')).split(/\n|[.!?]\s+/).some(statement => {
|
||
const subject = /^(?:Correction:\s*)?(?:the|this|that)\s+(legacy\s+)?(?:regression|characterization)\s+(?:suite|tests?)\b/i.exec(statement.trim());
|
||
return Boolean(subject && (!foreignSuite || subject[1]) && withdrawn(statement));
|
||
});
|
||
});
|
||
for (const section of current.filter(s => /^Required tests(?: \([^\n]*\))?$/i.test(s.title))) {
|
||
const body = section.body.join('\n').trim();
|
||
if (!body.startsWith('- ')) continue;
|
||
for (const block of body.split(/\n(?=-\s)/)) {
|
||
const claim = block.replace(/\s+/g, ' ').trim();
|
||
if (suiteWithdrawn || !/^- CRITICAL regression legacyAuthFlow(?:\(\))? snapshot: capture current outputs for [^;.!?]{1,240} BEFORE any change; assert both legacy \(flag OFF\) and new \(flag ON\) paths produce identical observable results\. Mandatory under the coverage-audit regression rule\./.test(claim)
|
||
|| !snapshotSource.includes(claim) || withdrawn(unquoted(claim))
|
||
|| /["“”]|\b(?:not|never|maybe|might|could|if|unless|optional|hypothetical|unproven)\b/i.test(claim)) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const body = tasks.body.join('\n').trim();
|
||
const taskPrefix = body.split(/\n(?=-\s)/)[0]?.trim() ?? '';
|
||
if (!body.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(taskPrefix)
|
||
|| /\b(?:if|unless|optional|hypothetical|example|source|quoted|unproven)\b/i.test(taskPrefix))) continue;
|
||
for (const task of body.split(/\n(?=-\s)/)) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–] [A-Za-z][\w-]*(?:\/[A-Za-z][\w-]*)+ [—–] Snapshot legacyAuthFlow\(\) behavior as regression tests before any change[\t ]*(?:\n|$)/.exec(task);
|
||
if (!match || !snapshotSource.includes(task.replace(/\s+/g, ' ').trim())
|
||
|| !/^\s+- Verify: tests pass against unmodified legacy code, then against flag-OFF route[\t ]*$/m.test(task)
|
||
|| withdrawn(unquoted(task).replace(/\b(?:this|that|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement'), match[1])) continue;
|
||
const taskWithdrawal = new RegExp(`\\b${match[1]}\\s+(?:is|was|has been)\\s+(?:cancelled|canceled|withdrawn|rejected|deferred|optional|not required|no longer required)\\b`, 'i');
|
||
if (!current.some(s => taskWithdrawal.test(unquoted(s.body.join('\n'))))) return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// A mandatory declaration can require capture before touching the legacy
|
||
// function, with a task and ordered verification on both router settings.
|
||
const sourceOwner = (body: string) => sourceFrame(body) ||
|
||
/\b(?:is|was|presents?|represents?)\s+(?:(?:only|just)\s+)?(?:an?\s+)?(?:quoted|hypothetical|historical|example|template)\b/i.test(body);
|
||
const conditionalOwner = (prefix: string) => /^(?:if|unless|maybe|perhaps|proposed|optional)\b/i.test(prefix.trim().split('\n').at(-1)?.trim() ?? '');
|
||
for (const section of current.filter(s => /^REGRESSION \(mandatory rule, no approval needed\) [—–-] CRITICAL$/i.test(s.title))) {
|
||
const body = unquoted(section.body.join(' ')).replace(/\s+/g, ' ').trim();
|
||
const declaration = /(?:^|[.!?]\s+)Add a characterization (?:test )?suite for legacyAuthFlow\(\) before (?:touching|changing|refactoring) it:\s*(?:capture|pin|record) current inputs and outputs \([^()!?]{1,300}\) and run the same suite against ([A-Za-z][\w]*) on both flag settings\. A behavior difference between paths is a test failure\b/i.exec(body);
|
||
if (!declaration || suiteWithdrawn || withdrawn(body) ||
|
||
!snapshotSource.includes(declaration[0].trim()) || sourceOwner(body.slice(0, declaration.index)) ||
|
||
/\b(?:if|unless|maybe|might|could|proposed|optional|hypothetical|unproven)\b/i.test(body.slice(0, declaration.index))) continue;
|
||
for (const section of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = section.body.join('\n').trim();
|
||
const taskPrefix = taskBody.split(/\n(?=-\s)/)[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(taskPrefix) ||
|
||
/\b(?:if|unless|optional|hypothetical|example|source|quoted|unproven)\b/i.test(taskPrefix))) continue;
|
||
for (const task of taskBody.split(/\n(?=-\s)/)) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w-]*(?:\/[A-Za-z][\w-]*)+ [—–-] CRITICAL characterization suite for legacyAuthFlow\(\), run on both router paths[\t ]*(?:\n|$)/i.exec(task);
|
||
const verify = /^\s+- Verify: suite passes on legacy before any refactor; passes on new path before flag enable[\t ]*$/m.exec(task);
|
||
const taskIntro = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || !verify || !snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
conditionalOwner(taskIntro) || sourceOwner(taskIntro) || conditionalOwner(task.slice(0, verify.index)) || sourceOwner(unquoted(task.slice(0, verify.index))) ||
|
||
withdrawn(unquoted(task).replace(/\b(?:this|that|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement'), match[1])) continue;
|
||
const taskWithdrawal = new RegExp(`\\b${match[1]}\\s+(?:is|was|has been)\\s+(?:cancelled|canceled|withdrawn|rejected|deferred|optional|not required|no longer required)\\b`, 'i');
|
||
if (current.some(s => taskWithdrawal.test(unquoted(s.body.join('\n'))))) continue;
|
||
for (const verification of current.filter(s => /^Verification(?: \([^\n]*\))?$/i.test(s.title))) {
|
||
const body = unquoted(verification.body.join('\n')).trim();
|
||
const baseline = /^([1-9]\d*)\. Run the characterization suite against legacyAuthFlow\(\) on the unmodified code; it must pass before any refactor lands\.[\t ]*$/m.exec(body);
|
||
const compare = new RegExp(`^([1-9]\\d*)\\. Run the characterization suite through ${declaration[1]} with the flag on new; zero differences\\.[\\t ]*$`, 'm').exec(body);
|
||
if (baseline && compare && Number(baseline[1]) < Number(compare[1]) && baseline.index < compare.index &&
|
||
!conditionalOwner(body.slice(0, baseline.index)) && !conditionalOwner(body.slice(0, compare.index)) &&
|
||
!sourceOwner(body.slice(0, baseline.index)) && !sourceOwner(body.slice(0, compare.index)) &&
|
||
!withdrawn(body.replace(/\b(?:this|that|the)\s+(?:baseline|verification)\b/gi, 'this requirement'), match[1]) &&
|
||
snapshotSource.includes(baseline[0]) && snapshotSource.includes(compare[0])) return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// A golden-master requirement names the existing output oracle, then ties
|
||
// its capture task to an untouched baseline and reruns after later tasks.
|
||
const goldenSourceOwner = (body: string) => sourceOwner(body) || /(?:^|\n)\s*(?:Source|Quoted source) excerpt:\s*(?:\n|$)/i.test(body);
|
||
// A staged refactor can bind its baseline and parity checks to two tasks,
|
||
// with the release labels separate from their task identities.
|
||
const staged = current.filter(s => /^REGRESSION \(CRITICAL, mandatory\)$/i.test(s.title));
|
||
const taskSections = current.filter(s => s.title === 'Implementation Tasks');
|
||
if (!suiteWithdrawn && staged.length === 1 && taskSections.length === 1) {
|
||
const stagedSource = (value: string) => goldenSourceOwner(unquoted(value)) ||
|
||
/(?:^|\n)\s*(?:Source|Quoted source|Earlier review assessment):/i.test(unquoted(value));
|
||
const stagedConditional = (value: string) => conditionalOwner(value) ||
|
||
/(?:^|\n)\s*(?:assuming|provided)\b/i.test(unquoted(value));
|
||
const body = unquoted(staged[0]!.body.join(' ')).replace(/\s+/g, ' ').trim();
|
||
const declaration = /^(?:[A-Za-z][\w./-]*:[1-9]\d*(?:-[1-9]\d*)? [—–-]\s*)?This is existing behavior being modified with no covering test\. (PR[1-9]\d*) adds characterization tests that pin every observable outcome of legacyAuthFlow\(\) \(([^()!?]{1,600})\) before any rewrite\. They run against the legacy path in \1, against both paths in (PR[1-9]\d*), and are folded into pipeline tests in (PR[1-9]\d*)\. Pre-authorized by the regression rule\.$/.exec(body);
|
||
const tasksText = taskSections[0]!.body.join('\n').trim();
|
||
const taskBlocks = tasksText.split(/\n(?=-\s)/);
|
||
const ids = taskBlocks.map(t => /^- (?:\[[ xX]\] )?(T[1-9]\d*)\b/.exec(t)?.[1]).filter(Boolean);
|
||
const taskPrefix = taskBlocks[0]!.startsWith('- ') ? '' : taskBlocks[0]!;
|
||
if (declaration && Number(declaration[1]!.slice(2)) < Number(declaration[3]!.slice(2)) &&
|
||
Number(declaration[3]!.slice(2)) < Number(declaration[4]!.slice(2)) &&
|
||
!withdrawn(body) && !stagedSource(taskPrefix) && !stagedConditional(taskPrefix) &&
|
||
ids.length === new Set(ids).size) {
|
||
const baselinePattern = new RegExp(`^- (?:\\[[ xX]\\] )?(T[1-9]\\d*)(?: \\([^\\n)]*\\))? [—–-] ${declaration[1]} legacy auth [—–-] Write characterization \\(regression\\) tests pinning legacyAuthFlow\\(\\) prior behavior before any rewrite[\\t ]*(?:\\n|$)`);
|
||
for (const baseline of taskBlocks) {
|
||
const task = baselinePattern.exec(baseline);
|
||
const verify = /^\s+- Verify: suite green against unmodified legacy path; ([1-9]\d*) cases recorded as oracle[\t ]*$/m.exec(baseline);
|
||
if (!task || !verify || Number(verify[1]) !== declaration[2]!.split(',').length ||
|
||
stagedSource(baseline) || stagedConditional(baseline.slice(0, verify.index))) continue;
|
||
const parityPattern = new RegExp(`^- (?:\\[[ xX]\\] )?(T[1-9]\\d*)(?: \\([^\\n)]*\\))? [—–-] ${declaration[3]} strangler fig [—–-] Make legacyAuthFlow delegate to the new pipeline behind a feature flag; ${task[1]} characterization tests pass against both paths[\\t ]*(?:\\n|$)`);
|
||
for (const parity of taskBlocks) {
|
||
const rerun = parityPattern.exec(parity);
|
||
const parityVerify = new RegExp(`^[\\t ]+- Verify: ${task[1]} suite green with flag on and off[\\t ]*$`, 'm').exec(parity);
|
||
if (!rerun || task[1] === rerun[1] || !parityVerify || taskBlocks.indexOf(baseline) >= taskBlocks.indexOf(parity) ||
|
||
stagedSource(parity) || stagedConditional(parity.slice(0, parityVerify.index))) continue;
|
||
// Quoted old prose is evidence about history. A quoted status word
|
||
// with a current task/suite subject still cancels its obligation.
|
||
const status = (value: string) => unquoted(value.replace(new RegExp(`((?:${task[1]}|${rerun[1]})(?: (?:verification|baseline verification|rerun))? (?:is|was|has been) |(?:this|the) (?:legacy )?(?:(?:characterization|regression|baseline|unchanged-code) )?(?:suite|requirement|verification) (?:is|was|has been) )["“'](withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer required)["”']`, 'gi'), '$1$2'));
|
||
const canceled = new RegExp(`\\b(?:${task[1]}|${rerun[1]})(?: (?:verification|baseline verification|rerun))? (?:is|was|has been) (?:withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer required)\\b`, 'i');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${task[1]}\\b`, 'i');
|
||
const noRerun = new RegExp(`\\b${rerun[1]} (?:no longer|does not|will not) (?:re)?runs? ${task[1]}\\b`, 'i');
|
||
const inactive = (value: string) => withdrawn(status(value).replace(/\b(?:this|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement'), task[1]) || canceled.test(status(value)) ||
|
||
/\b(?:this|the) (?:legacy )?(?:(?:characterization|regression|baseline|unchanged-code) )?(?:suite|requirement|verification) (?:is|was|has been) (?:superseded|not current)\b/i.test(status(value));
|
||
if (inactive(body) || inactive(baseline) || inactive(parity) || current.some(s => {
|
||
const assessment = status(s.body.join('\n'));
|
||
return /\b(?:the|this) legacy (?:regression|characterization) (?:suite|tests?|requirement) (?:is|was|has been) (?:withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer required)\b/i.test(assessment) ||
|
||
canceled.test(assessment) || noRerun.test(assessment) ||
|
||
assessment.split(/\n|[.!?]\s+/).some(line => changedFirst.test(line.trim()));
|
||
})) continue;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const goldenWithdrawn = current.some(s => {
|
||
const namedSuite = /^(.*?)\b(?:regression|characterization|golden[ -]master)\s+(?:suite|fixtures?|tests?)\b/i.exec(s.title);
|
||
const foreignSuite = Boolean(namedSuite?.[1]?.trim() && !/^(?:legacy(?:AuthFlow(?:\(\))?)?|final|current|updated)[\s:—–-]*$/i.test(namedSuite[1]));
|
||
return unquoted(s.body.join('\n')).split(/\n|[.!?]\s+/).some(statement => {
|
||
const subject = /^(?:Correction:\s*)?(?:the|this|that)\s+(legacy(?:AuthFlow(?:\(\))?)?\s+)?golden[ -]master\s+(?:suite|fixtures?|tests?)\b/i.exec(statement.trim());
|
||
return Boolean(subject && (!foreignSuite || subject[1]) && withdrawn(statement
|
||
.replace(/golden[ -]master\s+(?:suite|fixtures?|tests?)/i, 'regression suite').replace(/\b(?:are|were|have been)\b/i, 'is')));
|
||
});
|
||
});
|
||
for (const section of current.filter(s => /^Tests(?: \([^\n]*\))?$/i.test(s.title))) {
|
||
const body = unquoted(section.body.join('\n')).split(/\n\s*\n/)
|
||
.map(paragraph => paragraph.replace(/\s+/g, ' ').trim()).join('\n\n');
|
||
const claim = /^CRITICAL \(regression rule, mandatory\): legacyAuthFlow(?:\(\))? golden[ -]master\.\s+(?:Capture|Pin|Record) current outputs for [^.!?]{1,300} BEFORE any change, assert identical behavio[u]?r after the rewrite(?: and after [^.!?]{1,120})?\./im.exec(body);
|
||
if (!claim || suiteWithdrawn || goldenWithdrawn || withdrawn(body) ||
|
||
!snapshotSource.includes(claim[0].replace(/\s+/g, ' ')) ||
|
||
goldenSourceOwner(body.slice(0, claim.index)) || conditionalOwner(body.slice(0, claim.index))) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = tasks.body.join('\n').trim();
|
||
const taskPrefix = taskBody.split(/\n(?=-\s)/)[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(taskPrefix) ||
|
||
/\b(?:if|unless|optional|hypothetical|example|source|quoted|unproven)\b/i.test(unquoted(taskPrefix)))) continue;
|
||
for (const task of taskBody.split(/\n(?=-\s)/)) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w-]*(?:\/[A-Za-z][\w-]*)* [—–-] Capture golden[ -]master regression fixtures for legacyAuthFlow(?:\(\))? before any change[\t ]*(?:\n|$)/i.exec(task);
|
||
const verify = /^\s+- Verify: fixtures pass against untouched legacy; rerun after every later task[\t ]*$/m.exec(task);
|
||
const taskIntro = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || !verify || !snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
/\b(?:if|unless|optional|hypothetical|source|quoted|unproven)\b/i.test(match[0]) ||
|
||
conditionalOwner(taskIntro) || goldenSourceOwner(taskIntro) ||
|
||
conditionalOwner(task.slice(0, verify.index)) || goldenSourceOwner(unquoted(task.slice(0, verify.index))) ||
|
||
withdrawn(unquoted(task).replace(/\b(?:this|that|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement'), match[1])) continue;
|
||
const taskWithdrawal = new RegExp(`\\b${match[1]}(?:\\s+rerun)?\\s+(?:is|was|has been)\\s+(?:cancelled|canceled|withdrawn|rejected|deferred|optional|not required|no longer required)\\b`, 'i');
|
||
if (current.some(s => taskWithdrawal.test(unquoted(s.body.join('\n'))))) continue;
|
||
for (const verification of current.filter(s => /^Verification(?: \([^\n]*\))?$/i.test(s.title))) {
|
||
const body = unquoted(verification.body.join('\n')).trim();
|
||
const baseline = new RegExp(`^([1-9]\\d*)\\. Run ${match[1]} fixtures before touching anything; they must pass\\.[\\t ]*$`, 'm').exec(body);
|
||
const rerun = new RegExp(`^([1-9]\\d*)\\. After each task, rerun the full suite plus ${match[1]} fixtures\\.[\\t ]*$`, 'm').exec(body);
|
||
if (baseline && rerun && Number(baseline[1]) < Number(rerun[1]) && baseline.index < rerun.index &&
|
||
!conditionalOwner(body.slice(0, baseline.index)) && !conditionalOwner(body.slice(0, rerun.index)) &&
|
||
!goldenSourceOwner(body.slice(0, baseline.index)) && !goldenSourceOwner(body.slice(0, rerun.index)) &&
|
||
!withdrawn(body.replace(/\b(?:this|that|the)\s+(?:baseline|verification)\b/gi, 'this requirement'), match[1]) &&
|
||
snapshotSource.includes(baseline[0]) && snapshotSource.includes(rerun[0])) return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// A current-output characterization declaration can bind the same file
|
||
// and pre-rewrite task directly, without a separate Verification heading.
|
||
for (const section of current.filter(s => /^REGRESSION \(mandatory, authorized by the coverage-audit regression rule [—–-] no question asked\)$/i.test(s.title))) {
|
||
const body = unquoted(section.body.join('\n')).trim();
|
||
const split = body.indexOf('\n- ');
|
||
if (split < 0 || suiteWithdrawn) continue;
|
||
const intro = body.slice(0, split).replace(/\s+/g, ' ').trim();
|
||
if (!/^legacyAuthFlow\(\) is existing behavior being rewritten\b[^!?]{1,400}\. CRITICAL requirement added to the plan:$/.test(intro) ||
|
||
goldenSourceOwner(intro) || /\b(?:if|unless|maybe|might|could|proposed|optional|hypothetical|unproven)\b/i.test(intro)) continue;
|
||
const claim = body.slice(split).replace(/\s+/g, ' ').trim();
|
||
const declaration = /^- ([A-Za-z][\w/.-]*\.test\.[jt]s) [—–-] record current outputs for: [^.!?]{1,600}\. Assert the new path \(behind the flag\) produces identical decisions and equivalent error surfaces\. These tests are written BEFORE any rewrite \((T[1-9]\d*)\) and stay green through cut-over\.$/.exec(claim);
|
||
if (!declaration || withdrawn(body) || !snapshotSource.includes(claim) ||
|
||
/\b(?:if|unless|maybe|might|could|proposed|optional|hypothetical|unproven)\b/i.test(claim)) continue;
|
||
for (const section of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = section.body.join('\n').trim(), blocks = taskBody.split(/\n(?=-\s)/);
|
||
const prefix = blocks[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(prefix) ||
|
||
/\b(?:if|unless|optional|hypothetical|example|source|quoted|unproven)\b/i.test(unquoted(prefix)))) continue;
|
||
for (const task of blocks) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w/-]* [—–-] Write characterization tests for legacyAuthFlow\(\) before any rewrite[\t ]*(?:\n|$)/.exec(task);
|
||
const file = /^\s+- Files: ([^\n]+)$/m.exec(task);
|
||
const verify = /^\s+- Verify: suite green on current main; re-run after each later task[\t ]*$/m.exec(task);
|
||
const beforeTask = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || match[1] !== declaration[2] || file?.[1] !== declaration[1] || !verify ||
|
||
!snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
conditionalOwner(beforeTask) || goldenSourceOwner(beforeTask) ||
|
||
conditionalOwner(task.slice(0, verify.index)) || goldenSourceOwner(unquoted(task.slice(0, verify.index))) ||
|
||
withdrawn(unquoted(task).replace(/\b(?:this|that|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement'), match[1])) continue;
|
||
const cancelledTask = new RegExp(`\\b${match[1]}(?:\\s+(?:rerun|verification|baseline verification))?\\s+(?:is|was|has been)\\s+(?:cancelled|canceled|withdrawn|rejected|deferred|optional|not required|no longer required)\\b`, 'i');
|
||
const changedBeforeBaseline = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${match[1]}\\b`, 'i');
|
||
const assessment = (body: string) => unquoted(body.replace(new RegExp(`(\\b${match[1]}(?:\\s+(?:rerun|verification|baseline verification))?\\s+(?:is|was|has been)\\s+)["“](withdrawn|rejected|cancelled|canceled)["”]`, 'gi'), '$1$2'));
|
||
if (!current.some(s => cancelledTask.test(assessment(s.body.join('\n'))) ||
|
||
assessment(s.body.join('\n')).split(/\n|[.!?]\s+/).some(statement => changedBeforeBaseline.test(statement.trim())))) return true;
|
||
}
|
||
}
|
||
}
|
||
// The same mandatory characterization can precede a behavior-preserving
|
||
// extraction: its untouched baseline and the extraction's rerun share a task ID.
|
||
const extractionSourceOwner = (body: string) => goldenSourceOwner(body) ||
|
||
/(?:^|\n)\s*(?:(?:quoted )?source(?: (?:excerpt|text|material))?|(?:historical|earlier|previous)(?: review)?(?: assessment)?|(?:hypothetical )?example):\s*(?:\n|$)/i.test(unquoted(body));
|
||
for (const section of current.filter(s => s.title === 'Tests')) {
|
||
const body = unquoted(section.body.join('\n'));
|
||
const claim = /^CRITICAL [—–-] regression rule \(mandatory, not a decision\): (T[1-9]\d*) adds a characterization test for legacyAuthFlow\(\)'s current behavior \([^\n)]{1,300}\) and lands before the ([1-9]\d*[A-D]) extraction\./m.exec(body);
|
||
if (!claim || suiteWithdrawn || withdrawn(body, claim[1]) || !snapshotSource.includes(claim[0]) ||
|
||
extractionSourceOwner(body.slice(0, claim.index)) || conditionalOwner(body.slice(0, claim.index))) continue;
|
||
for (const section of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = section.body.join('\n').trim(), blocks = taskBody.split(/\n(?=-\s)/);
|
||
const prefix = blocks[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(prefix) ||
|
||
extractionSourceOwner(unquoted(prefix)) || conditionalOwner(prefix))) continue;
|
||
const active = (task: string, id: string, verifyAt: number) => {
|
||
const beforeTask = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
return snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) &&
|
||
!conditionalOwner(beforeTask) && !extractionSourceOwner(beforeTask) &&
|
||
!conditionalOwner(task.slice(0, verifyAt)) && !extractionSourceOwner(unquoted(task.slice(0, verifyAt))) &&
|
||
!withdrawn(unquoted(task).replace(/\b(?:this|that|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement'), id);
|
||
};
|
||
for (const baseline of blocks) {
|
||
const task = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w/-]* [—–-] Add characterization\/regression test for legacyAuthFlow\(\) current behavior[\t ]*(?:\n|$)/.exec(baseline);
|
||
const file = /^\s+- Files: ([A-Za-z][\w/.-]*\.test\.[jt]s)$/m.exec(baseline);
|
||
const verify = /^\s+- Verify: test passes against unmodified legacy before any other commit[\t ]*$/m.exec(baseline);
|
||
if (!task || task[1] !== claim[1] || !file || !verify || !active(baseline, task[1], verify.index)) continue;
|
||
for (const extraction of blocks) {
|
||
const task2 = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w/-]* [—–-] Extract IDP checks \+ token validation into shared ([A-Za-z][\w]*)\(\); legacy calls it, behavior unchanged[\t ]*(?:\n|$)/.exec(extraction);
|
||
const origin = /^\s+- Surfaced by: Code quality Issue [1-9]\d* \(D[1-9]\d*, ([1-9]\d*[A-D])\)[\t ]*$/m.exec(extraction);
|
||
const rerun = /^\s+- Verify: (T[1-9]\d*) still green; diff to legacy is call-site only[\t ]*$/m.exec(extraction);
|
||
if (!task2 || task2[1] === task[1] || origin?.[1] !== claim[2] || rerun?.[1] !== task[1] || !active(extraction, task2[1], rerun.index)) continue;
|
||
const canceled = new RegExp(`\\b(?:${task[1]}|${task2[1]})(?:\\s+(?:rerun|verification|baseline verification|regression test|characterization test))?\\s+(?:is|was|has been)\\s+(?:cancelled|canceled|withdrawn|rejected|deferred|optional|not required|no longer required)\\b`, 'i');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${task[1]}\\b`, 'i');
|
||
const assessment = (value: string) => unquoted(value.replace(new RegExp(`(\\b(?:${task[1]}|${task2[1]})(?:\\s+(?:rerun|verification|baseline verification|regression test|characterization test))?\\s+(?:is|was|has been)\\s+)["“](withdrawn|rejected|cancelled|canceled)["”]`, 'gi'), '$1$2'));
|
||
const rerunWithdrawn = new RegExp(`\\b${task2[1]} (?:no longer|does not|will not) reruns? ${task[1]}\\b`, 'i');
|
||
if (!current.some(s => canceled.test(assessment(s.body.join('\n'))) || rerunWithdrawn.test(assessment(s.body.join('\n'))) ||
|
||
assessment(s.body.join('\n')).split(/\n|[.!?]\s+/).some(line => changedFirst.test(line.trim())))) return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// A golden requirement can name its parity oracle in a test-list item,
|
||
// with the same task capturing current outputs on untouched legacy code.
|
||
for (const section of current.filter(s => s.title === 'Test requirements')) {
|
||
const requirements = section.body.join('\n').trim();
|
||
for (const block of requirements.split(/\n(?=-\s)/)) {
|
||
const claim = unquoted(block).replace(/\s+/g, ' ').trim();
|
||
const rule = /^- CRITICAL [—–-] ([A-Za-z][\w/.-]*\/legacyAuthFlow\.regression\.test\.[jt]s) \((T[1-9]\d*), REGRESSION RULE, no approval needed\): golden tests for ([^.!?]{1,300})\./.exec(claim);
|
||
if (!rule || suiteWithdrawn || goldenWithdrawn || !snapshotSource.includes(claim) ||
|
||
!/(?:^|\. )These tests are the parity oracle for the D[1-9]\d* flag-off path\./.test(claim) ||
|
||
extractionSourceOwner(block) || conditionalOwner(block) ||
|
||
extractionSourceOwner(requirements.slice(0, requirements.indexOf(block))) ||
|
||
conditionalOwner(requirements.slice(0, requirements.indexOf(block)))) continue;
|
||
const id = rule[2]!;
|
||
const assessment = (value: string) => value.replace(/"[^"\n]*"|“[^”\n]*”/g,
|
||
(quoted: string, index: number, source: string) =>
|
||
/^(?:withdrawn|rejected|cancelled|canceled|optional|not current|no longer required)$/i.test(quoted.slice(1, -1)) &&
|
||
new RegExp(`(?:^|[.!?]\\s+|\\n)[\\t ]*(?:Correction:\\s*)?(?:${id}(?: (?:verification|baseline verification|regression tests?))? (?:is|was|has been)|(?:this|the) (?:(?:unchanged-code|baseline) )?verification (?:is|was|has been)|(?:the|this) legacy golden (?:tests|suite) (?:are|is|were|was|have been|has been)) $`, 'i').test(source.slice(0, index))
|
||
? quoted.slice(1, -1) : '');
|
||
const inactive = (value: string) => {
|
||
const body = assessment(value).replace(/\b(?:these|the|this)\s+tests\s+(?:are|were|have been)\b/gi, 'this suite is')
|
||
.replace(/\b(?:these|the|this)\s+tests\b/gi, 'this suite')
|
||
.replace(/\b(?:this|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement');
|
||
return withdrawn(body, id) || /\b(?:this|the|that) (?:requirement|suite|test) (?:is|was|has been) (?:not current|no longer current)\b/i.test(body) || new RegExp(`\\b${id}(?: (?:verification|baseline verification|regression tests?))? (?:is|was|has been) (?:withdrawn|rejected|cancelled|canceled|optional|not current|no longer required)\\b`, 'i').test(body);
|
||
};
|
||
if (inactive(block)) continue;
|
||
const cancelled = new RegExp(`\\b${id}(?: (?:verification|baseline verification|regression tests?))? (?:is|was|has been) (?:withdrawn|rejected|cancelled|canceled|optional|not current|no longer required)\\b`, 'i');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow(?:\\(\\))? (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${id}\\b`, 'i');
|
||
if (current.some(s => {
|
||
const body = assessment(s.body.join('\n'));
|
||
return cancelled.test(body) || /\b(?:the|this) legacy golden (?:tests|suite) (?:are|is|were|was|have been|has been) (?:withdrawn|rejected|cancelled|canceled|optional|not current|no longer required)\b/i.test(body) ||
|
||
body.split(/\n|[.!?]\s+/).some(line => changedFirst.test(line.trim()));
|
||
})) continue;
|
||
const ordering = current.some(s => {
|
||
if (s.title !== 'Implementation steps') return false;
|
||
const body = unquoted(s.body.join('\n'));
|
||
const step = new RegExp(`^[1-9]\\d*\\. Golden regression tests for legacyAuthFlow \\(${id}\\) [—–-] pin current outputs\\s+per input class before any other code moves\\. CRITICAL, lands first\\.`, 'm').exec(body);
|
||
return Boolean(step && !extractionSourceOwner(body.slice(0, step.index)) && !conditionalOwner(body.slice(0, step.index)) &&
|
||
!inactive(body) && snapshotSource.includes(step[0].replace(/\s+/g, ' ')));
|
||
});
|
||
if (!ordering) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const body = tasks.body.join('\n').trim();
|
||
for (const task of body.split(/\n(?=-\s)/)) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] ([A-Za-z][\w/.-]*\/legacyAuthFlow) tests [—–-] CRITICAL golden regression tests, land first[\t ]*(?:\n|$)/.exec(task);
|
||
const file = /^\s+- Files: ([^\n]+)$/m.exec(task);
|
||
const verify = /^\s+- Verify: (one|two|three|four|five|six|seven|eight|nine|ten|[1-9]\d*) input classes pinned; suite green against unmodified legacy code before any refactor commit[\t ]*$/m.exec(task);
|
||
const prefix = unquoted(body.slice(0, body.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || match[1] !== id || file?.[1] !== rule[1] || !verify ||
|
||
!rule[1].startsWith(match[2] + '.regression.test.') ||
|
||
!snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
extractionSourceOwner(prefix) || conditionalOwner(prefix) ||
|
||
extractionSourceOwner(unquoted(task.slice(0, verify.index))) || conditionalOwner(task.slice(0, verify.index)) || inactive(task)) continue;
|
||
const count = /^\d+$/.test(verify[1]!) ? Number(verify[1]) : ['zero','one','two','three','four','five','six','seven','eight','nine','ten'].indexOf(verify[1]!);
|
||
if (count === rule[3]!.split(',').length) return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
// A mandatory before-rewrite declaration binds exact captured behavior to
|
||
// the same task file's current-code baseline and flag-on rerun.
|
||
const approvalPending = (value: string) => unquoted(value).split('\n').some(line =>
|
||
/^\s*(?:once|when|pending)\b[^.!?\n]{0,80}\bapprov(?:e[ds]?|al)\b/i.test(line));
|
||
// A required fixture file can own the legacy oracle while its read-only
|
||
// baseline task gates the scheduled task that changes the legacy module.
|
||
for (const section of current.filter(s => /^CRITICAL: regression test for legacyAuthFlow\(\) \(regression rule, mandatory\)$/i.test(s.title))) {
|
||
const body = unquoted(section.body.join(' ')).replace(/\s+/g, ' ').trim();
|
||
const rule = /(?:^|\. )Before any rewrite: - ([A-Za-z][\w/.-]*\.test(?:\.[jt]s)?) records, for a fixture set of tenants and tokens, the exact claims returned and the exact error for each failure case \(([^()!?]{1,300})\)\. - The same fixture set is the shadow comparator's assertion set and stays as the permanent behavioral spec after legacy is deleted\./.exec(body);
|
||
if (!rule || suiteWithdrawn || !snapshotSource.includes(rule[0].trim()) || withdrawn(body) ||
|
||
extractionSourceOwner(body) || conditionalOwner(body) || approvalPending(body) ||
|
||
!['expired', 'wrong audience', 'wrong issuer', 'suspended tenant', 'revoked token', 'malformed token'].every(kind => rule[2]!.split(',').map(item => item.trim()).includes(kind))) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = tasks.body.join('\n').trim(), blocks = taskBody.split(/\n(?=-\s)/), prefix = blocks[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(prefix) ||
|
||
extractionSourceOwner(prefix) || conditionalOwner(prefix) || approvalPending(prefix) || /(?:^|\n)\s*(?:assuming|provided)\b/i.test(unquoted(prefix)))) continue;
|
||
const ids = blocks.map(block => /^- (?:\[[ xX]\] )?(T[1-9]\d*)\b/.exec(block)?.[1]).filter(Boolean);
|
||
if (ids.length !== new Set(ids).size) continue;
|
||
for (const task of blocks) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w/-]* [—–-] CRITICAL regression test capturing legacyAuthFlow\(\) behavior before any rewrite[\t ]*(?:\n|$)/.exec(task);
|
||
const files = [...task.matchAll(/^\s+- Files: ([^\n]+)$/gm)];
|
||
const verifies = [...task.matchAll(/^\s+- Verify: test passes against unmodified legacy; same fixtures drive shadow compare[\t ]*$/gm)];
|
||
const verify = verifies[0], preceding = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || files.length !== 1 || files[0]![1] !== rule[1] || verifies.length !== 1 ||
|
||
(task.match(/^\s+- Verify:/gm)?.length ?? 0) !== 1 || !snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
extractionSourceOwner(preceding) || conditionalOwner(preceding) || approvalPending(preceding) ||
|
||
extractionSourceOwner(task.slice(0, verify!.index)) || conditionalOwner(task.slice(0, verify!.index)) || approvalPending(task.slice(0, verify!.index)) ||
|
||
/(?:^|\n)\s*(?:assuming|provided)\b/i.test(unquoted(task.slice(0, verify!.index)))) continue;
|
||
const id = match[1]!, status = '(?:withdrawn|rejected|declined|cancelled|canceled|superseded|deferred|optional|not current|no longer current|not required|no longer required)';
|
||
const assessment = (value: string) => unquoted(value.replace(new RegExp(
|
||
`((?:^|[.!?;]\\s+|\\n)[\\t ]*(?:Correction:\\s*)?(?:${id}(?: (?:baseline requirement|verification|baseline verification))?|(?:this|the) (?:(?:legacy|baseline|unchanged-code) )?(?:(?:regression|characterization) )?(?:suite|requirement|verification)) (?:is|was|has been) )["“'‘](${status})["”'’]`, 'gim'), '$1$2'))
|
||
.replace(/(?<![A-Za-z0-9])'[^'\n]*'(?![A-Za-z0-9])|‘[^’\n]*’/g, '')
|
||
.split(/\n|[.!?]\s+/).filter(line => !conditionalOwner(line) && !approvalPending(line) && !/^\s*(?:assuming|provided)\b/i.test(line)).join('\n');
|
||
const cancelled = new RegExp(`\\b${id}(?: (?:baseline requirement|verification|baseline verification))? (?:is|was|has been) ${status}\\b`, 'i');
|
||
const inactive = (value: string) => {
|
||
const owned = assessment(value).replace(/\b(?:this|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement');
|
||
return withdrawn(owned, id) || cancelled.test(owned) || new RegExp(`\\b(?:this|the) (?:suite|requirement) (?:is|was|has been) ${status}\\b`, 'i').test(owned);
|
||
};
|
||
const statusRow = new RegExp(`^\\s*\\|\\s*${id}\\s*\\|\\s*["“'‘]?${status}["”'’]?\\s*\\|`, 'im');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${id}\\b`, 'im');
|
||
if (inactive(body) || inactive(task) || current.some(s => {
|
||
const value = assessment(s.body.join('\n'));
|
||
return statusRow.test(s.body.join('\n')) || cancelled.test(value) || changedFirst.test(value) ||
|
||
new RegExp(`\\b(?:the|this) legacy (?:regression|characterization) (?:suite|tests?|requirement) (?:is|was|has been) ${status}\\b`, 'i').test(value);
|
||
})) continue;
|
||
for (const strategy of current.filter(s => s.title === 'Worktree parallelization strategy')) {
|
||
const schedule = unquoted(strategy.body.join('\n'));
|
||
const rows = [...schedule.matchAll(/^\| (T[1-9]\d*) ([^|]+) \| ([^|]+) \| ([^|]+) \|$/gm)];
|
||
if (rows.length !== new Set(rows.map(row => row[1])).size || extractionSourceOwner(schedule) || approvalPending(schedule) || inactive(schedule) ||
|
||
schedule.split('\n').some(line => conditionalOwner(line) || /^\s*(?:assuming|provided)\b/i.test(line))) continue;
|
||
const baseline = rows.find(row => row[1] === id && row[2] === 'legacy regression test' && row[4] === '—');
|
||
const modules = baseline && /^([A-Za-z][\w/-]*) \(read\), ([A-Za-z][\w/-]*)$/.exec(baseline[3]!);
|
||
const writers = modules ? rows.filter(row => row[1] !== id && row[3]!.split(',').map(item => item.trim()).includes(modules[1]!)) : [];
|
||
if (modules && rule[1]!.startsWith(modules[2] + '/') && writers.length > 0 &&
|
||
writers.every(row => row[4]!.split(',').map(item => item.trim()).includes(id)) &&
|
||
snapshotSource.includes(baseline![0].replace(/\s+/g, ' ').trim())) return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
for (const section of current.filter(s => s.title === 'REGRESSION RULE (mandatory, no decision required)')) {
|
||
const body = unquoted(section.body.join(' ')).replace(/\s+/g, ' ').trim();
|
||
const rule = /^legacyAuthFlow\(\) is existing behavior being rewritten\b[^!?]{1,240}\. CRITICAL: before any rewrite, record a characterization suite in ([A-Za-z][\w/.-]*\.test\.[jt]s): for each supported tenant configuration, capture inputs \([^()!?]{1,300}\) and the exact output \([^()!?]{1,300}\)\. The new path must pass the same suite with the flag on\. This is the parity gate for D[1-9]\d*\.$/.exec(body);
|
||
if (!rule || suiteWithdrawn || !snapshotSource.includes(body) || withdrawn(body) ||
|
||
/\b(?:if|unless|maybe|might|could|optional|hypothetical|unproven)\b/i.test(body)) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = tasks.body.join('\n').trim(), blocks = taskBody.split(/\n(?=-\s)/);
|
||
const prefix = blocks[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(prefix) ||
|
||
extractionSourceOwner(prefix) || approvalPending(prefix) || /\b(?:if|unless|assuming|provided|optional|hypothetical|unproven)\b/i.test(unquoted(prefix)))) continue;
|
||
const ids = blocks.map(block => /^- (?:\[[ xX]\] )?(T[1-9]\d*)\b/.exec(block)?.[1]).filter(Boolean);
|
||
if (ids.length !== new Set(ids).size) continue;
|
||
for (const task of blocks) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w/-]* [—–-] Write the legacyAuthFlow\(\) characterization \(regression\) suite before any rewrite[\t ]*(?:\n|$)/.exec(task);
|
||
const file = /^\s+- Files: ([^\n]+)$/m.exec(task);
|
||
const verify = /^\s+- Verify: suite green on current code; green again with flag on after rewrite[\t ]*$/m.exec(task);
|
||
const preceding = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || file?.[1] !== rule[1] || !verify || !snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
extractionSourceOwner(preceding) || conditionalOwner(preceding) || approvalPending(preceding) ||
|
||
extractionSourceOwner(task.slice(0, verify.index)) || conditionalOwner(task.slice(0, verify.index)) || approvalPending(task.slice(0, verify.index)) ||
|
||
/(?:^|\n)\s*(?:assuming|provided)\b/i.test(unquoted(task.slice(0, verify.index)))) continue;
|
||
const id = match[1]!;
|
||
// Preserve a quoted status word on a current subject, while still
|
||
// ignoring quoted historical sentences and foreign suite withdrawals.
|
||
const assessment = (value: string) => unquoted(value.replace(new RegExp(
|
||
`(\\b(?:${id}(?: (?:verification|baseline verification|rerun))?|(?:this|the) (?:(?:legacy|baseline|unchanged-code) )?(?:(?:regression|characterization) )?(?:suite|requirement|verification)) (?:is|was|has been) )["“](withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer required)["”]`, 'gi'), '$1$2'));
|
||
const inactive = (value: string) => {
|
||
const body = assessment(value).replace(/\b(?:this|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement');
|
||
return withdrawn(body, id) || /\b(?:this|the) (?:suite|requirement) (?:is|was|has been) (?:superseded|not current|no longer current)\b/i.test(body);
|
||
};
|
||
const cancelled = new RegExp(`\\b${id}(?: (?:verification|baseline verification|rerun))? (?:is|was|has been) (?:withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer required)\\b`, 'i');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${id}\\b`, 'i');
|
||
const statusRow = new RegExp(`^\\s*\\|\\s*${id}\\s*\\|\\s*["“]?(?:withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer required)["”]?\\s*\\|`, 'im');
|
||
if (inactive(task) || current.some(s => {
|
||
const value = assessment(s.body.join('\n'));
|
||
return statusRow.test(s.body.join('\n')) || cancelled.test(value) || /\b(?:the|this) legacy (?:regression|characterization) (?:suite|tests?|requirement) (?:is|was|has been) (?:withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer required)\b/i.test(value) ||
|
||
value.split(/\n|[.!?]\s+/).some(line => changedFirst.test(line.trim()));
|
||
})) continue;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// A mandatory current-output baseline can land before all other tasks.
|
||
// This obligation does not imply an identical suite on the flag-on path:
|
||
// a plan may specify its rollout parity check separately.
|
||
for (const section of current.filter(s => /^CRITICAL [—–-] regression \(mandatory, REGRESSION RULE\)$/i.test(s.title))) {
|
||
const body = unquoted(section.body.join(' ')).replace(/\s+/g, ' ').trim();
|
||
const rule = /^legacyAuthFlow\(\) is (?:live|existing|current) behavior being (?:changed|modified|refactored) with no covering test(?: \([^()!?]{1,120}\))?\. Before any (?:rewrite|refactor|change): ([A-Za-z][\w/.-]*\.test\.[jt]s) (?:records|captures|pins) (?:current|existing) outputs \(including quirks\) for [^.!?]{1,300} inputs\. This suite runs against the legacy path now\b/.exec(body);
|
||
if (!rule || suiteWithdrawn || !snapshotSource.includes(rule[0]) || withdrawn(body) ||
|
||
extractionSourceOwner(body) || approvalPending(body) || /\b(?:if|unless|maybe|might|could|optional|hypothetical|unproven)\b/i.test(body)) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = tasks.body.join('\n').trim(), blocks = taskBody.split(/\n(?=-\s)/);
|
||
const prefix = blocks[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(prefix) ||
|
||
extractionSourceOwner(prefix) || approvalPending(prefix) || /\b(?:if|unless|assuming|provided|optional|hypothetical|unproven)\b/i.test(unquoted(prefix)))) continue;
|
||
const ids = blocks.map(block => /^- (?:\[[ xX]\] )?(T[1-9]\d*)\b/.exec(block)?.[1]).filter(Boolean);
|
||
if (ids.length !== new Set(ids).size) continue;
|
||
for (const task of blocks) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w/-]* [—–-] CRITICAL regression: characterization suite for legacyAuthFlow\(\) (?:current|existing|prior) behavior[\t ]*(?:\n|$)/.exec(task);
|
||
const files = [...task.matchAll(/^\s+- Files: ([^\n]+)$/gm)];
|
||
const verifies = [...task.matchAll(/^\s+- Verify: ([^\n]+)$/gm)];
|
||
const verify = verifies[0];
|
||
const preceding = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || files.length !== 1 || files[0]![1] !== rule[1] || verifies.length !== 1 ||
|
||
!/^suite green against unmodified legacy before any other task merges[\t ]*$/.test(verify![1]!) ||
|
||
!snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
extractionSourceOwner(preceding) || conditionalOwner(preceding) || approvalPending(preceding) ||
|
||
extractionSourceOwner(task.slice(0, verify!.index)) || conditionalOwner(task.slice(0, verify!.index)) || approvalPending(task.slice(0, verify!.index)) ||
|
||
/(?:^|\n)\s*(?:assuming|provided)\b/i.test(unquoted(task.slice(0, verify!.index)))) continue;
|
||
const id = match[1]!;
|
||
const status = '(?:withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer current|not required|no longer required)';
|
||
// Current scalar statuses retain their owner; whole-sentence quoted
|
||
// history and conditional future statuses cannot cancel this baseline.
|
||
const assessment = (value: string) => unquoted(value.replace(new RegExp(
|
||
`((?:^|[.!?]\\s+|\\n)[\\t ]*(?:Correction:\\s*)?(?:${id}(?: (?:verification|baseline verification))?|(?:this|the) (?:(?:legacy|baseline|unchanged-code) )?(?:(?:regression|characterization) )?(?:suite|requirement|verification)) (?:is|was|has been) )["“'‘](${status})["”'’]`, 'gim'), '$1$2'))
|
||
.split(/\n|[.!?]\s+/).filter(line => !conditionalOwner(line) && !approvalPending(line) && !/^\s*(?:assuming|provided)\b/i.test(line)).join('\n');
|
||
const inactive = (value: string) => {
|
||
const body = assessment(value).replace(/\b(?:this|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement');
|
||
return withdrawn(body, id) || new RegExp(`\\b(?:this|the) (?:suite|requirement) (?:is|was|has been) ${status}\\b`, 'i').test(body);
|
||
};
|
||
const cancelled = new RegExp(`\\b${id}(?: (?:verification|baseline verification))? (?:is|was|has been) ${status}\\b`, 'i');
|
||
const statusRow = new RegExp(`^\\s*\\|\\s*${id}\\s*\\|\\s*["“'‘]?${status}["”'’]?\\s*\\|`, 'im');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${id}\\b`, 'im');
|
||
if (inactive(body) || inactive(task) || current.some(s => {
|
||
const value = assessment(s.body.join('\n'));
|
||
return statusRow.test(s.body.join('\n')) || cancelled.test(value) || changedFirst.test(value) ||
|
||
new RegExp(`\\b(?:the|this) legacy (?:regression|characterization) (?:suite|tests?|requirement) (?:is|was|has been) ${status}\\b`, 'i').test(value);
|
||
})) continue;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// A directory-owned characterization task can name changed worktree steps
|
||
// in its baseline check, with the baseline's own lane merging first.
|
||
for (const section of current.filter(s => /^Tests(?: \([^\n)]+\))?$/.test(s.title))) {
|
||
const body = unquoted(section.body.join(' ')).replace(/\s+/g, ' ').trim();
|
||
const rule = /^CRITICAL regression suite \(mandatory, IRON RULE\)\. legacyAuthFlow\(\) is existing behavior being rewritten and the original plan had no regression coverage\. Before any rewrite, write a characterization suite that pins current behavior: [^.!?]{1,300}\. The suite runs against both the legacy path and the new flow \(via the flag\) for the whole rollout window\./.exec(body);
|
||
if (!rule || suiteWithdrawn || !snapshotSource.includes(rule[0]) || withdrawn(body) ||
|
||
/\b(?:if|unless|maybe|might|could|optional|hypothetical|unproven)\b/i.test(rule[0])) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = tasks.body.join('\n').trim(), blocks = taskBody.split(/\n(?=-\s)/), prefix = blocks[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(prefix) || extractionSourceOwner(prefix) ||
|
||
approvalPending(prefix) || /\b(?:if|unless|assuming|provided|optional|hypothetical|unproven)\b/i.test(unquoted(prefix)))) continue;
|
||
const ids = blocks.map(block => /^- (?:\[[ xX]\] )?(T[1-9]\d*)\b/.exec(block)?.[1]).filter(Boolean);
|
||
if (ids.length !== new Set(ids).size) continue;
|
||
for (const task of blocks) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] ([A-Za-z][\w/-]*) [—–-] Write the CRITICAL characterization suite for legacyAuthFlow\(\) before any rewrite; run it against legacy and new flow[\t ]*(?:\n|$)/.exec(task);
|
||
const files = [...task.matchAll(/^\s+- Files: ([^\n]+)$/gm)];
|
||
const verifies = [...task.matchAll(/^\s+- Verify: suite green on legacy path before (S[1-9]\d*)\/(S[1-9]\d*) land; green on both paths after[\t ]*$/gm)];
|
||
const verify = verifies[0], preceding = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || files.length !== 1 || files[0]![1] !== `${match[2]}/, router flag stub` || verifies.length !== 1 ||
|
||
(task.match(/^\s+- Verify:/gm)?.length ?? 0) !== 1 || !snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
extractionSourceOwner(preceding) || conditionalOwner(preceding) || approvalPending(preceding) ||
|
||
extractionSourceOwner(task.slice(0, verify!.index)) || conditionalOwner(task.slice(0, verify!.index)) || approvalPending(task.slice(0, verify!.index)) ||
|
||
/(?:^|\n)\s*(?:assuming|provided)\b/i.test(unquoted(task.slice(0, verify!.index)))) continue;
|
||
for (const strategy of current.filter(s => s.title === 'Worktree parallelization strategy')) {
|
||
const schedule = unquoted(strategy.body.join('\n'));
|
||
const steps = [...schedule.matchAll(/^\| (S[1-9]\d*)\b/gm)].map(row => row[1]);
|
||
if (steps.length !== new Set(steps).size) continue;
|
||
const baseline = /^\| (S[1-9]\d*) Regression suite for legacyAuthFlow\(\) \| ([A-Za-z][\w/-]*) \| [—–-] \|$/m.exec(schedule);
|
||
const lanes = baseline ? [...schedule.matchAll(new RegExp(`^\\s*Lane ([A-Z]): ${baseline[1]} \\(independent\\)$`, 'gm'))] : [];
|
||
const lane = lanes.length === 1 ? lanes[0] : undefined;
|
||
const order = lane && new RegExp(`^Execution order: launch [A-Z](?:, [A-Z])+ in parallel worktrees\\. Merge ${lane[1]} first \\(it is\\s+pure tests and gates the rewrite\\)\\.`, 'm').exec(schedule);
|
||
if (!baseline || baseline[2] !== match[2] || !lane || !order || verify![1] === verify![2] ||
|
||
!new RegExp(`^\\| ${verify![1]} AuthBroker \\+ SessionMint \\| [^|]+ \\| [^|]+ \\|$`, 'm').test(schedule) ||
|
||
!new RegExp(`^\\| ${verify![2]} Flattened dispatcher \\+ flag router \\+ fallback \\| [^|]+ \\| [^|]+ \\|$`, 'm').test(schedule) ||
|
||
extractionSourceOwner(schedule.slice(0, order.index)) || conditionalOwner(schedule.slice(0, order.index)) || approvalPending(schedule.slice(0, order.index)) ||
|
||
!snapshotSource.includes(order[0].replace(/\s+/g, ' '))) continue;
|
||
const id = `(?:${match[1]}|${baseline[1]})`, status = '(?:withdrawn|rejected|cancelled|canceled|superseded|optional|not current|no longer current|not required|no longer required)';
|
||
const assessment = (value: string) => unquoted(value.replace(new RegExp(
|
||
`((?:^|[.!?]\\s+|\\n)[\\t ]*(?:Correction:\\s*)?(?:${id}(?: (?:verification|baseline verification))?|(?:this|the) (?:(?:legacy|baseline|unchanged-code) )?(?:(?:regression|characterization) )?(?:suite|requirement|verification)) (?:is|was|has been) )["“'‘](${status})["”'’]`, 'gim'), '$1$2'))
|
||
.replace(/(?<![A-Za-z0-9])'[^'\n]*'(?![A-Za-z0-9])|‘[^’\n]*’/g, '')
|
||
.split(/\n|[.!?]\s+/).filter(line => !conditionalOwner(line) && !approvalPending(line) && !/^\s*(?:assuming|provided)\b/i.test(line)).join('\n');
|
||
const inactive = (value: string) => {
|
||
const text = assessment(value).replace(/\b(?:this|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement');
|
||
return withdrawn(text, id) || new RegExp(`\\b(?:this|the) (?:suite|requirement) (?:is|was|has been) ${status}\\b`, 'i').test(text);
|
||
};
|
||
const cancelled = new RegExp(`\\b${id}(?: (?:verification|baseline verification))? (?:is|was|has been) ${status}\\b`, 'i');
|
||
const statusRow = new RegExp(`^\\s*\\|\\s*${id}\\s*\\|\\s*["“'‘]?${status}["”'’]?\\s*\\|`, 'im');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${id}\\b`, 'im');
|
||
if (inactive(body) || inactive(task) || current.some(s => {
|
||
const value = assessment(s.body.join('\n'));
|
||
return statusRow.test(s.body.join('\n')) || cancelled.test(value) || changedFirst.test(value) ||
|
||
new RegExp(`\\b(?:the|this) (?:legacy (?:regression|characterization) (?:suite|tests?|requirement)|(?:baseline|unchanged-code) verification) (?:is|was|has been) ${status}\\b`, 'i').test(value);
|
||
})) continue;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// A blocking declaration can bind its task to the first ordered step:
|
||
// characterize both existing entry points before any implementation changes.
|
||
for (const section of current.filter(s => /^REGRESSION \(CRITICAL, mandatory under the regression rule, no question asked\)$/i.test(s.title))) {
|
||
const body = unquoted(section.body.join(' ')).replace(/\s+/g, ' ').trim();
|
||
const rule = /(?:^|\. )(T[1-9]\d*) is a blocking requirement: before any rewrite, (?:capture|record|pin) the current behavior of legacyAuthFlow\(\) and validateAndDispatch\(\) as a characterization suite: every accepted token shape, every rejected token shape, every error response, for at least (?:two|[2-9]\d*) tenants\. The same suite runs against the AuthBroker path behind the flag and must produce identical outcomes\b/.exec(body);
|
||
if (!rule || suiteWithdrawn || !snapshotSource.includes(rule[0].trim()) ||
|
||
extractionSourceOwner(body) || conditionalOwner(body) || approvalPending(body)) continue;
|
||
const id = rule[1]!;
|
||
const status = '(?:withdrawn|rejected|declined|cancelled|canceled|superseded|deferred|optional|not current|no longer current|not required|no longer required)';
|
||
const taskSubject = `${id}(?: (?:baseline requirement|verification|baseline verification|regression tests?))?`;
|
||
const assessment = (value: string) => unquoted(value.replace(new RegExp(
|
||
`((?:^|[.!?]\\s+|\\n)[\\t ]*(?:Correction:\\s*)?(?:${taskSubject}|(?:this|the) (?:(?:legacy|baseline|unchanged-code) )?(?:(?:regression|characterization) )?(?:suite|requirement|verification)) (?:is|was|has been) )["“'‘](${status})["”'’]`, 'gim'), '$1$2'))
|
||
.split(/\n|[.!?]\s+/).filter(line => !conditionalOwner(line) && !approvalPending(line) && !/^\s*(?:assuming|provided)\b/i.test(line)).join('\n');
|
||
const cancelled = new RegExp(`\\b${taskSubject} (?:is|was|has been) ${status}\\b`, 'i');
|
||
const inactive = (value: string) => {
|
||
const owned = assessment(value).replace(/\b(?:this|the)\s+(?:(?:unchanged-code|baseline)\s+)?verification\b/gi, 'this requirement');
|
||
return withdrawn(owned, id) || cancelled.test(owned) ||
|
||
new RegExp(`\\b(?:this|the) (?:suite|requirement) (?:is|was|has been) ${status}\\b`, 'i').test(owned);
|
||
};
|
||
const statusRow = new RegExp(`^\\s*\\|\\s*${id}\\s*\\|\\s*["“'‘]?${status}["”'’]?\\s*\\|`, 'im');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${id}\\b`, 'im');
|
||
if (inactive(body) || current.some(s => {
|
||
const value = assessment(s.body.join('\n'));
|
||
return statusRow.test(s.body.join('\n')) || cancelled.test(value) || changedFirst.test(value) ||
|
||
new RegExp(`\\b(?:the|this) legacy (?:regression|characterization) (?:suite|tests?|requirement) (?:is|was|has been) ${status}\\b`, 'i').test(value);
|
||
})) continue;
|
||
const baseline = current.some(s => {
|
||
if (s.title !== 'Implementation steps (ordered)') return false;
|
||
const schedule = unquoted(s.body.join('\n')).trim();
|
||
const first = new RegExp(`^1\\. ${id} Characterization suite for legacyAuthFlow\\(\\) and validateAndDispatch\\(\\)\\. Green on current code before anything else changes\\.[\\t ]*(?:\\n|$)`).exec(schedule);
|
||
return Boolean(first && !inactive(schedule) && snapshotSource.includes(first[0].replace(/\s+/g, ' ').trim()));
|
||
});
|
||
if (!baseline) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = tasks.body.join('\n').trim(), blocks = taskBody.split(/\n(?=-\s)/), prefix = blocks[0]?.trim() ?? '';
|
||
if (!taskBody.startsWith('- ') && (!/^Synthesized from (?:this|the) review's findings\./.test(prefix) ||
|
||
extractionSourceOwner(prefix) || conditionalOwner(prefix) || approvalPending(prefix) ||
|
||
/(?:^|\n)\s*(?:assuming|provided)\b/i.test(unquoted(prefix)))) continue;
|
||
const ids = blocks.map(block => /^- (?:\[[ xX]\] )?(T[1-9]\d*)\b/.exec(block)?.[1]).filter(Boolean);
|
||
if (ids.length !== new Set(ids).size) continue;
|
||
for (const task of blocks) {
|
||
const match = /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] [A-Za-z][\w/-]* [—–-] Write the characterization\/regression suite for legacyAuthFlow\(\) and validateAndDispatch\(\) before any rewrite \(CRITICAL\)[\t ]*(?:\n|$)/.exec(task);
|
||
const files = [...task.matchAll(/^\s+- Files: [^,\n]+, (?:tests?|__tests__)\/[A-Za-z][\w/.-]*[\t ]*$/gm)];
|
||
const verifies = [...task.matchAll(/^\s+- Verify: suite green on current code; later green on both flag states[\t ]*$/gm)];
|
||
const verify = verifies[0], preceding = unquoted(taskBody.slice(0, taskBody.indexOf(task))).trim().split('\n').at(-1) ?? '';
|
||
if (!match || match[1] !== id || files.length !== 1 || verifies.length !== 1 ||
|
||
(task.match(/^\s+- Verify:/gm)?.length ?? 0) !== 1 || !snapshotSource.includes(task.replace(/\s+/g, ' ').trim()) ||
|
||
extractionSourceOwner(preceding) || conditionalOwner(preceding) || approvalPending(preceding) ||
|
||
extractionSourceOwner(task.slice(0, verify!.index)) || conditionalOwner(task.slice(0, verify!.index)) || approvalPending(task.slice(0, verify!.index)) ||
|
||
/(?:^|\n)\s*(?:assuming|provided)\b/i.test(unquoted(task.slice(0, verify!.index))) || inactive(task)) continue;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// A required-test item may own the named legacy oracle while a separate
|
||
// parity item and task preserve its compatibility obligation. Bind these
|
||
// small obligations structurally; unrelated prose cannot complete the chain.
|
||
for (const section of current.filter(s => /^Required tests(?: \([^\n]*\))?$/i.test(s.title))) {
|
||
const blocks = section.body.join('\n').trim().split(/\n\s*\n/);
|
||
for (const block of blocks) {
|
||
const claim = block.replace(/\s+/g, ' ').trim();
|
||
const rule = /^CRITICAL\s*\([^)]*\bmandatory\b[^)]*\):\s*([A-Za-z][\w/.-]*\.test\.[jt]s)\./i.exec(claim);
|
||
if (!rule || /\b(?:not|never|no longer)\s+mandatory\b/i.test(rule[0]) || !/^\s*(?:Pin|Capture|Record) current behavior of legacyAuthFlow\(\) before any (?:change|rewrite|refactor):/i.test(claim.slice(rule[0].length))
|
||
|| !/\bThis is the oracle for the parity suite\b/i.test(claim) || !snapshotSource.includes(claim)) continue;
|
||
const quoteFree = (value: string) => value.replace(/"[^"\n]*"|“[^”\n]*”|(?<![\w])'[^'\n]*'(?![\w])|‘[^’\n]*’/g, '');
|
||
const inactiveWords = '(?:withdrawn|rejected|declined|cancelled|canceled|superseded|deferred|optional|proposed|not current|no longer current|not required|no longer required|hypothetical|unproven)';
|
||
const subject = '(?:(?:this|the) (?:(?:legacy|baseline|unchanged-code) )?(?:(?:regression|characterization|parity) )?(?:suite|tests?|requirement|verification|oracle))';
|
||
const currentText = (value: string, ids: string) => quoteFree(value.replace(new RegExp(
|
||
`((?:^|[.!?;]\\s+|\\n)[\\t ]*(?:Correction:\\s*)?(?:${ids}|${subject}) (?:is|are|was|were|has been|have been) )["“'‘](${inactiveWords})["”'’]`, 'gim'), '$1$2'));
|
||
// Input conditions describe asserted behavior, including accepted tokens.
|
||
// An implicit approval or an explicit work/approver subject instead
|
||
// governs whether this work exists.
|
||
const conditionalApproval = /(?:^|[.!?;]\s+|\n)[\t ]*(?:if|unless|assuming|provided|once|when|pending|after)\s+(?:(?:approv\w*|authoriz\w*|consent|confirm\w*|accept\w*|desired|requested|needed)\b|(?:(?:this|the|our) )?(?:work|plan|review|task|baseline|parity|regression|characterization|suite|tests?|requirement|verification|proposal|implementation|T[1-9]\d*|we|you|they|reviewer|owner|user)\b[^.!?;,\n]{0,80}\b(?:approv\w*|authoriz\w*|consent|confirm\w*|accept\w*|desired|requested|needed|proceed)\b)/i;
|
||
const unowned = (value: string) => extractionSourceOwner(value) || /\b(?:proposed|optional|hypothetical|unproven|maybe|might|could)\b/i.test(quoteFree(value)) || conditionalApproval.test(quoteFree(value));
|
||
const inactive = (value: string, ids: string) => unowned(value) || new RegExp(
|
||
`\\b(?:${ids}|${subject}) (?:is|are|was|were|has been|have been) ${inactiveWords}\\b|\\b(?:skip|defer|omit) (?:the |this )?(?:baseline|regression|characterization|parity) (?:test|suite|verification)`, 'i').test(currentText(value, ids));
|
||
if (inactive(block, 'T[1-9]\\d*')) continue;
|
||
for (const parityBlock of blocks) {
|
||
const parity = parityBlock.replace(/\s+/g, ' ').trim();
|
||
const comparison = /^Decision [1-9]\d*[A-D], parity suite:\s*([A-Za-z][\w/.-]*\.test\.[jt]s)\./i.exec(parity);
|
||
if (!comparison || inactive(parityBlock, 'T[1-9]\\d*') || !snapshotSource.includes(parity)
|
||
|| !/^\s*(?:One|The same) fixture table, each row run through both paths \(flag off, flag on\), assert identical outcomes?\b/i.test(parity.slice(comparison[0].length))) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = tasks.body.join('\n').trim(), taskBlocks = taskBody.split(/\n(?=-\s)/);
|
||
const rows = taskBlocks.map(body => ({ body, match: /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–-] ([^\n]+)(?:\n|$)/.exec(body) })).filter(row => row.match);
|
||
if (rows.length !== new Set(rows.map(row => row.match![1])).size) continue;
|
||
const field = (body: string, name: string) => {
|
||
const found = [...body.matchAll(new RegExp(`^ - ${name}: ([^\\n]+)$`, 'gm'))];
|
||
return found.length === 1 ? found[0]![1] : undefined;
|
||
};
|
||
// Match the asserted action after its component label. A later
|
||
// positive test phrase cannot override a leading "Do not write".
|
||
const taskAction = (title: string) => /^[^\n]*?\s+[—–-]\s+(.+)$/.exec(title)?.[1] ?? title;
|
||
const ownedTask = (body: string) => {
|
||
// Source quotations identify the finding but cannot grant or revoke
|
||
// its separately asserted action or verification.
|
||
const action = body.replace(/^ - Surfaced by:.*$/gm, '');
|
||
const preceding = taskBody.slice(0, taskBody.indexOf(body)).trim().split('\n').at(-1) ?? '';
|
||
return !unowned(action) && !unowned(preceding) && snapshotSource.includes(body.replace(/\s+/g, ' ').trim());
|
||
};
|
||
for (const baseline of rows) {
|
||
const title = baseline.match![2]!, id = baseline.match![1]!;
|
||
if (!/^(?:(?:Write|Add|Create) (?:the )?)?CRITICAL (?:regression|characterization) tests? (?:pinning|capturing|recording) current legacyAuthFlow\(\) behavior before any (?:change|rewrite|refactor)\b/i.test(taskAction(title))
|
||
|| field(baseline.body, 'Files') !== rule[1] || !ownedTask(baseline.body)
|
||
|| !/^(?:test|suite) passes against (?:unmodified|unchanged|untouched) legacyAuthFlow\(\) (?:first|before any (?:change|rewrite|refactor))$/i.test(field(baseline.body, 'Verify') ?? '')) continue;
|
||
for (const next of rows) {
|
||
const nextId = next.match![1]!;
|
||
if (nextId === id || field(next.body, 'Files') !== comparison[1] || !ownedTask(next.body)
|
||
|| !/^(?:(?:Write|Add|Implement) (?:the )?)?(?:Table-driven )?parity suite running each fixture row through flag-off and flag-on paths\b/i.test(taskAction(next.match![2]!))
|
||
|| !/^suite green for every row\b/i.test(field(next.body, 'Verify') ?? '')) continue;
|
||
const ids = `(?:${id}|${nextId})(?: (?:baseline requirement|baseline verification|verification|rerun))?`;
|
||
if ([block, parityBlock, baseline.body, next.body].some(value => inactive(value.replace(/^ - Surfaced by:.*$/gm, ''), ids))) continue;
|
||
const cancelled = new RegExp(`\\b${ids} (?:is|was|has been) ${inactiveWords}\\b`, 'i');
|
||
const changedFirst = new RegExp(`^(?:Correction:\\s*)?legacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored) before ${id}\\b`, 'i');
|
||
const withdrawn = current.some(s => {
|
||
if (/\b(?:history|historical|source|quoted|example)\b/i.test(s.title)) return false;
|
||
const body = s.body.join('\n');
|
||
if (new RegExp(`^\\s*\\|\\s*(?:${id}|${nextId})\\s*\\|\\s*["“'‘]?${inactiveWords}["”'’]?\\s*\\|`, 'im').test(body)) return true;
|
||
return currentText(body, ids).split(/\n|[.!?;]\s+/).some(line => !extractionSourceOwner(line) && !/\b(?:if|unless|assuming|provided)\b|\b(?:once|when|pending|after)\b[^.!?\n]{0,50}\bapprov/i.test(line) && (cancelled.test(line) || changedFirst.test(line.trim())
|
||
|| new RegExp(`\\b(?:the|this) legacy (?:regression|characterization) (?:suite|tests?|requirement) (?:is|are|was|were|has been|have been) ${inactiveWords}\\b`, 'i').test(line)));
|
||
});
|
||
if (!withdrawn) return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// A bold inline regression rule may bind two test files through a table
|
||
// and one task: the recorded legacy baseline and its same-fixture parity.
|
||
for (const section of current.filter(s => s.title === 'Tests')) {
|
||
const body = section.body.join('\n');
|
||
const paragraphs = body.split(/\n\s*\n/).map(p => p.replace(/\s+/g, ' ').trim());
|
||
const rule = paragraphs.map(p => /^REGRESSION RULE \(mandatory, no decision required\): legacyAuthFlow\(\) is existing behavior being modified with no covering test(?: \([^()!?]{1,120}\))?\. A regression test is a CRITICAL requirement of this plan: (?:record|capture|pin) legacyAuthFlow\(\) outputs on a fixture set covering [^.!?]{1,300}, before any rewrite begins\. A parity test then runs the same fixtures through ([A-Za-z][\w]*) and asserts identical results\. Both live until the legacy path is deleted\.$/.exec(p)).find(Boolean);
|
||
if (!rule || suiteWithdrawn || !snapshotSource.includes(rule[0])) continue;
|
||
const conditional = /(?:^|[.!?;]\s+|\n)\s*(?:if|unless|once|when|assuming|provided|pending)\s+(?:(?:approv\w*|authoriz\w*|accept\w*|requested)\b|(?:this work|the requirement|we|you|the reviewer)\b[^.!?;\n]{0,80}\b(?:approv\w*|authoriz\w*|accept\w*)\b)/i;
|
||
const status = '(?:withdrawn|rejected|cancelled|canceled|superseded|deferred|optional|proposed|not current|no longer current|not required|no longer required)';
|
||
const assessment = (value: string, id: string) => unquoted(value.replace(new RegExp(
|
||
`((?:^|[.!?;]\\s+|\\n)\\s*(?:Correction:\\s*)?(?:${id}(?: baseline verification)?|(?:this|the) (?:(?:legacy|baseline) )?(?:(?:regression|parity) )?(?:suite|test|requirement|verification)) (?:is|was|has been) )["“'‘](${status})["”'’]`, 'gim'), '$1$2'))
|
||
.replace(/(?<!\w)'[^'\n]*'(?!\w)|‘[^’\n]*’/g, '');
|
||
const inactive = (value: string, id: string, global = false) => {
|
||
const text = assessment(value, id);
|
||
const subject = global ? `${id}(?: baseline verification)?|(?:this|the) legacy (?:regression|parity) (?:suite|test|requirement)`
|
||
: `${id}(?: baseline verification)?|(?:this|the) (?:(?:legacy|baseline) )?(?:(?:regression|parity) )?(?:suite|test|requirement|verification)`;
|
||
return new RegExp(`(?:^|[.!?;]\\s+|\\n)\\s*(?:Correction:\\s*)?(?:${subject}) (?:is|was|has been) ${status}\\b`, 'i').test(text)
|
||
|| new RegExp(`(?:^|[.!?;]\\s+|\\n)\\s*(?:Correction:\\s*)?(?:do not|don't|never|skip|defer|cancel|withdraw) (?:run |execute |implement )?(?:${subject})\\b`, 'i').test(text)
|
||
|| new RegExp(`^\\s*\\| ${id} \\| ${status} \\|`, 'im').test(text)
|
||
|| new RegExp(`(?:^|\\n)(?:Correction:\\s*)?legacyAuthFlow\\(\\) is (?:changed|rewritten|modified) before ${id}\\b`, 'i').test(text);
|
||
};
|
||
const framed = (value: string) => extractionSourceOwner(value) || conditional.test(assessment(value, 'T[1-9]\\d*'));
|
||
if (framed(body) || inactive(body, 'T[1-9]\\d*')) continue;
|
||
for (const table of current.filter(s => s.title === 'Tests to add (every GAP above)')) {
|
||
const text = table.body.join('\n');
|
||
const baseline = /^\| ([A-Za-z][\w/.-]*\.test\.[jt]s) \| unit, CRITICAL \| recorded fixture outputs unchanged \|$/m.exec(text);
|
||
const parity = new RegExp(`^\\| ([A-Za-z][\\w/.-]*\\.test\\.[jt]s) \\| integration, CRITICAL \\| legacy and ${rule[1]} agree on every fixture \\|$`, 'm').exec(text);
|
||
if (!baseline || !parity || framed(text) || inactive(text, 'T[1-9]\\d*') ||
|
||
!snapshotSource.includes(baseline[0]) || !snapshotSource.includes(parity[0])) continue;
|
||
for (const tasks of current.filter(s => s.title === 'Implementation Tasks')) {
|
||
const taskBody = tasks.body.join('\n').trim(), blocks = taskBody.split(/\n(?=-\s)/);
|
||
const ids = blocks.map(b => /^- (?:\[[ xX]\] )?(T[1-9]\d*)\b/.exec(b)?.[1]).filter(Boolean);
|
||
if (ids.length !== new Set(ids).size) continue;
|
||
for (const task of blocks) {
|
||
const match = new RegExp(`^- (?:\\[[ xX]\\] )?(T[1-9]\\d*)(?: \\([^\\n)]*\\))? [—–-] tests [—–-] CRITICAL regression fixtures for legacyAuthFlow\\(\\) and parity test against ${rule[1]}[\\t ]*(?:\\n|$)`).exec(task);
|
||
if (!match) continue;
|
||
const files = [...task.matchAll(/^\s+- Files: ([^\n]+)$/gm)], verifies = [...task.matchAll(/^\s+- Verify: ([^\n]+)$/gm)];
|
||
const names = files[0]?.[1]?.split(', ').sort();
|
||
const before = taskBody.slice(0, taskBody.indexOf(task)).trim().split('\n').at(-1) ?? '';
|
||
if (files.length !== 1 || verifies.length !== 1 || JSON.stringify(names) !== JSON.stringify([baseline[1], parity[1]].sort()) ||
|
||
verifies[0]![1] !== 'both suites green before and after the rewrite' || framed(before) || framed(task) ||
|
||
inactive(task, match[1]!) || !snapshotSource.includes(task.replace(/\s+/g, ' ').trim())) continue;
|
||
if (!current.some(s => inactive(s.body.join('\n'), match[1]!, true))) return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return hasRetainedLegacyCorpus(current, snapshotSource)
|
||
|| !suiteWithdrawn && hasScheduledLegacyRegression(current, snapshotSource);
|
||
}
|
||
|
||
/** Bind a required test file to its task and a baseline run before changing the legacy code. */
|
||
function hasScheduledLegacyRegression(current: ReadonlyArray<{ title: string; body: string[] }>, snapshot: string): boolean {
|
||
const flat = (s: string) => s.replace(/\s+/g, ' ').trim();
|
||
const unquoted = (s: string) => s.replace(/"[^"]*"|“[^”]*”|(?<!\w)'[^'\n]*'(?!\w)|‘[^’]*’/g, '');
|
||
const framed = (s: string) => /(?:^|\n)\s*(?:source|quoted|historical|example|if approved|once approved|when approved|pending approval|assuming approval|provided approval)\b/i.test(unquoted(s));
|
||
const inactive = '(?:withdrawn|rejected|cancelled|canceled|deferred|optional|proposed|hypothetical|unproven|superseded|not current|no longer current|not required|no longer required|conditional on approval)';
|
||
const approval = /\b(?:if|when|once|unless) approved|\b(?:after|pending|assuming|provided) approval\b|(?:^|\n|:\s*)(?:if|when|once|unless) accepted\b/i;
|
||
const owned = (s: string) => snapshot.includes(flat(s)) && !framed(s)
|
||
&& !approval.test(unquoted(s))
|
||
&& !/\b(?:do not|don\x27t|never|skip|omit|defer) (?:add|write|run|capture|record|pin|implement)\b|\b(?:maybe|might|could|optional|proposed)\b/i.test(unquoted(s));
|
||
const allTasks = current.flatMap(s => {
|
||
const text = s.body.join('\n');
|
||
return text.split(/\n(?=- )/).map(body => ({ body, section: s.title,
|
||
preceding: text.slice(0, text.indexOf(body)).trim().split('\n').at(-1) ?? '',
|
||
match: /^- (?:\[[ xX]\] )?(T[1-9]\d*)(?: \([^\n)]*\))? [—–:-] (.+)(?:\n|$)/.exec(body) }));
|
||
});
|
||
// Required test lists may give each suite its own inline label instead of
|
||
// a heading. Keep that bullet's body separate from adjacent test requirements.
|
||
const declarations: Array<{ title: string; body: string[]; inlineRequired?: boolean }> = [...current];
|
||
for (const section of current.filter(s => /\b(?:required|mandatory) tests?\b/i.test(s.title))) {
|
||
const blocks = section.body.join('\n').split(/\n(?=- )/);
|
||
for (const block of blocks) {
|
||
const match = /^- ((?:CRITICAL|MANDATORY|REQUIRED)\b[^\n]*\b(?:regression|characterization)\b[^\n]*)\n([\s\S]+)$/i.exec(block.trim());
|
||
if (match && !framed(section.title) && owned(block) && !new RegExp(`\\b${inactive}\\b`, 'i').test(unquoted(match[1]!)))
|
||
declarations.push({ title: match[1]!, body: [match[1]!, match[2]!], inlineRequired: true });
|
||
}
|
||
}
|
||
for (const declaration of declarations) {
|
||
const requiredRule = /\b(?:mandatory|critical|required)\b/i.test(unquoted(declaration.title));
|
||
if (!/\b(?:regression|characterization)\b/i.test(declaration.title) || !requiredRule
|
||
|| /\b(?:not|never|no longer) (?:mandatory|critical|required)\b/i.test(declaration.title) || approval.test(unquoted(declaration.title)) || /\b(?:if|when|once|unless) accepted\b/i.test(unquoted(declaration.title))) continue;
|
||
const body = declaration.body.join('\n').trim(), text = flat(unquoted(body));
|
||
const files = [...text.matchAll(/\b([A-Za-z][\w/.-]*\.test(?:\.[jt]s)?)\b/g)].map(m => m[1]!);
|
||
const beforeAndAfter = /\bmust (?:pass|be green) before and after (?:this|the) (?:refactor|rewrite|change)\b/i.test(text);
|
||
if (!owned(body) || !/\blegacyAuthFlow\b/.test(declaration.title + ' ' + text)
|
||
|| !beforeAndAfter && !/\bbefore (?:any |the )?(?:rewrite|refactor|change)\b/i.test(text)) continue;
|
||
// A unique task ID can carry the file and verification. The declaration
|
||
// supplies the required old-code corpus and parity; no particular heading
|
||
// or repeated filename/CRITICAL label is needed on the task itself.
|
||
const declaredIds = [...new Set([...text.matchAll(/\bT[1-9]\d*\b/g)].map(m => m[0]))];
|
||
const linkedId = declaredIds.length === 1 && /\bCRITICAL\b/.test(text)
|
||
&& current.filter(s => /\bmandatory\b/i.test(s.title) && /\bCRITICAL\b/.test(unquoted(s.body.join(' ')))
|
||
&& new RegExp(`\\b${declaredIds[0]}\\b`).test(unquoted(s.body.join(' ')))).length === 1 ? declaredIds[0] : undefined;
|
||
const parityTargets = [...text.matchAll(/\b(?:run|execute|replay) (?:the )?same (?:corpus|suite|fixtures) (?:against|through|on) (?:the )?([A-Za-z][\w]*)(?: path)?[.;]/gi)];
|
||
const linkedTarget = linkedId && parityTargets.length === 1
|
||
&& /\b(?:write|add|create) (?:regression|characterization)(?: \(golden\))? tests? for legacyAuthFlow\(\)/i.test(text)
|
||
&& /\bBoth must (?:produce|return|have) (?:identical|matching) (?:results|outcomes|outputs)\b/i.test(text) ? parityTargets[0]![1] : undefined;
|
||
const scheduled = files.length === 1 && /\b(?:captures?|capturing|records?|recording|pins?|pinning)\b[^.;:]{0,180}\b(?:behavior|outcomes|outputs)\b/i.test(text);
|
||
const comparisons = [...text.matchAll(/\b(?:asserts?|verifies?|checks?) ([A-Za-z][\w]*) (?:agrees with|matches) (?:it|(?:the )?(?:legacy )?(?:suite|baseline|outputs))\b/gi)];
|
||
const pinnedParity = scheduled && beforeAndAfter && comparisons.length === 1 && comparisons[0]![1] !== 'legacyAuthFlow'
|
||
&& /\b(?:captures?|capturing|records?|recording|pins?|pinning) (?:the )?(?:current|prior|existing) (?:outputs|outcomes|behavior)\b/i.test(text);
|
||
const tasks = allTasks.filter(t => linkedTarget || pinnedParity || /^Implementation Tasks$/i.test(t.section));
|
||
for (const task of tasks) {
|
||
if (!task.match) continue;
|
||
const [, id, rawTitle] = task.match, title = unquoted(rawTitle!);
|
||
if (linkedTarget && id !== linkedId) continue;
|
||
if (tasks.filter(t => t.match?.[1] === id).length !== 1 || !/\blegacyAuthFlow\b/.test(title)
|
||
|| !/\b(?:regression|characterization)\b/i.test(title)) continue;
|
||
const taskFiles = [...task.body.matchAll(/^ - Files: (.+)$/gm)];
|
||
const verifies = [...task.body.matchAll(/^ - Verify: (.+)$/gm)];
|
||
if (taskFiles.length !== 1 || verifies.length !== 1 || !owned(task.body) || framed(task.preceding)) continue;
|
||
const verify = unquoted(verifies[0]![1]!);
|
||
const runs = verify.split(/;\s*/);
|
||
const linkedBaseline = linkedTarget && linkedTarget !== 'legacyAuthFlow' && id === linkedId
|
||
&& /^[A-Za-z][\w/.-]*\.test(?:\.[jt]s)?$/.test(taskFiles[0]![1]!)
|
||
&& (files.length === 0 || files.length === 1 && files[0] === taskFiles[0]![1])
|
||
&& /\b(?:write|add|create) (?:regression|characterization)(?: \(golden\))? tests? for legacyAuthFlow\(\) before (?:touching (?:it|legacyAuthFlow\(\))|(?:any |the )?(?:rewrite|refactor|change))\b/i.test(title)
|
||
&& runs.length === 2 && /^(?:the )?(?:suite|tests?|corpus) (?:passes|is green) (?:against|on) (?:legacy|legacyAuthFlow(?:\(\))?)$/i.test(runs[0]!)
|
||
&& new RegExp(`^(?:later|then) (?:passes|is green) unchanged (?:against|on) ${linkedTarget}[.]?$`, 'i').test(runs[1]!);
|
||
const scheduledVerification = scheduled && /\bCRITICAL\b/.test(title) && taskFiles[0]![1] === files[0]
|
||
&& /\b(?:pass(?:es)?|green)\b/i.test(verify) && /\bbefore\b[^.;]*\bafter\b|\bbefore\b[^.;]*;[^.;]*\bafter\b/i.test(verify);
|
||
// A task's explicit green baseline on unchanged code before any rewrite
|
||
// commit is already an ordered verification; it need not be repeated in
|
||
// a separate Verification section. The same file pins current outputs
|
||
// in the declaration and supplies the comparison oracle for the new path.
|
||
const committedBaseline = pinnedParity && scheduledVerification
|
||
&& tasks.filter(t => new RegExp(`^ - Files: ${files[0]!.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm').test(t.body)).length === 1
|
||
&& /\b(?:passes?|green) (?:on|against) (?:main|master|(?:the )?(?:unmodified|untouched) (?:code|legacy path)) before (?:any|the) (?:refactor|rewrite|change) commit\b/i.test(verify);
|
||
// The rule, uniquely named file/task and ordered verification together
|
||
// establish the unchanged-code baseline without repeating CRITICAL and
|
||
// "before" on every line of that same requirement.
|
||
const green = (value: string) => /\b(?:pass(?:es)?|green)\b/i.test(value)
|
||
&& !/\b(?:not|never|no longer|fail(?:s|ed|ing)?|red)\b/i.test(value);
|
||
const orderedBaseline = requiredRule && scheduled && taskFiles[0]![1] === files[0]
|
||
&& /\b(?:write|add|create|implement|record|capture|pin)\b/i.test(title)
|
||
&& runs.some(run => green(run) && /\b(?:current|unmodified|untouched) (?:code|implementation|legacy path)\b/i.test(run))
|
||
&& runs.some(run => green(run) && /\bafter (?:the )?(?:rewrite|refactor|change)\b/i.test(run))
|
||
&& /\b(?:captures?|capturing|records?|recording|pins?|pinning) (?:the )?(?:current|prior|existing) (?:outputs|outcomes|behavior)\b/i.test(text)
|
||
&& /\bmust (?:pass|satisfy) (?:the )?same (?:suite|tests|assertions) unchanged\b|\b(?:the )?same (?:suite|tests|assertions) must (?:pass|remain green) unchanged (?:after|across) (?:the )?(?:rewrite|refactor|change)\b/i.test(text)
|
||
&& tasks.filter(t => new RegExp(`^ - Files: ${files[0]!.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm').test(t.body)).length === 1
|
||
&& current.filter(s => /^Verification(?: \([^)]*\))?$/i.test(s.title)).some(s => {
|
||
const steps = s.body.join('\n').trim().split(/\n(?=\d+[.)] )/);
|
||
const first = unquoted(steps[0] ?? '').trim();
|
||
return steps.length >= 2 && owned(steps[0]!) && /^1[.)] (?:run|execute|test|verify)\b/i.test(first)
|
||
&& new RegExp(`\\b${id}\\b`).test(first) && /\b(?:unmodified|untouched) (?:code|implementation|legacy path)\b/i.test(first) && green(first)
|
||
&& steps.slice(1).some(step => {
|
||
const statement = unquoted(step).trim();
|
||
return owned(step) && /^(?:[2-9]|[1-9]\\d+)[.)] (?:re-run|rerun|run|execute)\b/i.test(statement)
|
||
&& new RegExp(`\\b${id}\\b`).test(statement) && /\bafter (?:the )?(?:rewrite|refactor|change)\b/i.test(statement)
|
||
&& green(statement) && /\bunchanged\b/i.test(statement);
|
||
});
|
||
});
|
||
const inlineBaseline = declaration.inlineRequired && scheduled && taskFiles[0]![1] === files[0]
|
||
&& tasks.filter(t => new RegExp(`^ - Files: ${files[0]!.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm').test(t.body)).length === 1
|
||
&& [...text.matchAll(/\bT[1-9]\d*\b/g)].every(m => m[0] === id)
|
||
&& new RegExp(`\\b${id}\\b`).test(text)
|
||
&& /\b(?:run|execute|replay) (?:the )?same fixtures\b/i.test(text)
|
||
&& /\bagainst (?:the )?new path\b/i.test(text)
|
||
&& /\bassert (?:identical|matching)\b[^.;]*\b(?:outputs?|outcomes?|session shape)\b/i.test(text)
|
||
&& /\b(?:rejection|error) (?:class|kind|code)\b/i.test(text)
|
||
&& runs.some(run => green(run) && /\bon (?:the )?legacy(?: path)? before (?:any|the) (?:refactor|rewrite|change)(?: commit)?\b/i.test(run))
|
||
&& runs.some(run => green(run) && /\bon both paths\b/i.test(run))
|
||
&& current.filter(s => /^Verification(?: \([^)]*\))?$/i.test(s.title)).some(s => {
|
||
const first = unquoted(s.body.join('\n').trim().split(/\n(?=\d+[.)] )/)[0] ?? '');
|
||
return owned(first) && /^1[.)] (?:run|execute|test|verify)\b/i.test(first)
|
||
&& new RegExp(`\\b${id}\\b`).test(first) && /\b(?:untouched|unmodified) legacy path\b/i.test(first)
|
||
&& /\bfirst\b/i.test(first);
|
||
});
|
||
const scopedBaseline = linkedBaseline || committedBaseline || orderedBaseline || inlineBaseline;
|
||
if (!scopedBaseline && !scheduledVerification) continue;
|
||
// An explicit ordered step provides the old-code oracle; matching a task label alone cannot.
|
||
const baseline = scopedBaseline || current.filter(s => /^Verification(?: \([^)]*\))?$/i.test(s.title)).some(s => {
|
||
const lines = s.body.join('\n').split(/\n(?=\d+\. )/);
|
||
return lines.some(line => {
|
||
const statement = unquoted(line);
|
||
return /^\d+\. (?:Write|Run|Execute|Add|Create)\b/i.test(statement) && owned(line) && new RegExp(`\\b${id}\\b`).test(statement)
|
||
&& /\blegacyAuthFlow\b/.test(statement) && /\b(?:untouched|unmodified)\b/.test(statement)
|
||
&& /\b(?:must (?:pass|be green)|commit it green)\b/i.test(statement)
|
||
&& /\bbefore\b|\bfirst\b/i.test(statement);
|
||
});
|
||
});
|
||
if (!baseline) continue;
|
||
const subject = `(?:${id}(?: (?:baseline )?verification)?|(?:this|the) (?:(?:legacy|baseline) )?(?:(?:regression|characterization) )?(?:suite|test|requirement|verification))`;
|
||
const cancelled = current.some(s => {
|
||
if (/\b(?:history|historical|source|quoted|example)\b/i.test(s.title)) return false;
|
||
const named = /^(.*?)\b(?:regression|characterization)\s+(?:suite|tests?)\b/i.exec(s.title)?.[1]?.trim();
|
||
const foreign = Boolean(named && !/^(?:(?:current|final|critical|required|updated)\s*)*(?:legacy(?:AuthFlow\(\))?)?[\s:—-]*$/i.test(named));
|
||
const raw = s.body.join('\n').replace(new RegExp(`(${subject} (?:is|was|has been) )["“'‘](${inactive})["”'’]`, 'gi'), '$1$2');
|
||
return unquoted(raw).split(/\n|[.!?;]\s+/).some(line => {
|
||
if (framed(line) || /^\s*(?:if|unless|assuming|provided)\b/i.test(line)) return false;
|
||
if (foreign && !new RegExp(`\\b${id}\\b|legacyAuthFlow|\\blegacy (?:regression|characterization)`).test(line)) return false;
|
||
return new RegExp(`\\b${subject} (?:is|was|has been) ${inactive}\\b|^\\s*\\|\\s*${id}\\s*\\|\\s*${inactive}\\s*\\|`, 'i').test(line)
|
||
|| new RegExp(`^\\s*(?:Correction:\\s*)?(?:do not|don't|never|skip|defer|cancel|withdraw) (?:run |execute |implement )?${subject}\\b`, 'i').test(line)
|
||
|| new RegExp(`\\b(?:run|execute|record|capture) ${id} only after (?:modifying|changing|rewriting|refactoring|removing|deleting) legacyAuthFlow\\b`, 'i').test(line)
|
||
|| new RegExp(`\\blegacyAuthFlow\\(\\) (?:is|was|has been|will be) (?:modified|changed|rewritten|refactored|removed|deleted) before ${id}\\b`, 'i').test(line)
|
||
|| Boolean(scopedBaseline && new RegExp(`\\b(?:update|change|replace|regenerate|rewrite) ${subject} (?:expectations|expected (?:results|outputs)|assertions)\\b|\\b${subject} (?:expectations|assertions) (?:are|will be) (?:changed|updated|replaced)\\b`, 'i').test(line));
|
||
});
|
||
});
|
||
if (!cancelled) return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function regressionEvidence(text: string): boolean {
|
||
if (declaredLegacyCharacterization(text)) return true;
|
||
return prose(text).split(/\n\s*\n|\n(?=\s*[-#])/).some(block => {
|
||
let task = block.trim().replace(/^[-+]\s+(?:\[[ xX]\]\s*)?/, '');
|
||
const numbered = /^T\d+(?:\s*\([^\n)]*\))?\s*[—–:-]\s*/.exec(task);
|
||
if (numbered) {
|
||
// A numbered task may place a simple component path before its action.
|
||
// Do not remove arbitrary prose or let this metadata assign the test target.
|
||
task = task.slice(numbered[0].length)
|
||
.replace(/^[A-Za-z][A-Za-z0-9_-]*(?:\/[A-Za-z][A-Za-z0-9_-]*)+[\t ]+[—–][\t ]+/, '');
|
||
}
|
||
if (requiredLegacyCharacterization(task)) return true;
|
||
const legacySubject = /^legacyAuthFlow(?:\(\))?\s*[—–:-]\s*/i;
|
||
const action = task.replace(legacySubject, '');
|
||
const instruction = action.match(/^(?:(?:I|we)\s+)?(?:add(?:ed)?|record(?:ed)?|write|wrote|require(?:d)?|include(?:d)?)\s+((?:(?:a|the|new|required|legacyAuthFlow(?:\(\))?|regression|characterization|baseline|prior-behavior)\s+)*(?:tests?|fixtures?|suites?))\b([^.;\n]*)/i);
|
||
const explicitTarget = instruction && /^\s+(?:for|of|covering|characterizing|pinning)\b/i.test(instruction[2]!);
|
||
const legacyTarget = instruction && (
|
||
/^\s+(?:for|of|covering|characterizing)\s+(?:the\s+)?(?:prior behavior of\s+)?legacyAuthFlow\b/i.test(instruction[2]!) ||
|
||
/^\s+pinning\s+(?:the\s+)?legacyAuthFlow(?:\(\))?(?:'s)?\s+(?:current|existing|prior)\s+behavior\b/i.test(instruction[2]!));
|
||
const target = instruction && (!explicitTarget || legacyTarget) &&
|
||
(legacySubject.test(task) || /\blegacyAuthFlow\b/.test(instruction[1]!) || legacyTarget);
|
||
// An affirmative task or completed addition, not an example, quotation,
|
||
// conditional proposal or an uncertain discussion of whether to add it.
|
||
return Boolean(target) &&
|
||
/\b(?:regression|characterization)\b/i.test(instruction![0]) &&
|
||
/\b(?:before|prior behavior|parity|compatibility|characterization)\b/i.test(instruction![0]) &&
|
||
!/\b(?:no|not|never|skip\w*|defer\w*|maybe|might|could|if|unless|optional)\b/i.test(block);
|
||
});
|
||
}
|
||
|
||
export function evaluateEngSeedCoverage(transcript: PlanCountTranscript, plan: string,
|
||
startedAt: number, finishedAt: number) {
|
||
const decisions: Partial<Record<Seed, string>> = {};
|
||
const problems: string[] = [];
|
||
const sessions = new Set(transcript.calls.map(c => c.sessionId));
|
||
const identities = transcript.calls.map(c => `${c.sessionId}:${c.toolUseId}`);
|
||
const bound = transcript.status === 'ready' && sessions.size === 1 && !sessions.has('') &&
|
||
identities.length === new Set(identities).size && Number.isFinite(startedAt) &&
|
||
Number.isFinite(finishedAt) && startedAt <= finishedAt;
|
||
if (!bound) problems.push('missing, ambiguous or unbound native transcript');
|
||
if (bound) for (const call of transcript.calls) {
|
||
if (!completedDecision(call, startedAt, finishedAt)) continue;
|
||
const seeds = call.questions.flatMap(seedSubjects);
|
||
// One combined approval cannot replace separate decisions for independent seeds.
|
||
// An unrelated, separately answered setup tab may accompany the one seed;
|
||
// multiple seeded questions still cannot lend this call ID to several seeds.
|
||
if (seeds.length === 1) decisions[seeds[0]!] ??= `${call.sessionId}:${call.toolUseId}`;
|
||
}
|
||
const missing = ENG_DECISION_SEEDS.filter(seed => !decisions[seed]);
|
||
const regression = regressionEvidence(plan) ? 'plan' : bound && transcript.assistantMessages.some(m =>
|
||
sessions.has(m.sessionId) && Date.parse(m.timestamp) >= startedAt && Date.parse(m.timestamp) <= finishedAt &&
|
||
regressionEvidence(m.text)) ? 'public-narration' : undefined;
|
||
if (!regression) problems.push('mandatory legacy regression coverage absent');
|
||
// The caller also retains the existing fresh owned-path/native completion and D19 checks.
|
||
if (!/^## GSTACK REVIEW REPORT[\t ]*\n\s*\S/m.test(prose(plan))) problems.push('final review report absent or empty');
|
||
return { ok: bound && missing.length === 0 && problems.length === 0, decisions, missing, regression, problems };
|
||
}
|