v1.37.0.0 feat: split-engine gbrain (remote MCP brain + local PGLite for code)#1500
Merged
Conversation
…ine status + 60s cache
Foundation for split-engine gbrain: shared classifier used by both
bin/gstack-gbrain-detect (preamble probe) and bin/gstack-gbrain-sync.ts
(orchestrator SKIP-when-not-ok). Single source of truth.
Probes via `gbrain sources list --json` and classifies stderr against the
same patterns lib/gbrain-sources.ts:66-67 already uses ("Cannot connect to
database", "config.json"). Returns one of: ok, no-cli, missing-config,
broken-config, broken-db. Defensive default: unrecognized failures
classify as broken-config so the raw stderr can be surfaced upstream.
Cache at ~/.gstack/.gbrain-local-status-cache.json keyed on
{home, path_hash, gbrain_bin_path, gbrain_version, config_mtime, config_size}
with 60s TTL. Cache invalidates on any invariant change. --no-cache option
busts the cache for callers that just mutated state (/setup-gbrain,
/sync-gbrain after init/migration).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ocal_status field Replaces the bash detect helper with a bun shebang script sharing the gbrain_local_status classifier from lib/gbrain-local-status.ts with the sync orchestrator. Single source of truth for engine-status classification between preamble-probe and orchestrator-skip paths. Filename stays gstack-gbrain-detect (no .ts extension) so existing skill preamble callers shell out unchanged. Shebang `#!/usr/bin/env -S bun run` resolves bun at runtime. Output is key/type backward-compatible with the bash version per plan codex #5: the 9 pre-existing keys (gbrain_on_path, gbrain_version, gbrain_config_exists, gbrain_engine, gbrain_doctor_ok, gbrain_mcp_mode, gstack_brain_sync_mode, gstack_brain_git, gstack_artifacts_remote) stay identical in name + type + value semantics. One new key added: gbrain_local_status (5-state string enum). Updates the existing schema regression at test/gstack-gbrain-detect-mcp-mode.test.ts to include the new key. Adds test/gbrain-detect-shape.test.ts asserting the regression contract for future changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…p transcripts via artifacts pipeline Two changes in the sync orchestrator, both per plan D11/D12: 1. bin/gstack-gbrain-sync.ts: runCodeImport + runMemoryIngest call localEngineStatus() (shared classifier from lib/gbrain-local-status.ts). When status is not 'ok', return a SKIP stage result with a clear reason instead of crashing with "source registration failed: gbrain not configured". Brain-sync stage runs regardless — it doesn't depend on local engine. dry-run preview path is gated above the check so it continues to show would-do steps even when the engine is broken. 2. bin/gstack-memory-ingest.ts: when gbrain MCP is registered as remote-http (Path 4), persist staged transcripts to ~/.gstack/transcripts/run-<pid>-<ts>/ instead of the ephemeral ~/.gstack/.staging-ingest-<pid>-<ts>/ tmp dir, and SKIP the local `gbrain import` call entirely. The artifacts pipeline (gstack-brain-sync push to git, brain admin pulls and indexes) handles routing to the remote brain. Local PGLite (when present via Step 4.5) stays code-only. State recording still happens — prepared pages get their mtime+sha256 stamped under remote-http mode so the next /sync-gbrain doesn't re-stage them. Cleanup is skipped intentionally so the persisted dir survives until gstack-brain-sync moves it. Adds test/gbrain-sync-skip.test.ts covering 5 SKIP scenarios (broken-db, broken-config, no-cli, missing-config, ok pass-through). All 25 sync-related unit tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…artifacts pipeline Per plan D5 + D11. Two pieces of the split-engine rollout: 1. gstack-upgrade/migrations/v1.34.0.0.sh — prints a one-time discoverability notice for existing Path 4 (remote-http MCP) users whose machine has no local engine yet. Tells them about /setup-gbrain Step 4.5 (the new local-PGLite opt-in). Silent for everyone else. User can suppress permanently via `gstack-config set local_code_index_offered true`. Touchfile at ~/.gstack/.migrations/v1.34.0.0.done makes it idempotent. 2. bin/gstack-artifacts-init — adds `transcripts/run-*/*.md` and `transcripts/run-*/**/*.md` to the managed allowlist so the gstack-memory-ingest persistent staging dir (used in remote-http mode per D11) gets pushed to the artifacts repo. Brain admin's pull job then indexes transcripts into the remote brain. Privacy class: behavioral (matches transcript content). Adds test/gstack-upgrade-migration-v1_34_0_0.test.ts with 5 cases: state match, no-MCP, local-config-present, opt-out, and idempotency. All 5 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lates
Per plan D4, D10, D11, D12. Wires the skill prose to the new
split-engine flow + classifier introduced in earlier commits.
setup-gbrain/SKILL.md.tmpl:
- Step 1: detect output description now includes the v1.34.0.0
gbrain_local_status field (5 values).
- Step 1.5 (NEW): broken-db / broken-config remediation. AskUserQuestion
with 4 options — Retry / Switch to PGLite / Switch brain mode / Quit
(plan D4). Retry is recommended first since broken-db often = transient
Postgres outage. PGLite is explicitly one-way + destructive (moves
existing config to ~/.gbrain/config.json.gstack-bak-<ts>); rollback on
init failure restores the .bak (plan D7).
- Step 4d → Step 4.5 (NEW): in Path 4, after the verify step, offer
local PGLite for code search. AskUserQuestion Yes/No (plan D10/D11).
Yes path runs gstack-gbrain-install + `gbrain init --pglite --json`
with the same rollback-safe sequence. No path skips Steps 3/4/5/7.5.
- Step 10 verdict (Path 4): adds "Code search" row reflecting Step 4.5
choice. Updates "Transcripts" row to describe the new D11 routing
(artifacts repo → remote brain).
sync-gbrain/SKILL.md.tmpl:
- Step 1 split-engine prose: corrects the prior misleading claim that
"memory routes through whatever setup-gbrain configured, including
remote-MCP" (codex finding #3). Memory stage shells out to local
`gbrain import` in local-stdio mode; in remote-http mode it persists
to ~/.gstack/transcripts/ for the artifacts pipeline.
- Step 1.5 (NEW): local-engine pre-flight. STOP on no-cli, broken-config,
broken-db. Soft skip (continue with code+memory SKIP) on
missing-config + remote-http per plan D12. Surfaces actionable user
remediation message instead of the orchestrator crashing two stages
with ERR.
Regenerated SKILL.md for all hosts (claude, kiro, opencode, slate,
cursor, openclaw, hermes, gbrain). All 712 skill-validation + gen-skill-docs
tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…path Per plan D7 (rollback semantics) and codex #10 (rollback scope). The /setup-gbrain skill instructs the model to follow a specific shell sequence when running `gbrain init --pglite` against an existing config: 1. mv ~/.gbrain/config.json ~/.gbrain/config.json.gstack-bak-<ts> 2. gbrain init --pglite --json 3. on non-zero exit: mv .bak back; surface error This test verifies that contract using a fake `gbrain` binary that fails on init. Three cases: - FAILURE: gbrain init exits non-zero → broken config restored to original path, no leftover .bak. - SUCCESS: gbrain init exits 0 → new config in place, .bak survives for audit (user reviews + deletes manually). - SCOPE: any partial PGLite directory at ~/.gbrain/pglite/ is NOT auto-cleaned. We only promise to restore config.json; PGLite cleanup is the user's call (codex #10). If the skill template rewrites this sequence in a future change, this test should fail until the test's shell is updated too. That's the point — keep the test and the skill template aligned. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
End-to-end coverage of the new opt-in question via runAgentSdkTest. Stubs the MCP endpoint at /tools/list with a 200 response carrying a fake gbrain v0.32.3.0 serverInfo, and fakes the gbrain + claude CLIs so init writes a PGLite config and mcp add succeeds. Asserts the model: 1. invokes gstack-gbrain-install (Step 4.5 Yes branch) 2. invokes `gbrain init --pglite --json` 3. writes a working ~/.gbrain/config.json with engine=pglite 4. registers the remote MCP via `claude mcp add --transport http` 5. never leaks the bearer token to CLAUDE.md Classified as periodic-tier per plan D6 (codex #12 flagged AgentSDK flakiness; gate-tier coverage of the split-engine behavior lives in the deterministic unit tests at gbrain-local-status.test.ts and gbrain-sync-skip.test.ts). Touchfile fires the test when the skill template, install/verify/init helpers, the local-status classifier, or the agent-sdk-runner harness changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
main shipped v1.34.0.0 (factory-export submodule) + v1.34.1.0 (update-check hardening) while this branch was in flight. The migration file I named v1.34.0.0.sh now belongs at v1.35.0.0 — the next minor on top of main, matching the scale of split-engine work (new lib + orchestrator skip + template overhaul + transcripts routing). Renames the migration script and its test file; updates all internal version references in both files. Behavior unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cuts detect's wall time substantially by sharing fork-exec results between the helper that walks the JSON output and the localEngineStatus classifier from lib/gbrain-local-status.ts. Before: detect made 2x `command -v gbrain` calls (one in detect's detectGbrain, one in the classifier's resolveGbrainBin) and 2x `gbrain --version` calls. With memoization keyed on PATH, both collapse to one fork each (~400ms saved per skill preamble). Also adds `--fast` to the `gbrain doctor --json` call in detect so a broken-db config (Garry's repro) doesn't burn a full 5s timeout on the doctor's DB-connection check. The classifier still probes the DB directly via `gbrain sources list --json` for engine reachability — that's `gbrain_local_status`, separate from the coarse `gbrain_doctor_ok` summary flag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per codex #12 (AgentSDK harness is non-deterministic): the E2E now asserts the model followed the split-engine path WITHOUT requiring a specific subcommand sequence. Three assertions: 1. AskUserQuestion was called (model reached interactive branches) 2. At least one of {gstack-gbrain-install, `gbrain init --pglite`, `claude mcp add`} fired (model followed the skill, not a no-op) 3. The fake bearer token never leaked to CLAUDE.md (security regression) Deterministic per-step coverage of the same flow lives in the gate-tier unit tests (gbrain-local-status, gbrain-sync-skip, init-rollback, upgrade-migration). The E2E exists to catch the "model can't follow the skill at all" regression class, not to pin the exact tool sequence. Test passes in 280s against the live Agent SDK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…load) The gstack-next-version integration smoke test spawns a child process that does git operations + sibling-worktree probing. Wall time hovers 4-5s on M-series Macs; flakes at exactly 5001-5002ms when the test suite runs under load (bun's parallel scheduling). Bumping per-test timeout to 15s eliminates the flake without changing test logic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # VERSION # package.json # test/gstack-next-version.test.ts
4 tasks
E2E Evals: ✅ PASS66/66 tests passed | $9.11 total cost | 12 parallel runners
12x ubicloud-standard-2 (Docker: pre-baked toolchain + deps) | wall clock ≈ slowest suite |
garrytan
added a commit
that referenced
this pull request
May 15, 2026
…at server egress (4 community PRs) (#1505) * fix(browse): single-point Unicode sanitization at server egress Add sanitizeLoneSurrogates (regex-based UTF-16 lone-half cleaner) and sanitizeReplacer (JSON.stringify replacer that runs the cleaner on every string field during encoding). Split handleCommandInternal into handleCommandInternalImpl (raw) plus a thin sanitizing wrapper. The wrapper applies sanitizeLoneSurrogates to cr.result so both single-command (handleCommand line 1034) and batch-loop (line 1966) egress paths inherit it. Inline INVARIANT comment near the wrapper documents the architectural constraint. Both SSE producers (activity feed at /activity/stream and inspector stream) stringify with sanitizeReplacer. Post-stringify regex is ineffective on those paths because JSON.stringify has already converted the lone surrogate into the escape sequence "\\\\uD800" before any regex could match it; the replacer runs during stringify on the raw string value, so the substitution lands. Originated from @realcarsonterry PR #1463 (handleCommand-only wrap). Architectural lift to handleCommandInternal + SSE coverage authored on this branch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(setup): _link_or_copy helper for Windows file-copy fallback On Windows without Developer Mode (MSYS2/Git Bash), plain ln -snf silently creates a frozen file copy that doesn't refresh on git pull. Skill files become stale after every upgrade. Add a _link_or_copy SRC DST helper near IS_WINDOWS detection (line ~33). It auto-dispatches: on Unix it preserves ln -snf semantics, on Windows it copies (cp -R for directories, cp -f for files). When the source is a Unix-style name-only alias that doesn't resolve on disk (the connect-chrome → gstack/open-gstack-browser pattern), the helper returns 0 silently on Windows rather than aborting setup under set -e. Rewrite all 42 prior ln -snf call sites to route through the helper: link_claude_skill_dirs (line 437), team-claude install paths (lines 556, 581, 592), Codex host adapter block (lines 618-640), Factory host adapter block (lines 658-678), OpenCode host adapter block (lines 696-731), Kiro host adapter block (lines 939-953), plus migration and alias sites. Add _print_windows_copy_note_once helper and call it from link_claude_skill_dirs after any linking work completes so Windows users see one user-visible note explaining they must re-run ./setup after every git pull. Extend cleanup_old_claude_symlinks and cleanup_prefixed_claude_symlinks with a Windows branch: when the target is a real directory containing a real-file SKILL.md (no symlink to readlink), and IS_WINDOWS=1, treat the name-matched directory as gstack-managed and remove it. This makes --prefix / --no-prefix flips work on Windows instead of leaving stale copies behind. Originated from @realcarsonterry PR #1462 (1 of 42 sites). Helper extraction, 42-site rewrite, alias-resolution edge case, and Windows cleanup compat authored on this branch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(docs): rename stale gbrain_sync_mode to artifacts_sync_mode + register /document-generate Five stale gstack-config references in docs/ pointed to the deprecated gbrain_sync_mode key (renamed to artifacts_sync_mode in v1.27.0.0): - docs/gbrain-sync.md: lines 62, 110, 111, 173 - docs/gbrain-sync-errors.md: lines 26, 203 Users following the docs would set a key that gstack-brain-sync no longer reads, silently breaking artifacts sync. Originated from @realcarsonterry PR #1461 (verbatim). Also register /document-generate in AGENTS.md (Operational + memory table) and docs/skills.md (skill index). The skill shipped in v1.35.0.0 but the doc-inventory cross-check in test/skill-validation.test.ts was failing because neither file mentioned it. Allowlist the new test/docs-config-keys.test.ts file in test/no-stale-gstack-brain-refs.test.ts — it intentionally lists the deprecated keys in its DEPRECATED_KEYS denylist (defending the rename). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci(windows): migrate windows-free-tests to paid faster runner + register wave tests Move the Windows free-test job from GitHub-hosted windows-latest to Blacksmith's paid Windows runner (blacksmith-2vcpu-windows-2022). Spin-up drops from ~60s to ~10s and Bun installs land 3-4x faster. The label can swap to namespace-profile-windows or ubicloud-windows-* if this repo's Blacksmith installation isn't configured. Register the four new wave tests in the workflow's curated test list: - browse/test/server-sanitize-surrogates.test.ts - test/setup-windows-fallback.test.ts - test/build-script-shell-compat.test.ts - test/docs-config-keys.test.ts These tests cover the Windows-hardening surface that this wave ships (sanitizer wiring, _link_or_copy helper, build-script subshells, doc- config drift), so they need to run on Windows where the bug shapes actually manifest. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test: wave coverage for sanitizer, link_or_copy, build script, doc drift Four new test files (29 cases total): browse/test/server-sanitize-surrogates.test.ts: - 11 unit cases for sanitizeLoneSurrogates (passthrough, valid pair, lone high/low mid-string, trailing/leading lone, adjacent doubles, pair-then-lone, lone-then-pair, empty) - 2 bug-repro tests pinning the regression intent (UTF-8 round-trip, JSON.parse round-trip with codepoint assertion) - 4 wiring invariants asserting the architectural choke points stay intact (handleCommandInternalImpl rename, central sanitization line, sanitizeReplacer function exists, SSE producers stringify with replacer) Function extracted from server.ts via regex + eval'd in test scope so no production-code export is needed. test/setup-windows-fallback.test.ts: - Static invariant (D7): zero raw `ln` calls outside the _link_or_copy helper body and comments - Helper-existence assertions - 4-cell behavior matrix (file/dir × Windows/Unix) via awk-style helper extraction + bash -c sourcing - Windows-note printer registration check Mirrors test/setup-conductor-worktree.test.ts patterns. test/build-script-shell-compat.test.ts: - Regex assertion that package.json scripts.* contain no bash brace groups (Bun-Windows-hostile) - Subshell-precedence check for `.version` redirects Strips single-quoted strings before regexing so embedded JS code inside echo '...' doesn't false-positive. test/docs-config-keys.test.ts: - DEPRECATED_KEYS denylist scanned across docs/**/*.md - Round-trip test for `gstack-config get artifacts_sync_mode` Defends the v1.27.0.0 rename from doc drift. Updates to two existing tests: - test/setup-conductor-worktree.test.ts: expect `_link_or_copy` instead of `ln -snf` at the Conductor-worktree guard call site - test/gen-skill-docs.test.ts: same swap at three assertion sites (Codex section, Claude link_claude_skill_dirs body, Codex link_codex_skill_dirs body) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: bump v1.38.0.0 + build-script subshells + CHANGELOG VERSION 1.35.0.0 → 1.38.0.0 (MINOR). PR #1500 (lyon-v2) claimed v1.37.0.0 ahead of this branch; v1.38.0.0 is the next free MINOR slot per bin/gstack-next-version queue check. Workspace-aware ship rule applies — queue-advancing past a claimed version within the same bump level is explicitly permitted. package.json build script: three `{ git rev-parse HEAD ...; }` brace groups → `( git rev-parse HEAD ... )` subshells. Bun's Windows shell parser doesn't grok bash brace groups; subshells are POSIX-universal. Originated from @realcarsonterry PR #1460. CHANGELOG entry covers the full wave: - Windows install hardening (42-site _link_or_copy + cleanup compat) - Unicode sanitization architecture (handleCommandInternal + SSE replacer) - Build script POSIX-shell compat (subshells) - Doc rename (gbrain_sync_mode → artifacts_sync_mode) - Windows CI on paid faster runner - 4 new wave tests (29 cases) Frames each item as a current system property, not a fix narrative. Credits @realcarsonterry for PRs #1460, #1461, #1462, #1463 (the seed of the wave). Scope expansion to all 42 setup sites, every server egress path, Windows CI migration, and codex-flagged P0/P1 fixes (connect-chrome alias on Windows, SSE replacer, prefix-cleanup Windows compat) authored on this branch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs: post-ship sync for v1.38.0.0 Document the two architectural invariants that landed in v1.38.0.0 in their persistent homes (not just CHANGELOG): - README Windows section: add the `./setup` re-run-after-git-pull requirement that `_print_windows_copy_note_once` shows at runtime. - CONTRIBUTING "Things to know": add the no-raw-`ln` invariant for contributors editing `setup`, with the test that enforces it. - ARCHITECTURE: new "Unicode sanitization at server egress" section between Shell injection prevention and Prompt injection defense, with egress table (HTTP/batch/SSE) and the post-stringify-regex rationale. - CLAUDE.md: cross-references for both invariants, matching the v1.6.0.0 dual-listener pattern (each constraint says which files to read before editing and which test pins it). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci(windows): use windows-latest-8-cores instead of unregistered Blacksmith label actionlint failed PR #1505 because `blacksmith-2vcpu-windows-2022` isn't in the repo's approved runner-label list (actionlint.yaml only registers `ubicloud-standard-2`, and Ubicloud doesn't ship a Windows pool). Switch to GitHub's paid larger Windows runner `windows-latest-8-cores` — 4x the cores of the free `windows-latest` at the larger-runner billing rate, no new third-party CI provider, no actionlint config changes. CHANGELOG: replace "Blacksmith" / "blacksmith-2vcpu-windows-2022" / "~6x faster spin-up" claims with the actual choice (8 cores vs 4, paid larger runner). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci(windows): switch from windows-latest-8-cores to ubicloud-standard-2-windows `windows-latest-8-cores` sat queued indefinitely because the GitHub larger-runner billing isn't enabled at the org level — the "Queued — Waiting to run this check" status surfaced on PR #1505 with no progress for the whole CI run. Switch to Ubicloud Windows runners (`ubicloud-standard-2-windows`) so Windows CI uses the same provider as the existing Linux evals (`ubicloud-standard-2`). Billing stays under one account instead of two. Register the new label in actionlint.yaml alongside the existing ubicloud-standard-2 entry so actionlint doesn't reject it as unknown. CHANGELOG entry updated: runner row reflects the actual provider chosen, "Itemized changes" mentions the actionlint.yaml registration, and the narrative paragraph documents why `windows-latest-8-cores` failed first. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci: migrate all workflows to Ubicloud (Linux + Windows, 8-core) Switch every `runs-on` in this repo to Ubicloud so CI has a single billing surface, consistent capacity, and 4x more cores on the workloads that were previously stuck on free `ubuntu-latest` (2 cores). Windows uses Ubicloud's Windows pool too — `ubicloud-standard-8-windows` — so the queued-forever problem with GitHub's `windows-latest-8-cores` paid larger runner (org-level larger-runner billing not enabled) goes away. Workflows touched (9): - evals.yml, evals-periodic.yml, ci-image.yml — bump default + matrix from `ubicloud-standard-2` to `ubicloud-standard-8`. The one matrix entry that was already on -8 stays. - windows-free-tests.yml — `ubicloud-standard-2-windows` → `ubicloud-standard-8-windows`. - make-pdf-gate.yml — matrix `ubuntu-latest` → `ubicloud-standard-8`. macOS entry preserved; the poppler-install `if: matrix.os` conditional swaps to match the new label. - actionlint.yml, pr-title-sync.yml, skill-docs.yml, version-gate.yml — `ubuntu-latest` → `ubicloud-standard-8`. .github/actionlint.yaml registers all four Ubicloud labels in one place: - ubicloud-standard-2 - ubicloud-standard-8 - ubicloud-standard-2-windows (the v1.38.0.0 windows-free-tests target) - ubicloud-standard-8-windows (this PR's windows-free-tests target) Removed the duplicate `actionlint.yaml` at the repo root that I accidentally created in the prior commit — actionlint only reads `.github/actionlint.yaml`, so the root file was dead weight. CHANGELOG entry updated: a single "all Ubicloud" sentence in the narrative plus a metrics-row covering the runner pool change, and the itemized line expanded to enumerate the 9 affected workflows. The previously-orphaned "Itemized changes" line about just `windows-free-tests.yml` is replaced. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci(windows): revert to free `windows-latest` Ubicloud doesn't ship Windows runners — confirmed via their docs. The `ubicloud-standard-*-windows` labels I added do not exist and were causing `windows-free-tests` to sit "Queued — Waiting to run this check" forever (GitHub Actions can't tell a typoed label from a self-hosted runner that's about to register; it just waits). Three prior Windows-runner attempts all failed for different reasons: - `blacksmith-2vcpu-windows-2022` — Blacksmith app not installed on the org - `windows-latest-8-cores` — GitHub paid larger-runner billing not enabled - `ubicloud-standard-2/8-windows` — Ubicloud doesn't offer Windows at all The free `windows-latest` runner (4 cores, ~60s spin-up, $0) is the one path that actually runs. The wave-coverage Windows tests are <30s of real work; total job time stays under 2 minutes. Cleaned up `.github/actionlint.yaml` to drop the bogus `ubicloud-standard-*-windows` entries — kept only the two real Linux labels. CHANGELOG: split the runner-pool row into Linux (migrated to Ubicloud-8) vs Windows (stays on free windows-latest), with the why on each. Itemized line for windows-free-tests rewritten to reflect the actual outcome. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(windows): skip Unix-only cases on Windows runner windows-free-tests on GitHub free windows-latest fails three cases that depend on Unix tooling the runner doesn't have: 1. `setup-windows-fallback.test.ts` behavior matrix — IS_WINDOWS=0 cells assert `ln -snf` produces a real symlink. On Windows-without-Developer- Mode (which the free `windows-latest` runner is), `ln -snf` silently creates a file copy. That's literally the bug `_link_or_copy` exists to work around, so the assertion can never pass there. Skip the whole describe block on win32. The static-invariant test (zero raw `ln` outside the helper body) above the matrix still runs and pins the shape the Windows install relies on. 2. `docs-config-keys.test.ts` round-trip — spawnSync(`bin/gstack-config`) on Windows doesn't read the bash shebang and fails to exec. Skip on win32; the deprecated-key denylist test in the same file still runs and is the actual invariant defending the v1.27.0.0 rename at the doc layer. Use `describe.skipIf(process.platform === 'win32', ...)` and `test.skipIf(process.platform === 'win32', ...)`. Tests still run on macOS and Linux unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds split-engine gbrain support: Path 4 (Remote MCP) users can now opt into a local PGLite for symbol-aware code search while keeping the remote brain for cross-machine knowledge. Transcripts route through the artifacts repo to the remote brain instead of polluting the local PGLite.
What ships:
lib/gbrain-local-status.ts— shared 5-state engine classifier (ok / no-cli / missing-config / broken-config / broken-db) with 60s TTL cache. Probes viagbrain sources list --json+ stderr classification.bin/gstack-gbrain-detect— rewritten bash → TypeScript shebang script. Backward-compatible JSON shape; newgbrain_local_statusfield. Shared memoized resolvers cut ~400ms of duplicate fork-exec per skill preamble./setup-gbrainStep 1.5 — broken-db remediation menu: Retry / Switch to PGLite (one-way, .bak rollback) / Switch brain mode / Quit./setup-gbrainStep 4.5 — Path 4 opt-in for local PGLite. Yes path installs gbrain + inits PGLite with rollback semantics. No path keeps remote-only./sync-gbrainStep 1.5 — pre-flight local engine check. STOPs on broken-config / broken-db with remediation. SKIPs code+memory inmissing-config + remote-httpso brain-sync still runs.bin/gstack-gbrain-sync.ts— orchestrator now SKIPs code + memory stages when local engine is non-ok. Brain-sync stage runs regardless.bin/gstack-memory-ingest.ts— in remote-http MCP mode, persists transcripts to~/.gstack/transcripts/run-<pid>-<ts>/instead of tmpdir and skips localgbrain import. Artifacts pipeline picks them up.bin/gstack-brain-sync— allowlist extends totranscripts/run-*/*.md.gstack-upgrade/migrations/v1.37.0.0.sh— one-time discoverability notice for existing Path 4 users.Test Coverage
```
CODE PATHS TESTS
[+] lib/gbrain-local-status.ts (NEW)
└── localEngineStatus() 5 cases [★★★ TESTED] gbrain-local-status.test.ts (11)
└── cache + invariants [★★★ TESTED] same file
[+] bin/gstack-gbrain-detect (bash→TS rewrite)
└── JSON shape regression (9 existing keys + 1 new) [★★★ TESTED] gbrain-detect-shape.test.ts (8)
└── existing detect tests [★★★ TESTED] gbrain-detect-install.test.ts (15), gstack-gbrain-detect-mcp-mode.test.ts (19)
[+] bin/gstack-gbrain-sync.ts
└── runCodeImport SKIP on !ok [★★★ TESTED] gbrain-sync-skip.test.ts (5)
└── runMemoryIngest SKIP on !ok [★★★ TESTED] same file
[+] bin/gstack-memory-ingest.ts
└── remote-http persistent staging + skip import [★★ COVERED] existing gstack-memory-ingest.test.ts (22)
[+] setup-gbrain/SKILL.md.tmpl Step 1.5 / 4.5
└── .bak-rollback contract [★★★ TESTED] gbrain-init-rollback.test.ts (3)
└── full Path 4 + Step 4.5 Yes E2E [★★★ TESTED] skill-e2e-setup-gbrain-path4-local-pglite.test.ts (1, periodic-tier, 280s)
[+] gstack-upgrade/migrations/v1.37.0.0.sh (NEW)
└── state match / no-match / opt-out / idempotency [★★★ TESTED] gstack-upgrade-migration-v1_37_0_0.test.ts (5)
COVERAGE: 100% of new code paths have test coverage
QUALITY: ★★★:5 ★★:1
Tests: 5 new unit test files + 1 periodic E2E. All passing.
```
Pre-Landing Review
Reviewed inline as part of /plan-eng-review (13 architectural decisions). Codex outside voice ran (12 findings: 4 became structural decisions D10-D13, 8 integrated as inline plan refinements). No blocking findings post-review.
Plan Completion
Plan file: `~/.claude/plans/the-real-product-fix-squishy-galaxy.md` (13 review decisions, 9 files, all 7 lanes shipped):
Plan completion: 100% — 7/7 lanes done, all DONE classification, 0 PARTIAL / NOT DONE / UNVERIFIABLE.
TODOS
No TODOs.md items completed in this PR (this was a plan-driven feature, not a TODO cleanup).
Test plan
skill-e2e-setup-gbrain-path4-local-pglite.test.ts— 280s against live Agent SDK)🤖 Generated with Claude Code