Skip to content

fix(memory-helpers): handle gbrain >=0.25 doctor non-zero exit and schema_version 2#1418

Open
mvanhorn wants to merge 1 commit into
garrytan:mainfrom
mvanhorn:fix/1415-detect-engine-tier-schema-v2
Open

fix(memory-helpers): handle gbrain >=0.25 doctor non-zero exit and schema_version 2#1418
mvanhorn wants to merge 1 commit into
garrytan:mainfrom
mvanhorn:fix/1415-detect-engine-tier-schema-v2

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

@mvanhorn mvanhorn commented May 10, 2026

Summary

/sync-gbrain and every caller of detectEngineTier() now resolve the engine correctly when gbrain doctor exits non-zero (every Supabase brain with health_score < 100) or returns schema_version 2 output (any gbrain >=0.25). Previously these paths collapsed to engine: "unknown" and the orchestrator silently skipped all sync stages.

Why this matters

Two layered bugs both fired for the same users:

  1. execSync threw on non-zero exit. gbrain doctor exits 1 whenever health_score < 100 (a fresh Supabase install always has at least one warning, so this is essentially everyone), so the JSON written to stdout never reached the parser. See lib/gstack-memory-helpers.ts:250 (pre-fix).
  2. gbrain >=0.25 switched to schema_version: 2 and dropped the top-level engine field. parsed?.engine became undefined, falling straight to "unknown". The project recognizes the new schema elsewhere (test/skill-e2e-brain-privacy-gate.test.ts:52 already stubs {"status":"ok","schema_version":2}) but freshDetectEngineTier was missed during the gbrain >=0.25 update wave.

The v1.29.0.0 release note explicitly bumped the required gbrain version to v0.30.0+, so schema_version 2 support is a project requirement, not a nice-to-have.

Changes

  • freshDetectEngineTier calls spawnSync('gbrain', ['doctor', '--json', '--fast'], { stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000 }). The array form drops the shell wrapper while preserving the original 2>/dev/null stderr suppression. Other gstack call sites use the same shape.
  • result.stdout is parsed even when result.status is non-zero. Only spawn errors and unparseable JSON fall to "unknown".
  • A new detectEngineFromDoctorJson helper implements a defensive lookup ladder: schema_v1 (parsed.engine) first, then schema_v2 top-level renames (engine_kind, backend, engine_type), then a checks[] fallback whose name matches /engine|backend|kind/i reading value/engine/kind. The ladder errs toward "engine resolves" rather than dropping to unknown when one path exists.
  • GSTACK_DEBUG-gated stderr warning fires when JSON parses but no engine resolves, so the lookup ladder can be tightened against real-world schema_v2 output. Default behavior is unchanged.

EngineDetect schema is unchanged (schema_version: 1 from gstack's side); the on-disk cache contract is preserved.

Testing

Three new cases under describe("detectEngineTier") cover schema_v1 (existing behavior), schema_v2 healthy, and schema_v2 unhealthy with non-zero exit. The fixtures use a PATH-shim that writes a gbrain bash stub to a tmpdir, prepended in beforeEach and restored in afterEach so the shim does not leak into the cache tests at lines 296 and 305.

$ bun test test/gstack-memory-helpers.test.ts
 25 pass
 0 fail
 59 expect() calls

Fixes #1415


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

…hema_version 2

gbrain doctor exits 1 whenever health_score < 100 and schema_version 2
dropped the top-level engine field. The previous execSync wrapper threw
on non-zero exit and the parser only knew schema_version 1, so any
unhealthy Supabase brain on gbrain >=0.25 collapsed to engine=unknown
and silently skipped /sync-gbrain stages.

- Switch the gbrain doctor call to spawnSync with stdio ['ignore',
  'pipe', 'ignore'] to keep the original 2>/dev/null stderr suppression
  while reading stdout regardless of exit code.
- Add a defensive engine lookup that handles schema_version 1's
  parsed.engine, schema_version 2 top-level renames (engine_kind,
  backend, engine_type), and a checks[] fallback whose name matches
  /engine|backend|kind/i.
- When JSON parses but no engine resolves, emit a GSTACK_DEBUG-gated
  stderr warning with the payload so the lookup ladder can be tightened
  against real-world output. Default behavior is unchanged.

Tests: extend describe('detectEngineTier') with a PATH-shim that stubs
gbrain via tmpdir and afterEach restoration so the existing cache
tests at lines 296 and 305 are not affected. Three new cases cover
schema_v1, schema_v2 healthy, and schema_v2 unhealthy/non-zero exit.

bun test test/gstack-memory-helpers.test.ts: 25 pass, 0 fail.

Fixes garrytan#1415
@mvanhorn
Copy link
Copy Markdown
Contributor Author

Heads up @garrytan: the report job is failing on Post PR comment with GraphQL: Resource not accessible by integration (addComment). The workflow's GITHUB_TOKEN is missing pull_requests: write. Adding

permissions:
  contents: read
  pull-requests: write

to the report job (or the workflow) should fix it. PR body itself is fine, CI evals all pass otherwise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(gstack-gbrain-sync): engine always "unknown" when gbrain ≥0.25 is unhealthy — silently breaks /sync-gbrain for all Supabase users

1 participant