fix: report standalone brain install in serve status - #2482
Conversation
… + self-provision DB)
genie could not migrate onto autopg/pgserve v3. Three coupled defects,
all reproduced + fixed against a live autopg v3 host:
1. KEYSTONE — migrations absent from the compiled binary.
db-migrations.ts loaded SQL via readdirSync(import.meta.dir
/../db/migrations) + Bun.file(); `bun build --compile` does NOT bundle
those runtime FS reads, so the shipped binary saw ZERO migrations →
`genie db migrate` reported "Applied: 0" on an empty DB, schema never
created. Fix: scripts/gen-migrations-manifest.ts →
src/db/migrations.generated.ts (static `with { type: 'text' }` imports
Bun embeds); loader prefers the embedded set, FS scan kept as a dev
fallback; build-binary.sh regenerates pre-compile so it can't go
stale; src/types/sql.d.ts ambient for the text import.
2. autopg v3 not detected as a direct postmaster.
readPostmasterDiscovery() only read `<socketDir>/admin.json`; autopg
v3 writes live discovery to `<socketDir>/runtime.json` (admin.json
moved to ~/.autopg/). directPostmaster was always false → genie stayed
on the legacy accept-hook/`postgres` path pgserve v3 deleted. Fix:
read runtime.json (v3) then admin.json (v2), shared parse/validate.
3. No native DB provisioning. resolveDatabaseName() always returned
`postgres`, relying on the deleted pgserve-v2 accept-hook to
auto-create+route `app_<name>_<fp>`. Fix: on the direct-postmaster
path target NATIVE_DB_NAME ('genie'; GENIE_DB_NAME override) and
ensureDatabaseExists() creates it from the `postgres` maintenance DB
(allowlisted ident; duplicate_database race tolerated). `database` is
reassigned in-place so the bootstrap pool, role-cutover GRANTs and
migrations agree on the target DB (an earlier split granted the scoped
role in `postgres` while the pool ran in `genie` → "no schema has been
selected to create in"). Legacy router path unchanged.
Proven on a live autopg v3 host, freshly compiled binary, zero env hacks
/ zero manual DB creation: genie db migrate → provisioned database
"genie" → role-cutover database=genie → 63 migrations → pg-seed 132
teams → genie db = 63 migrations / 52 tables → `genie ls` reads PG
cleanly. biome + tsc clean on changed files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ple from role-cutover CodeRabbit (Major) + Codex (P1) both correct: - Native-DB provisioning was gated on `directPostmaster`, which is `cutoverEnabled && … && readPostmasterDiscovery()`. With GENIE_ROLE_CUTOVER=0 it was skipped → v3 fell back to `postgres` and re-broke (CodeRabbit). - readPostmasterDiscovery() now also matches v2 `admin.json`, so `directPostmaster` was true on pgserve v2 too → my branch retargeted v2 hosts from `postgres` to `genie`, booting them against a new empty DB (Codex P1 regression). Fix: new `hasV3RuntimeDiscovery()` — true ONLY when `<socketDir>/ runtime.json` (the v3, router-less marker; v2 publishes only admin.json) parses. Native-DB now keys off `v3NativeDb = transport.useSocket && hasV3RuntimeDiscovery()` — NOT cutoverEnabled, NOT v2 admin.json. `directPostmaster` (role-cutover) is left exactly as-is, so v2 + v3 role-cutover behavior is unchanged. Validated on a live v3 host, freshly compiled binary: - default (cutover ON): 63 migrations → `genie` DB ✓ - GENIE_ROLE_CUTOVER=0: 63 migrations → `genie` DB ✓ (no postgres fallback) - v2 (no runtime.json): v3NativeDb=false → stays on `postgres` (router path) check:fast (typecheck/lint/dead-code/skills/wishes/emit) + biome green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ration fix(db): native genie → autopg/pgserve-v3 migration (embed migrations + self-provision DB)
chore: rolling promotion dev → main (native pgserve-v3 fix + recovery runbook)
release(stable): v4.260520.1 — agents survive VM reboot
release(stable): autopg-v3 install resolver — fresh-host genie install now works
Promote dev to stable: Genie observability resume-anchor fix
Promote dev to stable: PM2 Genie install recovery
Promote dev to stable: update diagnostics cleanup
Promote dev to stable: socket transport diagnostics
Promote v4.260522.17 stable: Brain control surface fix
Promote v4.260522.19 stable: doctor PATH fidelity
fix: reduce Genie DX false positives
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request updates version metadata and enhances the serve command to report the status of the standalone 'brain' binary even when the server is not running. The printStandaloneBrainStatus function was refactored to support dependency injection for testing, and a new test case was added. Feedback was provided to include timeouts for both the external process execution and the health check fetch to prevent potential hangs in the CLI.
| function getStandaloneBrainVersion(): string | null { | ||
| try { | ||
| const resp = await fetch(`http://127.0.0.1:${config.port}/healthz`); | ||
| const result = spawnSync('brain', ['--version'], { encoding: 'utf-8' }); |
There was a problem hiding this comment.
It's a good practice to include a timeout when spawning external processes to prevent the CLI from hanging indefinitely if the command (e.g., 'brain --version') fails to respond or hangs. Hardcoded limits are acceptable here to prevent performance issues.
| const result = spawnSync('brain', ['--version'], { encoding: 'utf-8' }); | |
| const result = spawnSync('brain', ['--version'], { encoding: 'utf-8', timeout: 2000 }); |
References
- It is acceptable to use hardcoded numeric limits (magic numbers) in non-critical fallback logic, especially when they serve as intentional caps to prevent performance issues like excessive I/O.
| } | ||
| try { | ||
| const fetchHealth = deps.fetchImpl ?? fetch; | ||
| const resp = await fetchHealth(`http://127.0.0.1:${config.port}/healthz`); |
There was a problem hiding this comment.
Adding a timeout to the health check fetch prevents the 'serve status' command from hanging if the network request to the local brain server takes too long. Hardcoded limits are acceptable here to prevent performance issues.
| const resp = await fetchHealth(`http://127.0.0.1:${config.port}/healthz`); | |
| const resp = await fetchHealth('http://127.0.0.1:' + config.port + '/healthz', { signal: AbortSignal.timeout(2000) }); |
References
- It is acceptable to use hardcoded numeric limits (magic numbers) in non-critical fallback logic, especially when they serve as intentional caps to prevent performance issues like excessive I/O.
Summary
genie serve statusreportingbrain: not installedwhen the standalonebrainCLI is installed but no Brain server is runningVerification
printStandaloneBrainStatuswas not exported/implemented for this caseGENIE_TEST_SKIP_PGSERVE=1 bun test src/term-commands/serve.test.tsbunx biome check src/term-commands/serve.ts src/term-commands/serve.test.tsbun run buildbrain: installed standalone (1.64.0, server not running)