mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-03 01:46:55 +02:00
- A1: the retired Eng lexical oracle (evaluateEngSeedCoverage, isEngSeedDecisionAUQ), the completion-handoff detector and the retained corpus had no paid caller since v1.87.6; delete their 26 replay files, ~2.6k helper LOC and fixtures, and the dead blocks in 8 mixed files (live hasNativePlanTerminal / batching assertions stay). - A2: dead viewport approvers in autoplan-artifact-permission and their 11 replay files + fixtures; recorder/launcher cases stay. - A3: never-wired oracles and seeders (autoplan-phase-order, eng-finding-fixture, ceo-paired-fixture, design-ui-scope, plan-skill-completion, pty-current-screen, required-reads, transcript-section-logger); plan-seed-submission now decodes through the production createPtyScreen; section manifests name their actual guard. - A4: zero-reference helper exports, plus execGit and invokeAndObserve found by the reachability pass. - 52 fixtures orphaned by the deletions; touchfile and selection-table entries for every deleted path.
95 lines
7.1 KiB
TypeScript
95 lines
7.1 KiB
TypeScript
/** Ground truth for the existing plan fixtures, independent of review phase. */
|
||
import type { NativeQuestion } from './plan-skill-questions';
|
||
export const CEO_PAIRED_FINDINGS = [
|
||
{ id: 'receipt-test', description: 'Independently decide happy-path processPayment test coverage asserting the correct receipt after a successful Stripe charge.' },
|
||
{ id: 'failure-test', description: 'Independently decide Stripe 502/timeout coverage asserting one retry with backoff and then clean failure.' },
|
||
];
|
||
|
||
export const CEO_SCOPE_CANDIDATES = [
|
||
{ id: 'E1', description: 'The whole Slack DM bot for incident alerts integration: include, defer, or cut it.' },
|
||
{ id: 'E2', description: 'The whole Discord guild bot for community channels integration: include, defer, or cut it.' },
|
||
{ id: 'E3', description: 'The whole Microsoft Teams webhook and bot framework integration: include, defer, or cut it.' },
|
||
{ id: 'E4', description: 'The whole Telegram bot API integration: include, defer, or cut it.' },
|
||
{ id: 'E5', description: 'The whole Mattermost REST plugin integration: include, defer, or cut it.' },
|
||
];
|
||
export const DEVEX_FINDINGS = [
|
||
{ id: 'persona', description: 'Decide a specific target developer persona instead of shipping for everyone.' },
|
||
{ id: 'first-run-benchmark', description: 'Decide measurement/benchmarking of time to hello world instead of leaving first-run duration unknown.' },
|
||
{ id: 'mandatory-ci', description: 'Decide how to remove or justify the mandatory five-minute CI step before the first eval.' },
|
||
{ id: 'aha', description: 'Decide a concrete interactive demo or aha moment in the getting-started flow instead of documentation alone.' },
|
||
{ id: 'peer-comparison', description: 'Produce grounded comparative analysis of peer SDK developer experiences and its implications for this plan instead of ignoring existing solutions.' },
|
||
];
|
||
export const ENG_BATCHING_FINDINGS = [
|
||
{ id: 'retry-library', description: 'Decide reuse of existing job-library retry hooks instead of a custom inline backoff scheduler per worker.' },
|
||
{ id: 'retry-duplication', description: 'Decide consolidation of the copied retry envelope across five workers.' },
|
||
{ id: 'at-most-once', description: 'Decide regression coverage for processWebhookJob at-most-once delivery when rewriting it.' },
|
||
{ id: 'dependency-cache', description: 'Decide caching and reusing the dependency graph across retries instead of rebuilding it on every attempt. Payload fetching or freshness is a separate policy and is not required for graph-cache coverage.' },
|
||
];
|
||
|
||
const planReviewQuestionLead = (question: NativeQuestion) =>
|
||
question.question.split(/\r?\n/, 1)[0]!.replace(/^D\d+(?:\.\d+)?\s*[—–:-]\s*/, '');
|
||
const planReviewOptionLabel = (label: string) => label.trim()
|
||
.replace(/^(?:[A-E][).:]?|\([A-E]\)|\[[A-E]\])\s+/i, '')
|
||
.replace(/\s*\(recommended\)\s*$/i, '').trim();
|
||
|
||
/** This fixture's actor retains optional roadmap work for later planning.
|
||
* It does not classify findings: a seeded obligation offered as a TODO still
|
||
* has to satisfy the unchanged semantic judge after native completion. */
|
||
export function pickDevexCheckpointQuestion(question: NativeQuestion): number {
|
||
const labels = question.options.map(option => planReviewOptionLabel(option.label).toLowerCase());
|
||
const todo = /^TODO(?:\s|:|$)/i;
|
||
const actionContext = labels.some(label => /^(?:add to todos\.md|build it now)\b/.test(label));
|
||
if (!todo.test(question.header.trim()) && !todo.test(planReviewQuestionLead(question).trim()) && !actionContext) {
|
||
return pickPlanReviewQuestion(question);
|
||
}
|
||
const actions = ['add to todos.md', 'skip', 'build it now'];
|
||
if (question.multiSelect || labels.length !== actions.length
|
||
|| actions.some(action => labels.filter(label => label === action).length !== 1)) {
|
||
throw new Error('DX checkpoint TODO menu must offer exactly Add to TODOS.md, Skip and Build it now');
|
||
}
|
||
return labels.indexOf('add to todos.md') + 1;
|
||
}
|
||
|
||
/** Answer only the finite next-step menus offered by the review sources. These
|
||
* are future handoffs; the driver still requires native completion and never
|
||
* approves ExitPlanMode or treats this selection as completion. */
|
||
export function pickPlanReviewQuestion(question: NativeQuestion): number {
|
||
const lead = planReviewQuestionLead(question);
|
||
const nextReview = /^(?:next review|next steps?|what['’]s next)\b/i.test(question.header.trim())
|
||
|| /^(?:next reviews?|next steps?|what['’]s next)\b/i.test(lead.trim());
|
||
const recommended = () => {
|
||
const choices = question.options.flatMap((option, index) =>
|
||
/\s\(recommended\)\s*$/i.test(option.label) ? [index + 1] : []);
|
||
if (choices.length > 1) throw new Error('Review question has multiple recommended options');
|
||
return choices[0] ?? 1;
|
||
};
|
||
if (!nextReview) return recommended();
|
||
const labels = question.options.map(option => planReviewOptionLabel(option.label));
|
||
const run = (label: string) => /^(?:Run )?\/plan-(?:ceo|eng|design|devex)-review(?: (?:next|first))?(?:\s*\((?:required gate|only if UI scope detected(?: and no design review exists)?|only if fundamental product gaps found|only if significant product change and no CEO review exists)\))?$/i.test(label)
|
||
|| /^(?:Run )?\/design-shotgun(?: to explore visual variants| for visual variants| after adding an OpenAI key|\s*[—–-]\s*explore visual design variants for issues found)?$/i.test(label)
|
||
|| /^(?:Run )?\/design-html(?:\s*[—–-]\s*generate Pretext-native HTML from approved mockups)?$/i.test(label);
|
||
// A bare Skip declines only an offered, recognized follow-up in this handoff.
|
||
const offersFollowUp = labels.some(run);
|
||
const manual = (label: string) => /^Skip\s*[,:;.—–-]\s*(?:I(?:['’]ll| will)\s+)?handle (?:reviews|next steps) manually$/i.test(label)
|
||
|| (offersFollowUp && (/^(?:Skip|Handle manually)$/i.test(label) || /^Skip\s*[,:;.—–-]\s*handle manually$/i.test(label) || /^Skip, manual next steps$/i.test(label)));
|
||
const future = (label: string) => /^Ready to implement(?:\s*[—–-]\s*run \/ship when done)?$/i.test(label)
|
||
|| /^Ready to implement[,;] (?:run )?\/devex-review after shipping$/i.test(label)
|
||
|| (offersFollowUp && (/^Implement, then \/devex-review$/i.test(label)
|
||
|| /^Implement now, \/devex-review after$/i.test(label)));
|
||
if (!labels.some(label => run(label) || manual(label) || future(label))) return recommended();
|
||
const manualChoices = labels.flatMap((label, index) => manual(label) ? [index + 1] : []);
|
||
const futureChoices = labels.flatMap((label, index) => future(label) ? [index + 1] : []);
|
||
const choices = manualChoices.length ? manualChoices : futureChoices;
|
||
if (choices.length !== 1 || labels.some(label => !run(label) && !manual(label) && !future(label))) {
|
||
throw new Error('Review handoff has no unambiguous offered manual or future-follow-up choice\n' + JSON.stringify({
|
||
header: question.header.slice(0, 80), lead: lead.slice(0, 240), optionCount: labels.length,
|
||
options: question.options.slice(0, 8).map((option, index) => ({
|
||
index: index + 1, label: option.label.slice(0, 256),
|
||
run: run(labels[index]!), manual: manual(labels[index]!), future: future(labels[index]!),
|
||
})),
|
||
omittedOptions: Math.max(0, labels.length - 8),
|
||
}));
|
||
}
|
||
return choices[0]!;
|
||
}
|