mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
+22




![dependabot[bot]](/assets/img/avatar_default.png)
![capy-ai[bot]](/assets/img/avatar_default.png)



Garry Tan
Jim McKeeth
thearbajonart
capy-ai[bot]
dependabot[bot]
DanCanadian
Nader Helmy
Nader Helmy
Zee Zheng
Jack Thomas
Jayesh Betala
techcenter68
Mattias Petersson
Arno V
0xDevNinja
Benjamin D. Smith
Brian Majewski
neallee2012
Max Hogan
maxpetrusenkoagent
katlun-lgtm
mikehasa
Briac
JackJin
EmilianoU26
frosimanuel
Tom Lam
Yotam
Jinzhe
Xianying Tan
Boyard Engels
Jey2311
8bb6d65270
* docs: fix CONTRIBUTING.md test-tiers link in TESTING_INTERNALS The link resolved relative to docs/, pointing at a nonexistent docs/CONTRIBUTING.md. Adopts #3070. Co-authored-by: thearbajonart <334330110+thearbajonart@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * ci: refresh GitHub Actions SHA pins (setup-buildx 4.4.1, build-push 7.4.0, attest 4.2.2, setup-node 7.0.0) Rewrite of dependabot #2988. Every bumped `uses:` stays pinned to the full commit SHA (verified against the upstream tags) and now carries its exact version comment, including the lines that had none. setup-node 6.5.0 -> 7.0.0 is a major bump; its notes list an ESM migration, cache outputs and removal of a dummy NODE_AUTH_TOKEN export, none of which affect our node-version-only use. actionlint 1.7.11 (CI image) and 1.7.12 are clean. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(openclaw): stop advising a per-spawn env for spawned sessions OpenClaw's sessions_spawn has no env argument (openclaw/openclaw#51654 was closed without adding one), so the documented env: { OPENCLAW_SESSION: "1" } silently did nothing. Point users at the ACP harness or Gateway environment instead and guard the old advice. Adopts #2749. Co-authored-by: DanCanadian <211164803+DanCanadian@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(benchmark): scope /benchmark routing to web page performance /benchmark claimed the bare words "benchmark" and "performance", so model and eval benchmark requests could route to the web performance skill instead of /benchmark-models. Narrow the description, Use-when list and triggers to web-specific phrases, point model/skill benchmarks at /benchmark-models, and split the root router line the same way. The catalog lead stays within the discovery budget ("Web performance ..."). Adopts #2302 and closes duplicate #2083 (rewritten on current main). Co-authored-by: Nader Helmy <31414549+creatornader@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(llms.txt): link skills by generated path, drop host-skipped skills gen-llms-txt built every skill link as <name>/SKILL.md, so the router pointed at a nonexistent gstack/SKILL.md (it is the root SKILL.md) and /claude-code pointed at a SKILL.md the Claude host never generates. Links now use the template's generated output path, and skills the Claude host skips are left out of the index. Adopts #2279. Co-authored-by: Nader Helmy <creator.nader@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(ci): install Bun from the SHA-256-verified release archive in GitLab CI and Ubicloud setup Rewrite of #1713. .gitlab-ci.yml's .setup-bun anchor and scripts/ubicloud/setup-free-suite.sh still piped https://bun.sh/install into bash. Both now download the bun-v$BUN_VERSION linux-x64 release zip, check it against the same SHA-256 Dockerfile.ci uses (#1706) before unzipping, install it to ~/.bun/bin and assert `bun --version`. test/ci-image-cli-pin.test.ts runs curlPipedToShell over both files and binds their hash and BUN_VERSION to Dockerfile.ci's amd64 arm and package.json's engines floor, so a Bun bump has to move all of them together. Verified by running the GitLab anchor in debian:stable-slim (bash and sh) and the Ubicloud block in a temp HOME. Co-authored-by: Zee Zheng <228040220+spacegeologist@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(browse): viewport auto unpins a pinned viewport without a restart `viewport WxH` pinned the page with no way back short of `browse restart`, which also drops a headed /connect-chrome session. `viewport auto` (aliases `reset`, `unpin`) now undoes the pin: - headed: the context already launches with `viewport: null`, but Playwright cannot drop a page's emulated size (and re-applies it on new frame sessions), so the active tab gets a fresh page in the same context at the same URL or load-html content. Cookies and storage persist, the tab id and ownership stay, refs reset as on navigation, and the page follows the real window again. - headless: back to the 1280x720 launch default, tracked viewport included. `auto` with --scale is rejected. Fixes #1059. Adopts #2782 and #1881. Co-authored-by: Jack Thomas <17882510+loulanyue@users.noreply.github.com> Co-authored-by: Jayesh Betala <jayesh.betala7@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(browse): screenshot accepts --clip=x,y,w,h and --selector=<css> Agent-generated commands often use the GNU `--flag=value` form, which threw "Unknown screenshot flag". Both value flags now accept it, through the same validation and conflict checks as the space-separated form. Adopts #1563. Co-authored-by: techcenter68 <75930234+techcenter68@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(benchmark): price Codex cached input at the cache rate, stop calling Gemini OAuth ready Codex reports cached_input_tokens as a subset of input_tokens, but the adapter billed all of input_tokens at the full input rate. Split them into disjoint uncached and cached counts (what estimateCostUsd expects) and show new+cached input in the table and Markdown reports. A stored Gemini OAuth file alone is now reported as not ready, with a reason naming GEMINI_API_KEY; key-based auth stays ready. Adopts the focused subset of #2283; the capacity error code and stdin change are left out (no captured failure or reproduced hang). Co-authored-by: Mattias Petersson <mattias.petersson@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(review): catch loosened-input consumers and stale user-facing strings The Enum & Value Completeness check fired only on newly added values, so a diff that widens what a validator, parser or guard accepts, and breaks an unchanged downstream consumer outside the diff, was a blind spot. Widen the check's trigger (heading unchanged) and add a consumer-grep bullet. Teach the maintainability specialist to flag user-facing strings whose guarding condition changed while the text did not. Adopts #2141 (rewritten on current main; the section heading is kept). Co-authored-by: Arno V <38716+aversini@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(global-discover): bucket Codex sessions by originator Every Codex rollout counted as one undifferentiated codex bucket, so /retro global could not tell interactive Codex Desktop work from codex_exec runs or Claude Code-driven calls. Normalize session_meta.payload.originator into desktop, exec, claude_code or other (missing, unknown and non-string values), report the split per repo as codex_originators and globally as tools.codex.originators, and print it in the summary. Existing totals are unchanged. Adopts #1488. Refs #1315. Co-authored-by: 0xDevNinja <manmit0x@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(hook-check): cover skill frontmatter hooks, sourced helpers, conflict markers and the caller's tsconfig Rewrite of #3066. bin/gstack-hook-check only listed setup's hooks plus /autoplan's by name, so the /careful and /freeze PreToolUse checks (also run by /guard and /investigate), /plan-ceo-review's hand-off hook and the helpers they source were never parse-checked before Claude Code ran them. - The hook list is setup's _hook_command_path arguments plus every gstack path on a `command:` line in a generated */SKILL.md frontmatter (9 hooks, up from 6). The generated files are read rather than the .tmpl sources because the /autoplan and /plan-ceo-review commands come from resolvers. - Each shell hook's gstack-tree helpers (`.`/`source` lines, assignments and shellcheck source= directives, followed transitively) are parsed too: careful/bin/hook-extract.sh and bin/gstack-state-root.sh for /careful and /freeze. - The shim, its helpers, each TypeScript entry and every local module the bundle used (bun build --metafile; entry only when bun lacks it) are scanned for conflict-marker lines, which still parse inside heredocs, strings and template literals. - bun build runs from the gstack root, so the caller's broken tsconfig.json or bunfig.toml no longer fails every TypeScript hook. Lists are newline separated, so a root with spaces works. Tests in test/setup-hook-syntax-gate.test.ts cover each case; the gstack-doctor fixture now copies autoplan/SKILL.md, whose frontmatter registers the hook its hooks-row test breaks. docs/troubleshooting.md describes the wider check. Co-authored-by: Benjamin D. Smith <7134745+BenjaminDSmithy@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(review): extend defensive-security framing to the security specialist and Red Team The "authorized defensive-security review" framing and fixture summary mode lived only in the adversarial subagent prompt. The security specialist and the Red Team also reason adversarially over the full diff, so repos whose test corpus is attack payloads could still get those passes refused. Move both paragraphs into scripts/resolvers/defensive-review.ts (the adversarial render stays byte-identical), state them once in the specialist dispatch for the security specialist and the Red Team, and have the Red Team prompt start from them. Testing and the other specialists keep full fixture reads; the codex host still strips Review Army. Parity caps for ship and review rise by the measured 1,380 bytes each (carve-guards notes). Adopts #1921 (rewritten on current main). Co-authored-by: Brian Majewski <1693720+bmajewski@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(team-init): make the required-mode hook shell-neutral (node CommonJS, no shell variables) Rewrite of #2217. Required team mode wrote a bash check-gstack.sh and registered it as "$CLAUDE_PROJECT_DIR/.claude/hooks/check-gstack.sh", which only works when the hook runner is a POSIX shell. Under cmd.exe or PowerShell (Windows, Copilot CLI) the variable is not expanded and the .sh cannot run, so an intended allow or deny became a hook error. - The hook is now .claude/hooks/check-gstack.cjs: the same root search (GSTACK_ROOT, every host's global root, the migrated repo), with the home directory from HOME, then USERPROFILE, then os.homedir(). It prints {} when gstack resolves and the PreToolUse deny JSON with exit 2 when not. - The registered command is `node -e "require(require('path').resolve( process.env.CLAUDE_PROJECT_DIR || '.', '.claude', 'hooks', 'check-gstack.cjs'))"`: no `$` or `%`, so sh, cmd.exe and PowerShell all pass it through unchanged. - An existing check-gstack registration (matcher Skill or Skill|skill) is replaced in place; duplicates are dropped and unrelated hooks in the same entry kept. The legacy .sh is deleted only after the settings write succeeds (a tracked one joins the suggested git add), and is kept when bun is missing or the write fails, which now reports instead of claiming success. Tests: team-mode and the hook-schema test run the command as registered; test/team-init-hook-shell-neutral.test.ts runs it through the platform's default shell (cmd.exe on the Windows lane, which curates it in) and through PowerShell when installed (verified here in the pwsh 7 container). The routing-probe check for the bash `_GSTACK_ROOT` gate now pins the same contract in the .cjs: every host root probed, block only when none resolves. Co-authored-by: neallee2012 <1499497+neallee2012@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(setup-gbrain): register the gbrain MCP through bun.exe on Windows so no console window opens Rewrite of #2007, folding #1963's MCP notes. On Windows the local stdio registration stored the bun `gbrain` binstub; gbrain.exe starts bun in a new console, so every Claude Code session opened a visible terminal for the MCP (the contributor's process trees show it; bun.exe launched directly attaches to Claude Code's headless ConPTY). Under MINGW/MSYS/Cygwin the skill now resolves BUN_ROOT from BUN_INSTALL or ~/.bun and registers `$BUN_ROOT/bin/bun.exe <global node_modules gbrain/src/cli.ts> serve`. That path also covers a `bun link`ed clone, whose global node_modules entry links to it; gstack-gbrain-install records no other clone location. When bun.exe or the entry is missing it registers `$BUN_ROOT/bin/gbrain.exe` by its full name, because Claude Code does not apply PATHEXT to a stored path (#1963). POSIX keeps the binstub line. The skill also notes that `claude mcp list` can show "Failed to connect" while a live session holds a local PGLite brain's single-writer lock (#1963). test/setup-gbrain-windows-mcp.test.ts runs the generated fence under bash with stub uname/claude and a temp HOME for MINGW64, MSYS with BUN_INSTALL, a missing entry, and Linux. It is listed as Windows-incompatible (POSIX stubs); the console behavior needs a manual Windows check. Co-authored-by: Max Hogan <max@maxhogan.dev> Co-authored-by: maxpetrusenkoagent <280464918+maxpetrusenkoagent@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(hosts): rewrite every "Agent tool" phrasing for agent-runtime hosts The Agent-tool rewrite for hermes, gbrain, openclaw and factory keyed only on "use the Agent tool", but templates mostly say "via the Agent tool", "with the Agent tool", "multiple Agent tool calls" or "Use the Agent tool", so the Claude tool name leaked into those hosts' skills and sections. Add ordered rules after the existing key (possessive, "the Agent tool", then bare "Agent tool") mapping to sessions_spawn (gbrain/openclaw), delegate_task (hermes) and delegation (factory). No goldens change. Adopts #1935 (rewritten on current main, without the new goldens). Co-authored-by: katlun-lgtm <264247399+katlun-lgtm@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(setup-deploy): Vercel auto-deploys only when connected to the Git repo /setup-deploy stated that Vercel always deploys on push, which is false for CLI-only projects with no Git integration, and its nothing-detected path did not say why such a project is invisible to detection. Qualify the Vercel trigger (ask for and record the CLI deploy command otherwise) and note that `vercel link` creates .vercel/ so a re-run detects the project. Adopts the Vercel accuracy part of #2992 (rewritten on current main); the third-party bring-up tool pointer is not adopted. Co-authored-by: mikehasa <91774998+mikehasa@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(retro): persist recommendations so the next retro detects follow-through /retro produced "3 Things to Improve" but never saved them in its JSON snapshot, so the next retro could not tell whether last week's recommendations were acted on. Step 13 now drafts the items before saving and always writes a `recommendations` array ({category, text}, [] when empty); Step 12 scores each prior recommendation addressed/partial/open against this window's commits, files and metrics and renders a follow-through line, skipping silently for snapshots without the key. The report format ties the items to the saved array. Global mode is unchanged. Adopts #1853 (rewritten on current main). Refs #1834. Co-authored-by: Jayesh Betala <jayesh.betala7@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(global-discover): count the interactive Codex CLI (codex_cli_rs) in its own cli bucket * feat(developer-profile): --reconcile backfills office-hours tenure from timeline.jsonl sessions[] is written only at office-hours Phase 4.5, so runs that ended earlier never counted toward tenure and the tier undercounted. Add a manual `--reconcile` that scans projects/*/timeline.jsonl for office-hours `completed` events and appends a count-only row ({date, mode, project_slug, signal_count: 0, signals: [], backfilled: true}) for each run with no sessions[] entry for the same project within 60 minutes, extended back by a duration_s under 8h. It is idempotent, skips malformed lines, and leaves the file untouched on a no-op. `--read` counts backfilled rows for SESSION_COUNT and TIER but takes LAST_*, DESIGN_*, CROSS_PROJECT and the nudge from logged sessions only. Not wired into the preamble yet; the office-hours greeting guard for backfill-only profiles is left to the office-hours owner. Adopts #2668 (rewritten on current main). Closes part (b) of #2657. Co-authored-by: Briac <137724549+briacSck@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(autoplan): the publication hook approves /autoplan's own Reads after the skill turn Claude Code applies a skill's allowed-tools only to the turn that invoked it. On 2.1.292 the default fork-subagent mode runs reviewers in the background, and the completion notice starts a new turn, so later Reads outside the working directory (the next phase's section file, the close packet) raised permission cards. Skill hooks stay registered for the session, and a PreToolUse "allow" skips the card (reproduced in a PTY on 2.1.292). autoplan/bin/owned-read.ts approves a parent Read only when the guard itself returned no output, and only for this installation's autoplan/sections/*.md (canonical path or the gbrain :user render) or an immutable phase artifact in <repo>/.gstack/tmp/autoplan/autoplan-<phase>-*/ whose repo is the session's project directory or contains it. Denials, unverified allows, child calls and every other path are unchanged. renderSectionBase and userRenderRoot move there with it. Tests that asserted a verified driver Read as {} now expect the approval; autoplan-owned-read covers the shipped shim, the close packet without a journal, verified artifacts through the guard, look-alikes (foreign install, writable copy, symlink alias, other repository, other names) and a guard denial staying a denial. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(autoplan): restore point and phase artifacts live in the project's .gstack/tmp/autoplan/ Skill hooks do not run inside subagents, so a reviewer launched after a background completion notice raised a card for its own input (native-prompt.md under the state root). Step 1 now puts RESTORE_PATH in the repository's git-excluded .gstack/tmp/autoplan/ (added to .git/info/exclude, mode 700), and the snapshot tool writes every phase artifact beside it, so /autoplan and its reviewers read them from the working directory without a card. The guard's bindings are unchanged: they anchor on the restore point's directory. Step 1 still prints the slug, which the old RESTORE_PATH carried. A free test runs the generated Step 1 block twice from a repository subdirectory: the restore point lands in <repo>/.gstack/tmp/autoplan/, the exclude line is written once and git status stays clean. The autoplan parity cap rises by the measured bytes (115,270 / 101,979 = 1.1303). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(evals): autoplan-guard-pty fails on any permission card after the reviewer's notice turn The scripted boundary now mirrors Step 1's layout (restore point and artifacts in the project's .gstack/tmp/autoplan/) and continues past the Phase 2 entry to dispatch the Phase 2 reviewer after the CEO reviewer's completion notice. The case asserts four verified guard allows, that the close packet Read, the Phase 2 section Read and the Phase 2 dispatch came after a task-notification turn, that the Phase 2 reviewer read its input, and that no permission card appeared at all. Still one trial on haiku, about 75-85 seconds. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(autoplan): permission cards after a background reviewer, and where /autoplan keeps its files Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(outside-review): read a trailing '— Medium.' or '(High)' severity on a finding line, the shape Codex now writes * feat(design): honor OPENAI_BASE_URL for every design API call One resolver, openaiUrl() in design/src/auth.ts, now builds every OpenAI URL the design binary, its prototype and its live model check use. Setting OPENAI_BASE_URL sends image generation and screenshot analysis to an OpenAI-compatible gateway; unset or blank keeps api.openai.com. The prototype script now goes through receiptedFetch, so its egress is receipted like the rest, and receipts already record the gateway host. A source scan keeps any design file outside auth.ts from hardcoding an OpenAI API URL again, and a request-shape test proves the gateway reaches both the vision and image endpoints. Co-authored-by: JackJin <1037461232@qq.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * review: testing specialist treats negative assertions as decisions A new assertion that something is absent records a behavior removal and needs cited intent. A rename or removal in the diff can empty a standing negative assertion so it passes forever; the specialist now greps the suite for negative assertions naming the old value. Adopted from the testing-specialist part of a community PR; the scope-check hunk targeted text that no longer exists. Co-authored-by: EmilianoU26 <314541067+EmilianoU26@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * review: security specialist checks authorization a route-guard read misses Four access-control classes where the guard is correct and the object still leaks: error pages composed outside the guard, ownership surviving a role downgrade, list/detail/download paths checked as one, and a session mutated before the identity-switch callback is validated. Adopted to review/specialists/security.md only; the /cso hunk targeted a bullet list that is now an OWASP/ASVS table. Co-authored-by: EmilianoU26 <314541067+EmilianoU26@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * review: zero findings is valid; suppress six common false positives The checklist now says a clean diff may come back clean, and suppresses framework-owned error handling, long exhaustive switches, null derefs a type guard already narrows, N+1 on fixed-cardinality loops, hardcoded test fixture values and non-cryptographic Math.random(). The contributor's "missing input validation on an internal function" and "missing await on fire-and-forget" suppressions are left out: both can hide real bugs. Co-authored-by: frosimanuel <frosimanuel@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(todos): parked contributor ideas by theme; /autoplan permission follow-ups * design-review: capture code tokens before universal principles /design-review read only DESIGN.md and otherwise fell back to universal principles, so a project whose system lives in CSS custom properties, tailwind.config or a theme file got audited and mocked up against an invented aesthetic. The skill now reads code tokens before falling back, and the target-mockup brief reuses the captured colors, fonts, radii and spacing, changing layout only. The contributor's optional microcopy checks (domain vocabulary, human register) are not adopted here. Co-authored-by: Tom Lam <tomlam0811@berkeley.edu> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(sidebar): add a terminal text-size picker to the toolbar The sidebar terminal toolbar gets an XS-XL text-size picker beside Restart. SM (13px) stays the default, so nothing changes until a user picks a size; the choice persists in chrome.storage.local and applies to the live terminal, re-fitting xterm and re-sending the PTY size two frames later so the new character cell is measured first. A VM test runs the real sidepanel-terminal.js through one auto-connect and checks the creation size, the stored-size fallback, persistence and the live resize. Co-authored-by: Yotam <yotsurfing@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(design-shotgun): generate with Codex's built-in $imagegen on Codex On the Codex host, /design-shotgun now generates and edits mockups with Codex's documented $imagegen skill in its default built-in mode (the image_gen tool), so users signed in with ChatGPT need no OPENAI_API_KEY. The design binary stays for the local comparison board only, images are published with gstack-design-claim as before, and screenshot evolution loads the captured page with view_image before an $imagegen edit. Step 3c-3d moved into a DESIGN_SHOTGUN_GENERATION resolver that returns the existing design-binary text unchanged for every other host; the Claude output differs only in two host-neutral rewordings of the evolve-from-screenshot pointers. DESIGN_SETUP and the regenerate step of the board loop gain matching Codex-only text for design-shotgun. Co-authored-by: Jinzhe <hwang.phy@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * voice: ban the "load-bearing" stock phrase Models overuse "load-bearing"; it joins the AI-vocabulary ban line in both voice tiers. The tier-2 voice directive is also tightened (the meta "compressed for runtime" tag and the bounded closer's restated exemption clause) so the shared preamble shrinks by 163 bytes per tier-2+ skill, which also pays for the reply-language rule that follows. The ship goldens are refreshed for the new render. Co-authored-by: Nader Helmy <creator.nader@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * voice: reply in the language of the user's latest message Every skill's voice directive now tells the agent to reply in the language of the user's latest message unless asked otherwise, so long English skill text no longer drags a Chinese (or any non-English) session into English. Mixed-language sessions follow the latest message. Code, commands, paths, identifiers and quoted output stay verbatim, and at tier 2+ so do the machine-read question markers (D<N>, option letters, (recommended)). The rule costs 205 bytes per tier-2+ skill and 144 per tier-1 skill. A further 25-byte cut to the bounded closer's exemption list leaves the net for this wave at +17 bytes per tier-2+ skill and +158 per tier-1 skill. review, qa and browse caps rise with measured notes; the ship goldens are refreshed. Co-authored-by: Xianying Tan <shrektan@126.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(redact-prepush): committed exact-span allowlist at .gstack-redact-allowlist The pre-push hook never fed the engine's allowlist option, so a reviewed, known-benign value (a public dev-compose URL, a documented sample key) warned or blocked every push, and the only ways out were a bypass or reshaping code until the detector stopped matching. The hook now reads the repo-root .gstack-redact-allowlist from each pushed commit (never the working tree), one exact matched span per line with # comments. Only a finding whose matched text equals an entry is suppressed: a password alone, a substring, or a different key still reports and still blocks, and engine.* unscanned findings are untouched. Every push that carries entries prints the entry count and each suppressed finding's rule and file line. Oversized files (over 64 KiB) are ignored. gstack.redact.allowEmail keeps working as before. Co-authored-by: Boyard Engels <boyard.engels@gmail.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(brain-sync): opt-in removal publishing with a bulk-removal valve gstack-brain-sync never published a removal: a deleted synced file, or the old directory after a project slug migration, stayed on the remote forever and gbrain indexed both copies. New setting artifacts_sync_removals (off by default). When on, each drain commits and pushes, receipted like every push, the removal of tracked files that are gone from disk and match the sync allowlist. Transcript pages are never removed here, and a decision log goes only together with its active snapshot. One valve: a drain that would remove more than 20 files at once publishes none and says so. `gstack-brain-sync --publish-removals` lists the pending removals and `--publish-removals --yes` publishes them on demand, whatever the setting. Co-authored-by: Jey2311 <5177215+Jey2311@users.noreply.github.com> * feat(windows): setup.ps1 checks prerequisites and runs ./setup in Git Bash A PowerShell entry point for Windows users. It checks that Git for Windows, Bun and Node.js are on PATH (printing the winget command for each missing one and changing nothing), finds Git Bash from git.exe rather than a PATH bash that may be WSL's, and runs ./setup from the checkout with every argument passed as its own argv entry to a constant bash script. No argument is interpolated into shell source. ./setup itself is unchanged. Co-authored-by: Jim McKeeth <jim@mckeeth.org> * v1.91.63.0: CHANGELOG, version * fix(ci): link bunx after the SHA-verified Bun install in GitLab CI and the Ubicloud setup * fix(redact-prepush): strip every parent column from combined-diff lines, so a merged release bump is exempt like any other * fix(hook-check): treat a Windows drive path as absolute (cygpath when present), so --list finds the skill hooks from a native caller * fix(windows): owned-read requires the read-only attribute on Windows; setup.ps1 passes arguments NUL-separated through a private file; Windows-only tests expect the owned-read approval * perf(hook-check): bundle every hook's TypeScript entries in one bun process instead of two bun starts per entry (seconds each on Windows) * perf(hook-check): read all skill frontmatter in one awk and scan conflict markers with one grep per hook (half the process starts) --------- Co-authored-by: thearbajonart <334330110+thearbajonart@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: DanCanadian <211164803+DanCanadian@users.noreply.github.com> Co-authored-by: Nader Helmy <31414549+creatornader@users.noreply.github.com> Co-authored-by: Nader Helmy <creator.nader@gmail.com> Co-authored-by: Zee Zheng <228040220+spacegeologist@users.noreply.github.com> Co-authored-by: Jack Thomas <17882510+loulanyue@users.noreply.github.com> Co-authored-by: Jayesh Betala <jayesh.betala7@gmail.com> Co-authored-by: techcenter68 <75930234+techcenter68@users.noreply.github.com> Co-authored-by: Mattias Petersson <mattias.petersson@gmail.com> Co-authored-by: Arno V <38716+aversini@users.noreply.github.com> Co-authored-by: 0xDevNinja <manmit0x@gmail.com> Co-authored-by: Benjamin D. Smith <7134745+BenjaminDSmithy@users.noreply.github.com> Co-authored-by: Brian Majewski <1693720+bmajewski@users.noreply.github.com> Co-authored-by: neallee2012 <1499497+neallee2012@users.noreply.github.com> Co-authored-by: Max Hogan <max@maxhogan.dev> Co-authored-by: maxpetrusenkoagent <280464918+maxpetrusenkoagent@users.noreply.github.com> Co-authored-by: katlun-lgtm <264247399+katlun-lgtm@users.noreply.github.com> Co-authored-by: mikehasa <91774998+mikehasa@users.noreply.github.com> Co-authored-by: Briac <137724549+briacSck@users.noreply.github.com> Co-authored-by: JackJin <1037461232@qq.com> Co-authored-by: EmilianoU26 <314541067+EmilianoU26@users.noreply.github.com> Co-authored-by: frosimanuel <frosimanuel@gmail.com> Co-authored-by: Tom Lam <tomlam0811@berkeley.edu> Co-authored-by: Yotam <yotsurfing@gmail.com> Co-authored-by: Jinzhe <hwang.phy@gmail.com> Co-authored-by: Xianying Tan <shrektan@126.com> Co-authored-by: Boyard Engels <boyard.engels@gmail.com> Co-authored-by: Jey2311 <5177215+Jey2311@users.noreply.github.com> Co-authored-by: Jim McKeeth <jim@mckeeth.org>
433 lines
23 KiB
TypeScript
433 lines
23 KiB
TypeScript
/**
|
|
* Windows-safe curation for the free suite (`--windows-only`): POSIX-only
|
|
* source patterns, explicit known-incompatible files, and force-includes for
|
|
* pattern hits that do not apply. Pinned by test/test-free-shards.test.ts and
|
|
* test/windows-native-workflows.test.ts.
|
|
*/
|
|
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
|
|
const ROOT = path.resolve(import.meta.dir, '../..');
|
|
|
|
// POSIX-only patterns that indicate a test will fail on windows-latest no
|
|
// matter how the runner shards. Codex's v1.18.0.0 review flagged the first
|
|
// three as concrete examples in the existing free suite (test/ship-version-sync.test.ts:72,
|
|
// test/helpers/providers/claude.ts:22, package.json:12). We scan the test's
|
|
// own content here so the filter stays automatic as new tests land. The
|
|
// "Windows-incompatible APIs" patterns at the bottom were added after the
|
|
// first windows-free-tests CI run surfaced concrete failure modes.
|
|
const WINDOWS_FRAGILE_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [
|
|
// Hardcoded POSIX shells / commands.
|
|
{ pattern: /['"`]\/bin\/(?:ba)?sh/, reason: 'hardcoded /bin/sh or /bin/bash' },
|
|
{ pattern: /spawnSync\(['"]sh['"],|spawn\(['"]sh['"],|exec\(['"]sh /, reason: 'spawn("sh", ...)' },
|
|
{ pattern: /['"]bash -c['"]|['"]sh -c['"]/, reason: 'bash -c / sh -c' },
|
|
{ pattern: /['"`]\/tmp\//, reason: 'raw /tmp/ path (use os.tmpdir())' },
|
|
{ pattern: /['"]chmod\b/, reason: 'chmod shell command' },
|
|
{ pattern: /['"]xargs\b/, reason: 'xargs pipeline' },
|
|
{ pattern: /\bwhich claude\b/, reason: 'which claude (use Bun.which)' },
|
|
// Windows-incompatible APIs.
|
|
{ pattern: /\.mode\s*&\s*0o[0-7]+/, reason: 'POSIX file mode bitmask (mode & 0o600 etc — Windows fakes mode bits)' },
|
|
{ pattern: /\.endsWith\(['"]\//, reason: 'hardcoded forward-slash path assertion (Windows uses \\\\)' },
|
|
{ pattern: /['"]\.\/[a-zA-Z][^"']*['"]\)\s*\.\s*toBe\(true\)/, reason: 'forward-slash path comparison' },
|
|
// Tests that spawn a bash shebang script in bin/ via spawnSync. Git Bash on
|
|
// Windows can run `bash /path/to/script` but spawnSync(scriptPath, ...)
|
|
// tries to execute the file directly via CreateProcess, which fails on the
|
|
// shebang. The pattern matches `, 'bin'` as a path-join argument (closing
|
|
// OR followed by another segment), which catches:
|
|
// - path.join(ROOT, 'bin', 'script-name') — typical
|
|
// - join(import.meta.dir, '..', 'bin', 'name') — destructured (diff-scope)
|
|
// - path.join(ROOT, 'bin') — bare BIN constant (brain-sync)
|
|
{ pattern: /,\s*['"]bin['"]\s*[,)]|['"]\.?\/?bin\/[a-z][\w-]+['"]/, reason: 'spawns bin/ shebang script (Windows CreateProcess does not parse shebangs)' },
|
|
// Tests that spawn the browse server as a subprocess via `bun run server.ts`.
|
|
// The Bun → server.ts → Playwright path is the same one that doesn't work
|
|
// on Windows (PR #1238 windows-pty-bun-pty-fix). Tests typically set
|
|
// BROWSE_HEADLESS_SKIP=1 to skip the browser launch but still need a working
|
|
// server, which they don't get on Windows.
|
|
{ pattern: /BROWSE_HEADLESS_SKIP|spawn\(\[['"]bun['"],\s*['"]run['"]/, reason: 'spawns the browse server subprocess (Bun-driven path is Windows-broken)' },
|
|
];
|
|
|
|
// Explicit known-Windows-incompatible test files that don't fit a regex
|
|
// pattern. Listed here with the precise reason. Prefer adding a pattern above
|
|
// when possible; this list is for environment-/runtime-specific tests where
|
|
// the failure mode is structural rather than detectable via source-file scan.
|
|
export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }> = [
|
|
{
|
|
file: 'test/ship-measure-seeded-fixture.test.ts',
|
|
reason: 'drives a POSIX shell stub project (`./evals.sh` with a shebang) that CreateProcess cannot exec; the measure runner itself is covered by ship-measure.test.ts with in-process fake runners',
|
|
},
|
|
{
|
|
file: 'test/qa-evidence-producer.test.ts',
|
|
reason: 'executes the registered Linux native actor and its inotify observer; portable capture and Windows job behavior are covered by qa-evidence.test.ts',
|
|
},
|
|
{
|
|
file: 'test/qa-functional-fixture.test.ts',
|
|
reason: 'executes graceful POSIX signal cancellation; Bun on Windows uses TerminateProcess and cannot run the fixture SIGTERM cleanup handler',
|
|
},
|
|
{
|
|
file: 'test/qa-functional-observer-atomic.test.ts',
|
|
reason: 'exercises real Linux inotify inode and directory watches through libc.so.6; Windows has no equivalent kernel interface',
|
|
},
|
|
{
|
|
file: 'test/docsync-report-interface.test.ts',
|
|
reason: 'executes registered native documentation callbacks with their real Linux inotify write observer before the model boundary',
|
|
},
|
|
{
|
|
file: 'test/setup-gbrain-fixture.test.ts',
|
|
reason: 'the fixture invokes real POSIX detector/verifier helpers through executable shebang wrappers',
|
|
},
|
|
{
|
|
file: 'test/setup-gbrain-windows-mcp.test.ts',
|
|
reason: 'drives the Windows MCP registration branch on POSIX by putting uname/claude shebang stubs on a colon-separated PATH and comparing POSIX path spellings; the console behavior it guards needs a manual Windows check',
|
|
},
|
|
{
|
|
file: 'test/hermetic-skills-seeding.test.ts',
|
|
reason: 'seeds the POSIX PTY skill runtime, whose embedded shell paths require a POSIX temporary root',
|
|
},
|
|
{
|
|
file: 'test/hermetic-wiring.test.ts',
|
|
reason: 'its runtime contract check seeds the POSIX PTY skill runtime; the curated Windows lane does not run that harness',
|
|
},
|
|
{
|
|
file: 'test/pty-workspace-trust.test.ts',
|
|
reason: 'launches the POSIX PTY harness with a fake executable and bound skill runtime',
|
|
},
|
|
{
|
|
file: 'test/host-config.test.ts',
|
|
reason: 'asserts "claude" binary on PATH (only true when running inside Claude Code, not on bare CI runner)',
|
|
},
|
|
{
|
|
file: 'browse/test/findport.test.ts',
|
|
reason: 'asserts Bun.serve.stop() is fire-and-forget — Bun behavior differs on Windows for this polyfill',
|
|
},
|
|
// First full run of the expanded lane (v1.66, 13 → ~258 files) surfaced
|
|
// seven POSIX-bound files the content patterns cannot see (their
|
|
// POSIX-ness is what they TEST, or arrives via a variable). Receipts:
|
|
// PR #2593 windows-free-tests run 31918591602.
|
|
{
|
|
file: 'test/codex-under-codex-detection.test.ts',
|
|
reason: 'drives the rendered preflight bash under a hardcoded POSIX PATH (/usr/bin:/bin) — bash is unreachable through that PATH on Windows, so every case sees empty output (v1.67 windows lane run 95234224148)',
|
|
},
|
|
{
|
|
file: 'test/regression-pr1169-build-app-sed.test.ts',
|
|
reason: 'tests sed escape sequences in build-app.sh — sed/bash are the subject under test',
|
|
},
|
|
{
|
|
file: 'test/setup-conductor-worktree.test.ts',
|
|
reason: 'tests ln -snf symlink semantics in the setup script — POSIX ln is the subject under test',
|
|
},
|
|
{
|
|
file: 'test/artifacts-init-migration.test.ts',
|
|
reason: 'runs a bash migration script + jq against a scaffolded git state — POSIX toolchain paths break under cmd spawn',
|
|
},
|
|
{
|
|
file: 'test/gstack-decision-semantic.test.ts',
|
|
reason: 'installs a fake gbrain SHEBANG SHIM on PATH; Windows spawn cannot exec shebang scripts',
|
|
},
|
|
{
|
|
file: 'test/question-log-hook.test.ts',
|
|
reason: 'spawns the PostToolUse hook script (bash shebang) directly; Windows spawn cannot exec it',
|
|
},
|
|
{
|
|
file: 'browse/test/browser-skills-e2e.test.ts',
|
|
reason: 'asserts forward-slash tier paths (<repo>/browser-skills/) that resolve with backslashes on Windows',
|
|
},
|
|
{
|
|
file: 'design/test/variants-retry-after.test.ts',
|
|
reason: 'wall-clock retry-timing assertions — flaky on the slow windows-latest runner even with widened bounds',
|
|
},
|
|
// Round-2 census (PR #2593 run 31919227507) after the first seven:
|
|
{
|
|
file: 'test/skill-census.test.ts',
|
|
reason: 'census walk throws at module load on Windows (skill-census.ts:63) — the skills-tree symlink layout needs Developer Mode that CI runners lack',
|
|
},
|
|
{
|
|
file: 'browse/test/browser-manager-unit.test.ts',
|
|
reason: 'wedges the shard to its wall deadline on windows-latest (in-flight at kill); needs a Windows repro to diagnose — macOS + Linux lanes cover the file',
|
|
},
|
|
// Round-3 census (PR #2593 run 31919871680): the round-2 wedge had been
|
|
// TRUNCATING its shard, so these seven only surfaced once shard 2 completed.
|
|
// All the same POSIX-environment classes: PID/cmdline identity probing,
|
|
// bash scripts as the subject under test, env-scrubbed child spawns.
|
|
{
|
|
file: 'browse/test/server-embedder-terminal-port.test.ts',
|
|
reason: 'identity-based terminal-agent kill probes PID/cmdline with POSIX semantics; teardown asserts fail on windows-latest',
|
|
},
|
|
{
|
|
file: 'design/test/daemon-discovery.test.ts',
|
|
reason: 'verifyIdentity matches a spawned daemon via /proc-style cmdline probing — POSIX identity semantics',
|
|
},
|
|
{
|
|
file: 'test/context-save-hardening.test.ts',
|
|
reason: 'bash context-save/migration scripts (HOME-unset semantics, random-suffix path) are the subject under test',
|
|
},
|
|
{
|
|
file: 'test/eval-list-cli.test.ts',
|
|
reason: 'spawns the eval:list CLI via bun with a constructed env — bun resolution fails under Windows spawn',
|
|
},
|
|
{
|
|
file: 'test/memory-cache-injection.test.ts',
|
|
reason: 'exercises hook/deny-enforcement shell scripts — POSIX toolchain is the subject under test',
|
|
},
|
|
{
|
|
file: 'test/migrations-v1.65.0.0.test.ts',
|
|
reason: 'bash migration script (bunx re-fetch, .done markers) is the subject under test',
|
|
},
|
|
{
|
|
file: 'test/question-preference-hook.test.ts',
|
|
reason: 'spawns the PreToolUse preference hook (shebang script) directly; Windows spawn cannot exec it',
|
|
},
|
|
// Round-4 census (PR #2593 run 31920052810): unhandled errors with no
|
|
// (fail) lines — attributed statically (the lane had no log artifact yet).
|
|
{
|
|
file: 'browse/test/browser-skill-commands.test.ts',
|
|
reason: 'spawnSkill spawns bun with a constructed env — bun resolution fails under Windows spawn (unhandled, no (fail) line)',
|
|
},
|
|
{
|
|
file: 'browse/test/security-audit-r2.test.ts',
|
|
reason: 'symlink-attack fixtures (evil-link) need Developer Mode CI runners lack; expect(toThrow) fires unhandled on Windows',
|
|
},
|
|
// CSO comprehensive execution is qualified only for Linux containers behind
|
|
// the POSIX watchdog and Unix-domain registry broker. Keep the portable
|
|
// static/parser contracts in the Windows lane while leaving these exact
|
|
// containment suites to the Linux and macOS gates.
|
|
{
|
|
file: 'test/cso-preparation-adversarial.test.ts',
|
|
reason: 'exercises POSIX prepared-tree and archive-cache containment for qualified Linux Docker execution, which Windows does not admit',
|
|
},
|
|
{
|
|
file: 'test/cso-preparation-container.test.ts',
|
|
reason: 'asserts POSIX permission and symlink semantics for inert exports consumed by qualified Linux Docker execution',
|
|
},
|
|
{
|
|
file: 'test/cso-preparation-executor.test.ts',
|
|
reason: 'executes the Linux Docker acquisition path and its Unix-domain registry broker; comprehensive execution is unavailable on Windows',
|
|
},
|
|
{
|
|
file: 'test/cso-verification-cleanup.test.ts',
|
|
reason: 'spawns the POSIX detached watchdog used by contained repair verification, which Windows intentionally leaves unavailable',
|
|
},
|
|
{
|
|
file: 'test/cso-witness.test.ts',
|
|
reason: 'tests the contained repair witness with POSIX private-directory and compiled-helper assumptions; comprehensive execution is unavailable on Windows',
|
|
},
|
|
{
|
|
file: 'test/shard-engine-equivalence.test.ts',
|
|
reason: 'its classification golden was recorded from the POSIX runners (process-group wall kill); the win32 engine path is pinned by the mocked-platform case in shard-engine.test.ts',
|
|
},
|
|
{
|
|
file: 'test/claude-overlay-setup-default.test.ts',
|
|
reason: 'every case runs the real ./setup under describe.skipIf(win32); its bin/ spawns live in test/helpers/claude-overlay-fixture.ts, so the source scan misses them',
|
|
},
|
|
{
|
|
file: 'test/claude-overlay-setup-installs.test.ts',
|
|
reason: 'every case runs the real ./setup under describe.skipIf(win32); its bin/ spawns live in test/helpers/claude-overlay-fixture.ts, so the source scan misses them',
|
|
},
|
|
{
|
|
file: 'test/cso-scanner-cli.test.ts',
|
|
reason: 'drives the prebuilt POSIX CSO launcher with /usr/bin/git and a POSIX-only PATH; native Windows launcher behavior is covered by the dedicated cso-windows-launcher gate',
|
|
},
|
|
];
|
|
|
|
// Force-include overrides: files a WINDOWS_FRAGILE_PATTERNS regex excludes for
|
|
// a reason that does not actually apply to them. Each entry documents WHY the
|
|
// pattern hit is a false positive — the point of these files is Windows
|
|
// coverage, so auto-excluding them defeats the regression tests they carry.
|
|
export const KNOWN_WINDOWS_SAFE: Array<{ file: string; reason: string }> = [
|
|
// Named Windows coverage for fixes whose bug only bites on Windows: kept in
|
|
// the curated lane even if a future edit trips a content pattern.
|
|
{ file: 'browse/test/cli-chain-stdin.test.ts', reason: 'piped-stdin browse chain against a stub daemon, and no-flow usage errors before any daemon boots; spawns the CLI through Bun argv' },
|
|
{ file: 'browse/test/runtime-root-server-bundle.test.ts', reason: 'a runtime root copy without node_modules resolves the checkout bundle via .source-path and refuses on version skew; os.tmpdir layouts only' },
|
|
{ file: 'test/cso-windows-docker.test.ts', reason: '/cso docker.exe discovery under known-folder roots (real path, no reparse points) and the native-transport-unsupported outcome; pure win32 path logic' },
|
|
{ file: 'test/copilot-windows-bash.test.ts', reason: 'the Copilot glossary line that runs bash blocks in Git for Windows Bash; reads host config only' },
|
|
{ file: 'test/ship-hook-windows-paths.test.ts', reason: 'runs bin/ helpers through explicit bash and Bun argv with forward-slash paths; never executes a shebang; the path-spelling simulation is skipIf win32' },
|
|
{ file: 'test/state-root-parity.test.ts', reason: 'runs the bash twin and lib/state-root.ts over an env table with PATH empty; no shebang execution, raw-string comparison is platform-neutral' },
|
|
{ file: 'test/generator-eexist.test.ts',
|
|
reason: 'E4: runs the generators through Bun argv with the Windows EEXIST emulation preload; no shebang execution' },
|
|
{ file: 'test/gstack-config-gbrain-refresh.test.ts', reason: 'E6: runs bin/gstack-config through explicit bash; its gbrain/python3 shims are found by bash PATH lookup, never launched by CreateProcess' },
|
|
{ file: 'test/hook-scripts.test.ts', reason: '#3067 PowerShell and NotebookEdit hook coverage: every hook runs through spawnSync("bash", [script]) with the payload on stdin, scratch state lives under os.tmpdir(); its /tmp/ literals are payload strings, never opened; symlink, newline-path and POSIX-PATH cases are skipIf win32' },
|
|
{ file: 'test/gstack-doctor.test.ts', reason: 'runs bin/gstack-doctor and setup --status through explicit bash argv in an os.tmpdir fixture; its bun/codex/claude/probe stubs are found by bash PATH lookup, and expected paths use bash spellings' },
|
|
{
|
|
file: 'test/qa-evidence.test.ts',
|
|
reason: 'invokes the production helper through Bun argv and exercises native Windows job cleanup, private file captures and backpressured receipt output',
|
|
},
|
|
{
|
|
file: 'test/qa-evidence-selection.test.ts',
|
|
reason: 'bin/ strings are literal dependency and Windows-selection assertions; no native actor or shebang command is launched',
|
|
},
|
|
{
|
|
file: 'test/qa-deadline.test.ts',
|
|
reason: 'launches the guard through Bun argv; mode assertions and POSIX signal cases are platform-gated, while Windows job cleanup must execute natively',
|
|
},
|
|
{
|
|
file: 'test/qa-deadline-selection.test.ts',
|
|
reason: 'bin/ strings are dependency-selection inputs; this suite never launches a shebang executable',
|
|
},
|
|
{
|
|
file: 'test/shared-libs-source-reads.test.ts',
|
|
reason: 'bin/ literal is a mocked launch assertion; actual worktree fingerprinting explicitly invokes Bash on Windows',
|
|
},
|
|
{
|
|
file: 'test/claude-code-windows-job.test.ts',
|
|
reason: 'invokes Bun directly; verifies Windows job containment at the standalone CLI boundary',
|
|
},
|
|
{
|
|
file: 'test/claude-code-runner.test.ts',
|
|
// The bin/ path is launched through process.execPath (Bun), never as a shebang; keep taskkill supervision in the Windows lane.
|
|
reason: 'invokes the runner via Bun argv; fake CLI and timeout descendant assertions cover native Windows taskkill',
|
|
},
|
|
{
|
|
file: 'test/setup-gbrain-remote-caller.test.ts',
|
|
// bin is an expected PATH component; the adapter injects the SDK boundary
|
|
// and never launches a shebang. Keep the native delimiter cases in CI.
|
|
reason: 'replays the registered SDK callback with fixture-only bin paths; covers native Windows PATH composition',
|
|
},
|
|
{
|
|
file: 'test/cso-windows-build-contract.test.ts',
|
|
// bin is a temporary staging directory. The adapter injects spawnSync;
|
|
// real PowerShell/native execution remains in cso-windows-launcher.
|
|
reason: 'replays the native build callbacks with an injected subprocess; bin paths are staging fixtures, not shebang launches',
|
|
},
|
|
{
|
|
file: 'test/setup-windows-rerun-refresh.test.ts',
|
|
// Trips the "spawns bin/ shebang script" pattern via path.join(..., 'bin',
|
|
// 'tool.sh') fixture paths, but every spawn goes through test/helpers/bash-script.ts
|
|
// (bash <tempfile>) — Git Bash executes it fine on windows-latest, with no argv-length ceiling. This file IS
|
|
// the #2444 Windows regression coverage (IS_WINDOWS=1 copy-refresh path);
|
|
// excluding it here would keep the bug class unexercised on the one
|
|
// platform it bites.
|
|
reason: 'bin/ hits are fixture path segments; spawns bash explicitly — the IS_WINDOWS=1 refresh path must run on windows-latest',
|
|
},
|
|
{
|
|
file: 'test/uninstall-windows-copies.test.ts',
|
|
// Trips the "spawns bin/ shebang script" pattern via the
|
|
// path.join(ROOT, 'bin', 'gstack-uninstall') constant, but the script is
|
|
// always spawned through spawnSync('bash', [UNINSTALL, ...]). This file
|
|
// carries the #2563 Windows real-dir-copy uninstall coverage — the bug
|
|
// ONLY reproduces on the copy install shape windows-latest exercises.
|
|
// The symlink-shape describe block self-skips on win32.
|
|
reason: 'bin/ hit is a bash-spawned script path; #2563 real-dir uninstall coverage must run on windows-latest',
|
|
},
|
|
{
|
|
file: 'browse/test/file-permissions.test.ts',
|
|
// Trips the POSIX-mode-bitmask pattern, but every `mode & 0o777` assertion
|
|
// is platform-guarded: win32-only tests return early, POSIX-only tests
|
|
// guard the bitmask behind `process.platform !== 'win32'`, and the
|
|
// symlink-skip regression test both wraps symlinkSync in try/catch
|
|
// (runners without Developer Mode can't create symlinks) and guards its
|
|
// bitmask — on win32 it asserts behavior (warns, skips, doesn't throw,
|
|
// target stays usable), never fake Windows mode bits (dirs stat 0o777
|
|
// there, so a 0o755 expectation fails on runner semantics, not our code).
|
|
// This file carries the win32-only icacls-by-SID regression tests, which
|
|
// can ONLY execute on windows-latest — excluding it here means the
|
|
// machine-account ACL lockout regression is never exercised on the one
|
|
// platform it bricks.
|
|
reason: 'every mode-bitmask assertion is guarded off win32 (behavior asserted instead); win32-only ACL regression tests must run on windows-latest',
|
|
},
|
|
{
|
|
file: 'browse/test/terminal-agent-owner-watchdog.test.ts',
|
|
// Trips the spawn(['bun','run',...]) pattern, whose reason is the
|
|
// Playwright-bound browse server. This test spawns terminal-agent.ts,
|
|
// which imports only fs/path/crypto + local helpers (no Playwright, no
|
|
// PTY at module scope) and boots under Bun on Windows — the owner-PID
|
|
// orphan leak it pins was reported on Windows (#2019).
|
|
reason: 'spawns terminal-agent (no Playwright), not the browse server; owner-orphan leak is a Windows defect',
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Pattern hits disproved by running each excluded file alone on windows-latest
|
|
* (temporary probe run 37221995077, Bun 1.4.0, Chromium installed): every case
|
|
* passed and at least one case executed. Their /tmp/ literals are fixture
|
|
* strings or platform-gated, never opened on Windows.
|
|
*/
|
|
export const WINDOWS_PROBE_SAFE: string[] = [
|
|
'browse/test/data-platform.test.ts',
|
|
'browse/test/dual-listener.test.ts',
|
|
'browse/test/error-handling.test.ts',
|
|
'browse/test/learnings-injection.test.ts',
|
|
'browse/test/pdf-flags.test.ts',
|
|
'browse/test/security-adversarial.test.ts',
|
|
'browse/test/tunnel-gate-unit.test.ts',
|
|
'browse/test/xprotect-heal.test.ts',
|
|
'design/test/feedback-roundtrip.test.ts',
|
|
'ios-qa/daemon/test/tailscale-localapi.test.ts',
|
|
'make-pdf/test/asideClient.test.ts',
|
|
'make-pdf/test/e2e/diagram-gate.test.ts',
|
|
'make-pdf/test/e2e/emoji-gate.test.ts',
|
|
'make-pdf/test/e2e/format-gate.test.ts',
|
|
'make-pdf/test/e2e/landscape-gate.test.ts',
|
|
'test/brain-preflight.test.ts',
|
|
'test/ceo-barless-submit.test.ts',
|
|
'test/ceo-split-collection.test.ts',
|
|
'test/ceo-split-question-policy.test.ts',
|
|
'test/ci-paid-coordination.test.ts',
|
|
'test/design-md.test.ts',
|
|
'test/document-skills-redaction.test.ts',
|
|
'test/evals-workflow-wiring.test.ts',
|
|
'test/gbrain-guards.test.ts',
|
|
'test/global-discover.test.ts',
|
|
'test/helpers-unit.test.ts',
|
|
'test/helpers/hermetic-env.test.ts',
|
|
'test/office-hours-review.test.ts',
|
|
'test/paid-run-manifest.test.ts',
|
|
'test/paid-shards.test.ts',
|
|
'test/plan-count-completion.test.ts',
|
|
'test/plan-design-floor-fixture.test.ts',
|
|
'test/pty-skill-seeding-wiring.test.ts',
|
|
'test/regression-issue2091-bsd-mktemp.test.ts',
|
|
'test/regression-pr1169-mktemp-fallbacks.test.ts',
|
|
'test/resolvers-gbrain-save-results.test.ts',
|
|
'test/skill-preflight-budget.test.ts',
|
|
'test/takes-fence-fallback.test.ts',
|
|
'test/terse-build.test.ts',
|
|
'test/typecheck-test-ratchet.test.ts',
|
|
];
|
|
|
|
/**
|
|
* Returns the first POSIX-only pattern hit in the file, or null if Windows-safe.
|
|
*/
|
|
export function detectWindowsFragility(absolutePath: string): { reason: string } | null {
|
|
let content: string;
|
|
try {
|
|
content = fs.readFileSync(absolutePath, 'utf-8');
|
|
} catch {
|
|
return null;
|
|
}
|
|
for (const { pattern, reason } of WINDOWS_FRAGILE_PATTERNS) {
|
|
if (pattern.test(content)) return { reason };
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export interface CurationResult {
|
|
safe: string[];
|
|
excluded: Array<{ file: string; reason: string }>;
|
|
}
|
|
|
|
export function curateWindowsSafe(files: string[], rootDir = ROOT): CurationResult {
|
|
const safe: string[] = [];
|
|
const excluded: Array<{ file: string; reason: string }> = [];
|
|
const knownBad = new Map(KNOWN_WINDOWS_INCOMPATIBLE.map((e) => [e.file, e.reason]));
|
|
const knownSafe = new Set([...KNOWN_WINDOWS_SAFE.map((e) => e.file), ...WINDOWS_PROBE_SAFE]);
|
|
for (const relativePath of files) {
|
|
const knownReason = knownBad.get(relativePath);
|
|
if (knownReason) {
|
|
excluded.push({ file: relativePath, reason: knownReason });
|
|
continue;
|
|
}
|
|
if (knownSafe.has(relativePath)) {
|
|
safe.push(relativePath);
|
|
continue;
|
|
}
|
|
const absolute = path.join(rootDir, relativePath);
|
|
const fragility = detectWindowsFragility(absolute);
|
|
if (fragility) {
|
|
excluded.push({ file: relativePath, reason: fragility.reason });
|
|
} else {
|
|
safe.push(relativePath);
|
|
}
|
|
}
|
|
return { safe, excluded };
|
|
}
|
|
|