mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 15:04:37 +01:00
* fix(make-pdf,design): real TOC page numbers and anchors; design models move to gpt-5.5 + gpt-image-2 (#2903, #2807) * ci: pin every action to a SHA, Scorecard, checksummed Bun, scoped Codex CLI in the CI image, macOS named-regressions job (#1948, #1706, #1997) * fix(cso,browse): Windows/macOS lease identity, retention skips bad runs, truthful 'not assessed' reports, env.kv false positive, Windows browse spawn fixes (#2894, #2912, #2048, #2637, #2805) * fix(outside-voice): one Codex model precedence for probe and dispatch, bounded timeouts with KILL escalation, probe cache identity, guarded mktemp, skills hidden in nested Codex (#2776, #2914, #2787, #2881, #2847) * feat(setup): install registry, ./setup --status, staged activation, per-scope migrations, host tiers and conformance kit, OpenCode/Hermes fixes, Copilot CLI (experimental), disabled_skills, slimmer CLAUDE.md (#1925, #2879, #2347, #2338, #2626, #2629, #2825, #2015, #393, #1206, #2096) Co-authored-by: Andrey Esipov <61357035+andrey-esipov@users.noreply.github.com> Co-authored-by: Matthew Williams <5863886+ridermw@users.noreply.github.com> Co-authored-by: Rocky Wu <92764750+lolisaigao1234@users.noreply.github.com> * fix(preamble): skills start in worktree-isolated Claude Code sessions — one literal skill-start line, context-recovery helper, no eval in skill blocks, $N token tripwire, honest plan-mode precedence (#2763, #2896, #2851) * fix(setup): per-install renders for renamed, vendored and custom-config installs, space-safe alias roots, per-host /cso launcher path (#1882, #2906) * test: free tests never write the real HOME (home-write tripwire), no orphaned browse daemons, symlinked-TMPDIR (macOS) fixes, gbrain-on-PATH isolation (#2895, #2816, #2829, #2841, #2982, #2911, #2909, #2908, #2910) * chore: integrate wave follow-ups — regenerate all hosts, README model docs, TODOS, touchfiles, duration seeds, Bun 1.3 telemetry test fix, replay fixtures pinned to their recorded bytes * v1.91.14.0: release metadata * test(browse): group-kill cleanup falls back to the leader when macOS returns EPERM for a live group * ci: Codex login steps run under bash (the CI container's default sh rejects pipefail) * fix: full-suite repairs — private state roots for 7 real-HOME writers, truthful home-guard attribution with --attribute-home, gstack-paths shim in ship-hook fixtures, Copilot in team-init probes, test-of-test detector, cookie review fixture pinned to the approved excerpt * fix(windows): LF for Dockerfiles, normalized home-guard exclusions, path.relative fixture containment, ship push test spawn timeouts * test(design-detect): widen the whole-scan budget test's timing margins so a loaded macOS runner can't time out the single-batch call * fix(evals): qa protocol names defined for the judge, async PTY judge so overlapping sessions keep their cleanup deadline, bws setup cases use the standard JUDGE_MS tier * test(pty): the non-blocking judge case is POSIX-only (its fake judge is a shebang script, like the PTY harness it covers) * test(evals): ship-coverage-audit names absolute skill paths, uses Read, and skips the Step 7 artifact nothing consumes (removes the mechanism behind 3 of 4 historical timeouts) * test: deflake terminal-agent concurrent startup (write the placeholder record before spawning, like the product) and qa-evidence resumed pipe (barrier on a full high-water mark instead of a sleep) * fix(design): daemon log stream handles open errors instead of crashing the daemon; shutdown test awaits the stubbed exit * test(evals): skillify checks the product's resolved state root (no GSTACK_HOME bypass), negative cases check every root; tpa shims live outside the agent's cwd and answer consistently * test(evals): coverage-audit detector splits parallel columns at │ rails and [+] groups; coverage prompts name absolute skill paths; TODOs from the gate eval census * fix(plan-eng-review): scope gate always sends the menu (no headless 'Scope pending' exit); PTY harness ends idle turn-ends and API-error panels promptly with a precise diagnostic * test(evals): one position-bound coverage-read rule credits read-only && chains (labels, context ranges, git displays in either order) and still rejects forged output * fix(ship): documentation section has one saved source inserted by cat (never retyped); spawned-marker evidence is the child output; refreshed codex/factory ship goldens * test(docsync): the byte-exact insert positive is POSIX-only (the bash-command observer refuses backslash paths by design) * test(evals): plan-design-with-ui focus-title rule accepts the comma-less 'dimensions or focus' wording (replayed on the captured CI run, negative controls kept) * fix(qa-callers): exploratory preflight says the scope/method Reads come after exploratory.md; design-review scan check credits an executed scan proven by one engine run over the changed files --------- Co-authored-by: Andrey Esipov <61357035+andrey-esipov@users.noreply.github.com> Co-authored-by: Matthew Williams <5863886+ridermw@users.noreply.github.com> Co-authored-by: Rocky Wu <92764750+lolisaigao1234@users.noreply.github.com>
101 lines
5.9 KiB
TypeScript
101 lines
5.9 KiB
TypeScript
/**
|
||
* /plan-design-review with UI scope (gate, paid, real-PTY).
|
||
*
|
||
* The exact UI-heavy plan is committed before the first model turn. Observe
|
||
* an acknowledged Design focus/rating question and a subsequent review
|
||
* question using the existing native counting driver. An initial target menu
|
||
* or a no-UI early exit cannot satisfy this positive path. Option descriptions
|
||
* are proposals, so mentioning "no UI scope" there is not an exit verdict.
|
||
*/
|
||
|
||
import { test } from 'bun:test';
|
||
import { PTY_MS } from './helpers/eval-budgets';
|
||
import { describeE2ETier } from './helpers/e2e-gate';
|
||
import { createDesignReviewPicker, DESIGN_BOARD_ACTOR_PROTOCOL } from './helpers/plan-review-board-feedback';
|
||
import * as fs from 'node:fs';
|
||
import * as path from 'node:path';
|
||
import {
|
||
runPlanSkillCounting,
|
||
type AskUserQuestionFingerprint,
|
||
} from './helpers/claude-pty-runner';
|
||
|
||
const describeE2E = describeE2ETier('gate');
|
||
const ROOT = path.resolve(import.meta.dir, '..');
|
||
const FIXTURE = path.join(ROOT, 'test', 'fixtures', 'plans', 'ui-heavy-feature.md');
|
||
|
||
const designFocusBoundary = (fp: AskUserQuestionFingerprint): boolean =>
|
||
fp.nativeCall?.answered === true && !fp.nativeCall.failed && fp.nativeCall.questions.some(({ question }) => {
|
||
const text = question.trim().replace(/^D\d+(?:\.\d+)?\s*[—–:-]\s*/i, '');
|
||
// Require the source Step 0D question or its retained native paraphrase.
|
||
// A target menu can mention a design system without reviewing this plan.
|
||
return /^I(?:['’]ve| have) rated this plan (?:10(?:\.0+)?|[0-9](?:\.\d+)?)\/10 on design completeness\.[\s\S]*\bWant me to focus on specific areas instead of all 7\?/i.test(text)
|
||
|| /^Review all 7 design (?:dimensions|passes),? or focus(?: on [^?\n]+)?\?$/i.test(text.split(/\r?\n/, 1)[0]!);
|
||
});
|
||
|
||
// Require a choice about the supplied UI, not a workflow offer after focus.
|
||
// The review labels each finding's options with its issue number and a letter
|
||
// ("3A", "3B"; review-sections.md), so a complete issue-labeled option set
|
||
// identifies a Design finding whatever vocabulary its title uses. Otherwise both
|
||
// the question and an offered remedy must describe concrete UI behavior.
|
||
const issueLabeled = (options: Array<{ label: string }>): boolean => {
|
||
const labels = options.map(({ label }) => /^\s*(\d+)([A-Z])(?=[):.\s]|$)/.exec(label));
|
||
return labels.length >= 2 && labels.every(Boolean)
|
||
&& new Set(labels.map(match => match![1])).size === 1
|
||
&& new Set(labels.map(match => match![2])).size === labels.length;
|
||
};
|
||
const uiChoice = /\b(?:layout|compos(?:e|ed|ition)|anchor|regions?|panels?|notifications?|activity|quick actions?|loading|skeletons?|empty|errors?|success|modals?|toasts?|buttons?|links?|copy|typography|fonts?|spacing|contrast|colors?|breakpoints?|responsive|keyboard|focus (?:order|trap|management)|aria|a11y|accessibility)\b/i;
|
||
const designReviewFinding = (fp: AskUserQuestionFingerprint): boolean =>
|
||
fp.nativeCall?.answered === true && !fp.nativeCall.failed && !designFocusBoundary(fp) && fp.nativeCall.questions.some(({ question, options }) => {
|
||
const title = question.split(/\r?\n/, 1)[0]!.trim().replace(/^D\d+(?:\.\d+)?\s*[—–:-]\s*/i, '');
|
||
const setup = /\b(?:outside (?:design )?voices|cross[ -]project learnings|review (?:target|scope|mode)|what should I (?:design[ -])?review|which (?:artifact|plan|file))\b/i;
|
||
return !setup.test(title) && (issueLabeled(options)
|
||
|| (uiChoice.test(title) && options.some(option => uiChoice.test(`${option.label} ${option.description}`))));
|
||
});
|
||
|
||
describeE2E('/plan-design-review with UI scope (gate)', () => {
|
||
test(
|
||
'reviews the supplied UI plan through an acknowledged Design finding',
|
||
async () => {
|
||
const startedAt = Date.now();
|
||
const plan = fs.readFileSync(FIXTURE, 'utf8');
|
||
let picker: ReturnType<typeof createDesignReviewPicker> | undefined;
|
||
let pickerCwd: string | undefined;
|
||
const obs = await runPlanSkillCounting({
|
||
skillName: 'plan-design-review', slashCommand: '/plan-design-review',
|
||
followUpPrompt: ['Review the supplied UI plan in review-input.md. Read it before',
|
||
'choosing review scope.', '', plan, '', DESIGN_BOARD_ACTOR_PROTOCOL].join('\n'),
|
||
fixtureFiles: {'review-input.md': plan},
|
||
isLastStep0AUQ: designFocusBoundary,
|
||
isReviewAUQ: designReviewFinding,
|
||
reviewCountCeiling: 1,
|
||
observeSetupQuestions: true,
|
||
bindDesignBoardState: true,
|
||
pickAUQ: (_routing, active, context) => {
|
||
const call = active.nativeCall;
|
||
const index = active.nativeQuestionIndex ?? (call?.questions.length === 1 ? 0 : -1);
|
||
if (!call && (/\/boards\//.test(active.promptSnippet) || active.options.some(option => /\bSubmitted\b/.test(option.label)))) {
|
||
throw new Error('Design board choice requires an owned native question');
|
||
}
|
||
if (!call || call.answered || call.failed || index < 0 || !call.questions[index]) return null;
|
||
if (pickerCwd && pickerCwd !== context.cwd) throw new Error('Design board picker fixture changed');
|
||
pickerCwd = context.cwd;
|
||
picker ??= createDesignReviewPicker(context);
|
||
const question = call.questions[index];
|
||
return picker({...question, multiSelect: question.multiSelect ?? false,
|
||
options: question.options.map(option => ({...option, description: option.description ?? ''}))});
|
||
},
|
||
timeoutMs: 600_000 - (Date.now() - startedAt),
|
||
});
|
||
const focus = obs.fingerprints.findIndex(designFocusBoundary);
|
||
const postFocus = focus >= 0 && obs.fingerprints.slice(focus + 1).some(fp => !fp.preReview && designReviewFinding(fp));
|
||
if (obs.outcome !== 'ceiling_reached' || !postFocus) {
|
||
throw new Error(
|
||
`plan-design-review with UI scope FAILED: outcome=${obs.outcome}; no acknowledged Design focus and subsequent review question\n` +
|
||
`--- evidence (last 3KB) ---\n${obs.evidence}`,
|
||
);
|
||
}
|
||
},
|
||
PTY_MS,
|
||
);
|
||
});
|