fix: two silent gbrain failures - DNS-isolated envs and dot-leading slugs - #2884
Open
Infiniteyieldai wants to merge 2 commits into
Open
Infiniteyieldai wants to merge 2 commits into
Infiniteyieldai wants to merge 2 commits into
Conversation
`gbrain sources list` fails with "Cannot connect to database: getaddrinfo ENOTFOUND" whenever the environment has no DNS at all: an agent sandbox, a restricted CI runner, an offline laptop. The classifier read that as broken-db, so /setup-gbrain Step 1.5 offered to move a perfectly healthy config.json aside and re-init the engine. Add a `network-isolated` status. On the failure path only, a control lookup decides: if a well-known public name does not resolve either, DNS is dead here and the engine's real state is unknowable, so report network-isolated; if it does resolve, the failure is specific to the configured host and the broken-db verdict stands. network-isolated is deliberately NOT timeout. The three sync stages special -case timeout to PROCEED (garrytan#1964), so reusing it would send code, memory and dream into a full walk against an unreachable DB and tell the user to raise GSTACK_GBRAIN_PROBE_TIMEOUT_MS, which cannot help. It falls through to the normal skip instead, while `--is-ok` still passes so brain-aware blocks keep rendering. GSTACK_ASSUME_NO_DNS=1|0 forces the probe either way, so the tests cover this without a network dependency. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gbrain's import walker prunes any path segment beginning with a dot, and stagedRelPath() maps a page slug 1:1 onto its path in the staging dir. So a project slug of ".claude" (minted whenever a session runs with cwd ~/.claude) staged a file gbrain never collected. The staged-vs-collected reconciliation then mismatched and the run refused to advance state - correctly, but that meant the SAME pages re-staged and failed on EVERY subsequent run. Transcript ingest never recovered on its own. Sanitize the leading dot to "dot-" at slug-build time, not in stagedRelPath: the staged path, the page slug and the slug recorded in state must stay identical, which both the reconciliation map and the resume-path reconstruction rely on. stagedRelPath now throws on a dot-leading segment as defence in depth, since disambiguateSlugs can re-pin a slug straight from state and the remote-http branch records page_slug without gbrain reconciliation. A loud failure beats a silent prune that wedges the next run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
An error occurred while submitting your PR to the queue: |
Author
Liveness proof for #2884
|
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.

Why (in my own words)
Two bugs that both bite silently, found while running
/setup-gbrainon a machine whose brain is remote Postgres.1. A sandboxed or offline environment gets told its brain is broken.
gbrain sources listfails withCannot connect to database: getaddrinfo ENOTFOUNDwhenever the environment has no DNS at all: a coding agent's sandbox, a restricted CI runner, a laptop on a plane. The classifier read that asbroken-db, which is the one verdict that routes/setup-gbraininto Step 1.5 remediation, and Step 1.5 offers to move your working~/.gbrain/config.jsonaside and re-init the engine. The brain is fine. It is simply not reachable from inside that sandbox. Offering a destructive repair for a network boundary is the wrong answer to the wrong question.2. A dot-leading project slug wedges memory ingest permanently. gbrain's import walker prunes any path segment starting with a dot, and
stagedRelPath()maps a page slug 1:1 onto its path in the staging dir. So a project slug of.claude, which gstack mints whenever a session runs with cwd~/.claude, stages a file gbrain never collects. The staged-vs-collected reconciliation mismatches and the run refuses to advance state. That refusal is correct, but the consequence is that the same pages re-stage and fail on every subsequent run. Transcript ingest never recovers on its own. Mine was stuck on 8 transcripts until I traced it.Live evidence
Bug 2: ingest wedged, then fixed. Same command, same real
~/.gstack, same cwd ($HOME), only the checkout differs. A fixture project dir~/.gstack/projects/.prfixture/supplies the dot-leading slug.Bug 1: same sandbox, same machine, different verdict.
The discrimination holds in both directions, verified on this branch with real network access:
And the sync orchestrator now skips with an accurate reason instead of pointing at a timeout knob that cannot help:
Tests. New regression file passes on this branch and cannot even link against base (
safeSlugSegmentdoes not exist there):Full affected surface (
test/*gbrain*,test/*memory-ingest*,test/*transcript*,test/code-intelligence*, new file):The single failure is
gstack-memory-ingest.test.ts > #2394 ... parity, a 5s timeout. It is pre-existing, not from this change: it fails the same way on a cleana6b3a575worktree with no patches applied, and is in fact slower there (15.9s vs 6.7s on this branch).Scope
lib/gbrain-local-status.ts: newnetwork-isolatedstatus, DNS-shape detection, memoized control lookup gated behindGSTACK_ASSUME_NO_DNS, and the status added to the existing#2520bearer-token thin-client reclassification so a remote-only brain is not mislabelled when local DNS is dead.bin/gstack-gbrain-sync.ts: entry in the exhaustive reason map. The three stage call sites special-case onlytimeoutto proceed, so the new status falls through to the normal skip with no other edit.bin/gstack-gbrain-detect:--is-okwhitelist plus the documented enum, so brain-aware blocks keep rendering in a sandbox.bin/gstack-memory-ingest.ts:safeSlugSegment()applied at slug-build time inrepoSlugandbuildArtifactPage;stagedRelPathnow throws on a dot-leading segment;buildArtifactPageexported for the test.setup-gbrain/SKILL.md.tmpl,sync-gbrain/SKILL.md.tmplplus regeneratedSKILL.mdviabun run gen:skill-docs.test/gbrain-local-status.test.ts(via a newdns-failedfake behaviour), newtest/regression-dot-leading-slug.test.ts.a6b3a575worktree and to success on this branch, on a real brain (remote Postgres, 1148 pages), inside and outside a no-DNS sandbox. Outputs pasted above.configuredEngine() === "pglite"anyway). Windows. The remote-http ingest branch, which is why thestagedRelPathguard throws rather than silently sanitizing. An egress proxy that resolves names but refuses connections is explicitly out of scope: it surfaces as ECONNREFUSED, not a DNS error, and still classifiesbroken-db.Design note on why
network-isolatedis a new status rather than reusingtimeouttimeoutlooked like the obvious fit and is wrong. It is the one non-ok status the sync orchestrator special-cases to PROCEED (#1964), so reusing it sends code, memory and dream into a full walk against an unreachable DB, andcode-intelligence/picker.tswould advertise gbrain as available in an environment where it cannot work. A distinct status falls through to the normal skip, and the exhaustiveRecord<Exclude<LocalEngineStatus, "ok">, string>in the orchestrator makes the compiler enumerate every consumer that needs a decision.Liveness proof
Screenshot attached by the human author (@Infiniteyieldai) in a follow-up comment on this PR.
Checklist
GSTACK PRtyped live into a real surface) or PR author is @garrytan (owner exemption) — attaching now, see commentGSTACK_ASSUME_NO_DNSis a test seam and manual escape hatch on an existing code patha6b3a575worktree in the Live evidence section above. Happy to file tracking issues if you would prefer them separated.