mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 14:34:42 +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>
185 lines
11 KiB
TypeScript
185 lines
11 KiB
TypeScript
// ─── Shared Design Constants ────────────────────────────────
|
|
|
|
import { DESIGN_SLOP_CATALOG } from '../../lib/design-catalog';
|
|
|
|
/**
|
|
* gstack's AI slop anti-patterns — shared between DESIGN_METHODOLOGY and DESIGN_HARD_RULES.
|
|
*
|
|
* Derived from the typed catalog in lib/design-catalog.ts: the 11 entries flagged
|
|
* `legacyBlacklist`, prose verbatim, in catalog order. Overused fonts live there
|
|
* too (OVERUSED_FONTS_DISPLAY), role-scoped: banned as the display voice, several
|
|
* still fine as body/UI on an Operate or Read surface.
|
|
*/
|
|
export const AI_SLOP_BLACKLIST: string[] = DESIGN_SLOP_CATALOG
|
|
.filter(e => e.legacyBlacklist)
|
|
.map(e => e.prose);
|
|
|
|
/** OpenAI hard rejection criteria (from "Designing Delightful Frontends with GPT-5.4", Mar 2026) */
|
|
export const OPENAI_HARD_REJECTIONS = [
|
|
'Generic SaaS card grid as first impression',
|
|
'Beautiful image with weak brand',
|
|
'Strong headline with no clear action',
|
|
'Busy imagery behind text',
|
|
'Sections repeating same mood statement',
|
|
'Carousel with no narrative purpose',
|
|
'App UI made of stacked cards instead of layout',
|
|
];
|
|
|
|
/** OpenAI litmus checks — 7 yes/no tests for cross-model consensus scoring */
|
|
export const OPENAI_LITMUS_CHECKS = [
|
|
'Brand/product unmistakable in first screen?',
|
|
'One strong visual anchor present?',
|
|
'Page understandable by scanning headlines only?',
|
|
'Each section has one job?',
|
|
'Are cards actually necessary?',
|
|
'Does motion improve hierarchy or atmosphere?',
|
|
'Would design feel premium with all decorative shadows removed?',
|
|
];
|
|
|
|
/**
|
|
* Web-search flag for every codex invocation (#2525).
|
|
*
|
|
* codex >=0.144 deprecated the legacy `--enable`-based web_search_cached
|
|
* spelling (web search is on by default; the deprecation notice says to set
|
|
* `web_search` to "live", "indexed", "cached", or "disabled" at the top
|
|
* level), and `--enable <FEATURE>` now means `-c features.<name>=true`
|
|
* (verified on 0.147.0), so the legacy spelling is headed for hard
|
|
* rejection. This is the ONE source
|
|
* of truth: resolvers interpolate it directly and templates reference it via
|
|
* the {{CODEX_WEB_SEARCH_FLAG}} token — never write the flag inline.
|
|
*
|
|
* Semantics note: unlike the legacy flag (which yielded to an existing
|
|
* top-level `web_search` in config.toml), the -c form explicitly overrides
|
|
* it. Deliberate: gstack wants deterministic cached search for review
|
|
* invocations. Native `codex review` disables web search regardless of
|
|
* configuration, so on that path the flag is a harmless no-op.
|
|
*/
|
|
export const CODEX_WEB_SEARCH_FLAG = `-c 'web_search="cached"'`;
|
|
|
|
/**
|
|
* Default model for gstack-owned Codex invocations when nothing else chooses.
|
|
*
|
|
* The runtime model is resolved per invocation kind by
|
|
* `_gstack_codex_select_model exec|review` (bin/gstack-codex-probe, backed by
|
|
* resolveCodexRuntimeModel in scripts/resolve-codex-generation-model.ts):
|
|
* explicit request, then GSTACK_CODEX_MODEL, then Codex config.toml (`model`;
|
|
* `review_model` first for native review; honors CODEX_HOME), then this default
|
|
* (#2914). The selection is printed before the first paid call and the probe
|
|
* checks the same record the flags below pass.
|
|
*/
|
|
export const CODEX_FRONTIER_MODEL = 'gpt-6-astra';
|
|
/**
|
|
* Nested gstack Codex calls are one-shot reviews: keep installed skills (gstack's
|
|
* own included) out of the model's context so the reviewer cannot re-run a whole
|
|
* skill workflow inside its budget (#2847). Read-only sandboxes do not prevent this.
|
|
*/
|
|
export const CODEX_SKILLS_ISOLATION_FLAG = '-c skills.include_instructions=false';
|
|
const SELECTED_MODEL = '${_GSTACK_CODEX_SEL:?}';
|
|
/** Requires `_gstack_codex_select_model exec` earlier in the same shell; `:?` stops an unselected command. */
|
|
export const CODEX_MODEL_CONFIG_FLAG = `-c "model=\\"${SELECTED_MODEL}\\"" ${CODEX_SKILLS_ISOLATION_FLAG}`;
|
|
/** Requires `_gstack_codex_select_model review`; native review prefers review_model, so both carry the selection. */
|
|
export const CODEX_REVIEW_MODEL_CONFIG_FLAG = `-c "review_model=\\"${SELECTED_MODEL}\\"" ${CODEX_MODEL_CONFIG_FLAG}`;
|
|
|
|
/**
|
|
* Shared Codex error handling block for resolver output.
|
|
* Used by ADVERSARIAL_STEP, CODEX_PLAN_REVIEW, CODEX_SECOND_OPINION,
|
|
* DESIGN_OUTSIDE_VOICES, DESIGN_REVIEW_LITE, DESIGN_SKETCH.
|
|
*/
|
|
export function codexErrorHandling(feature: string): string {
|
|
return `**Error handling:** All errors are non-blocking — the ${feature} is informational.
|
|
- Auth failure (stderr contains "auth", "login", "unauthorized"): note and skip
|
|
- Timeout: note timeout duration and skip
|
|
- Empty response: note and skip
|
|
On any error: continue — ${feature} is informational, not a gate.`;
|
|
}
|
|
|
|
/**
|
|
* Shared Codex preflight bash block — the single source of truth for deciding
|
|
* whether a Codex review pass should run. Used by ADVERSARIAL_STEP,
|
|
* CODEX_PLAN_REVIEW, and CODEX_DOC_REVIEW so install/auth/config detection
|
|
* lives in exactly one place.
|
|
*
|
|
* Emits ONE self-contained bash block (the caller must place it in a single
|
|
* fenced block — CLAUDE.md: each block is a fresh shell, so functions sourced
|
|
* here do NOT persist to later blocks). It:
|
|
* 1. reads the `codex_reviews` master switch,
|
|
* 2. sources `gstack-codex-probe`,
|
|
* 3. runs `command -v codex` (literal — keeps the e2e substring assertion),
|
|
* then `_gstack_codex_auth_probe`, then `_gstack_codex_version_check`,
|
|
* 4. logs the relevant `_gstack_codex_log_event` for each non-ready outcome,
|
|
* 5. sets ONE canonical mode var and echoes `CODEX_MODE: <mode>` so the agent
|
|
* gates later blocks on the echoed value.
|
|
*
|
|
* Mode values: `disabled` (config off) | `not_installed` | `not_authed` | `ready`.
|
|
* The path is host-rewritten at gen-skill-docs time (pathRewrites), so the
|
|
* literal `~/.claude/skills/gstack` is correct here and becomes `$GSTACK_ROOT`
|
|
* etc. for non-Claude hosts.
|
|
*
|
|
* `disabledBehavior` controls the `disabled`-mode interpretation, which is the
|
|
* one branch that legitimately differs per caller (D1):
|
|
* - `skip-all` (plan / doc reviews): disabled means no extra review step at
|
|
* all — skip the section, no Claude fallback.
|
|
* - `codex-only` (diff adversarial): disabled gates only the Codex passes; the
|
|
* free Claude adversarial subagent still runs.
|
|
*/
|
|
export function codexPreflight(opts: { modeVar?: string; disabledBehavior: 'skip-all' | 'codex-only'; nativeReview?: boolean }): string {
|
|
const m = opts.modeVar ?? '_CODEX_MODE';
|
|
const disabledLine = opts.disabledBehavior === 'codex-only'
|
|
? 'Skip the Codex passes only; the Claude adversarial subagent below STILL runs (it is free and fast). Print: "Codex passes skipped (codex_reviews disabled) — running Claude adversarial only."'
|
|
: 'Skip this section entirely; do NOT fall back to a Claude subagent — disabled means no extra review step. Print: "Codex review skipped (codex_reviews disabled). Re-enable: `gstack-config set codex_reviews enabled`."';
|
|
const nativeRoute = opts.disabledBehavior === 'codex-only'
|
|
? 'Keep the required Claude adversarial pass; do not dispatch a duplicate.'
|
|
: 'Fall back to the Claude subagent path.';
|
|
return `\`\`\`bash
|
|
# Codex preflight: one block (functions sourced here don't persist to later blocks).
|
|
_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || echo off)
|
|
_CODEX_CFG=$(~/.claude/skills/gstack/bin/gstack-config get codex_reviews 2>/dev/null || echo enabled)
|
|
source ~/.claude/skills/gstack/bin/gstack-codex-probe 2>/dev/null || true
|
|
if [ "$_CODEX_CFG" = "disabled" ]; then
|
|
${m}="disabled"
|
|
elif { [ -n "\${CODEX_THREAD_ID:-}" ] || [ -n "\${CODEX_SANDBOX:-}" ] || [ "\${GSTACK_ACTIVE_HOST:-}" = codex ]; }; then
|
|
${m}="under_codex"
|
|
elif ! command -v codex >/dev/null 2>&1; then
|
|
${m}="not_installed"; _gstack_codex_log_event "codex_cli_missing" 2>/dev/null || true
|
|
elif ! _gstack_codex_auth_probe >/dev/null 2>&1; then
|
|
${m}="not_authed"; _gstack_codex_log_event "codex_auth_failed" 2>/dev/null || true
|
|
else
|
|
# Capture the probe's code: 2 means the CLI cannot execute at all, which is a
|
|
# different problem (and a different fix) from a model the account can't use.
|
|
_gstack_codex_model_probe; _CODEX_MP=$?${opts.nativeReview ? `
|
|
[ "$_CODEX_MP" -ne 0 ] || { _gstack_codex_model_probe review; _CODEX_MP=$?; }` : ''}
|
|
if [ "$_CODEX_MP" -eq 2 ]; then
|
|
${m}="broken_install"
|
|
elif [ "$_CODEX_MP" -ne 0 ]; then
|
|
${m}="model_unusable"
|
|
else
|
|
${m}="ready"; _gstack_codex_version_check 2>/dev/null || true
|
|
fi
|
|
fi
|
|
echo "CODEX_MODE: $${m}"
|
|
\`\`\`
|
|
|
|
Branch on the echoed \`CODEX_MODE\`:
|
|
- **\`disabled\`** — the user turned Codex reviews off (\`codex_reviews=disabled\`). ${disabledLine}
|
|
- **\`not_installed\`** — Codex CLI absent. Print: "Codex not installed; outside coverage unavailable. Install: \`npm install -g @openai/codex\`." ${nativeRoute}
|
|
- **\`under_codex\`** — stale artifact selected its own harness. Print: "Codex outside review unavailable: harness mismatch; no outside process started. Missing coverage. Repair: setup --host codex." Skip the outside invocation and follow the workflow's native-review instructions below. Conflicting inherited harness markers are not grounds to guess another provider.
|
|
- **\`not_authed\`** — installed but no credentials. Print: "Codex not authenticated; outside coverage unavailable. Run \`codex login\` or set \`$CODEX_API_KEY\`." ${nativeRoute}
|
|
- **\`broken_install\`** — the CLI is on PATH but cannot execute (spawn ENOENT, non-executable binary, missing vendor payload). Print: "Codex is installed but its binary cannot run — Codex passes skipped. Reinstall: \`npm install -g @openai/codex\`." Relay the probe's HINT lines. ${nativeRoute}
|
|
- **\`model_unusable\`** — the selected model (see \`CODEX_MODEL:\`) is invalid or unavailable to the account (#2477: HTTP 400 on every call). Relay the probe's HINT lines and the fix (\`GSTACK_CODEX_MODEL=<supported-model>\` or config.toml \`model\`); never substitute a model. ${nativeRoute} The ~10s round trip is cached for 1h; timeouts fail open to \`ready\`.
|
|
- **\`ready\`** — run the Codex pass below.`;
|
|
}
|
|
|
|
/**
|
|
* Canonical foreground-dispatch guidance (#497 → #2440 → a third recurrence at
|
|
* a /ship documentation dispatch). Claude Code v2.1.198 made Agent-tool subagents run in the
|
|
* BACKGROUND by default; a synchronous dispatch site must pass the flag
|
|
* explicitly or the parent waits on output that never arrives. Rendered via
|
|
* {{FOREGROUND_DISPATCH_NOTE}} in section templates; resolver sites may
|
|
* interpolate it directly. Same name as the placeholder for grep-ability.
|
|
*/
|
|
/** The Claude Code release that flipped Agent-tool subagents to background-by-default (#497/#2440 class). Interpolated at every RESOLVER site; three templates carry the literal inline (autoplan/sections/ceo-phase, cso, design-shotgun) — grep 'Claude Code v2.1' when bumping. */
|
|
export const CC_BACKGROUND_DEFAULT_SINCE = 'Claude Code v2.1.198';
|
|
|
|
export const FOREGROUND_DISPATCH_NOTE =
|
|
`**Foreground required:** pass \`run_in_background: false\` on the Agent call — subagents run in the background by default since ${CC_BACKGROUND_DEFAULT_SINCE}, so omitting the flag gives a background run. Dispatch through the Agent tool only: invoking the target as a Skill, or executing its workflow inline in your own context, forfeits the fresh-context isolation this dispatch exists for, even though the skill may appear in your available-skills list; the explicit flag already makes the Agent call block. (Where a step defines an inline fallback, it applies only after a dispatched subagent has failed.)`;
|