mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
Merge origin/master (v0.33.3.0 foundation) into garrytan/managua-v3
Master landed PR #934 (v0.33.3.0 code intelligence foundation: W0a
source-routing fix + W0b CLI source-scoping flip + W0c within-file
two-pass symbol resolver + W3 MCP exposure of code_callers/callees/def/
refs + pre-w0 eval harness). My branch already contained all of that
work via the original miami merge at the start of this session; the
conflicts are version-label drift (my comments said v0.33.2, master
shipped v0.33.3) and a few additive cases.
Conflict resolutions:
- VERSION / package.json: kept 0.34.0.0 (higher semver wins).
- CHANGELOG.md: both entries preserved. Order is v0.34.0.0 → v0.33.3.0 →
v0.33.2.1 → v0.33.2.0 → v0.33.1.1 → v0.33.1.0; chronologically
reasonable with newest-on-top.
- src/core/chunkers/symbol-resolver.ts (add/add): kept my version. Diff
was W1+W2 documentation block + bumped EDGE_EXTRACTOR_VERSION_TS
('2026-05-14T01:00:00Z' vs master's '2026-05-11T00:00:00Z') so the
next dream cycle re-walks every chunk and picks up qualified-name
matches from the W1 receiver-type resolution + W2 imports/references.
- src/core/cycle.ts, operations-descriptions.ts, src/commands/eval.ts,
test/core/cycle.serial.test.ts, test/e2e/cycle.test.ts, test/e2e/
dream-cycle-phase-order-pglite.test.ts: pure version-string drift
(v0.33.2 → v0.33.3 in comments). Took master's labels — that's the
shipped version number.
- src/core/operations.ts: 4 zones merged.
1. Kept my "v0.34 (Codex finding #2) sourceId resolution" comment.
2. Took master's wording on the hybridSearchCached comment (functionally
identical).
3. Kept my new code_blast + code_flow + code_traversal_cache_clear op
definitions (W3 + W3b — master doesn't have these).
4. Deduplicated the ops registration: kept master's v0.33.3 label +
my W3 + W3b ops registered alongside the foundation ones.
Verification:
- `bun run typecheck` clean
- `bun run verify` clean (all 11 pre-checks pass)
- Migrations v50→v59 schema still valid (no new master migrations in
this merge; v55-v57 search-lite + v58-v59 v0.34 already landed
pre-merge in commit f25b674f)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,14 @@ To exercise the new W1/W2 edge shapes on an existing brain:
|
||||
- contents of `~/.gbrain/upgrade-errors.jsonl` if it exists
|
||||
- which step broke
|
||||
|
||||
## [0.33.3.0] - 2026-05-12
|
||||
|
||||
**Code intelligence ships to agents. Plan-mode subagents stop falling through to grep.**
|
||||
**`code_callers`, `code_callees`, `code_def`, `code_refs` are MCP-exposed with resolver-grade descriptions.**
|
||||
|
||||
Pre-v0.33.3 the four code-intelligence commands from v0.20+ Cathedral II lived in `CLI_ONLY` at `cli.ts:30`. An agent running through MCP saw `query`/`search` but no structural retrieval, so it grepped, missed callers in string literals, shipped plans with broken call chains, and got caught in review. v0.33.3 closes that gap and lays the foundation work that v0.34 Cathedral III (recursive blast/flow + Leiden clusters + wiki) will build on top of.
|
||||
|
||||
This release was scoped after Codex's outside-voice review caught two load-bearing premise gaps in the original v0.34 plan: the call graph stored bare callee tokens (not qualified names), and source routing was already broken in `query` and `two-pass.ts`. Both are fixed here before any user-facing recursive op ships.
|
||||
## [0.33.2.1] - 2026-05-14
|
||||
|
||||
**Doc-only: name the fork-PR escape hatch so AI-authored PRs don't drop their CI secrets.**
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
# v0.33.3.0 migration — code intelligence foundation + MCP-exposed code ops
|
||||
|
||||
`gbrain upgrade` runs `gbrain apply-migrations` automatically. Most users
|
||||
won't need to do anything else. If you hit issues or want to verify the
|
||||
upgrade succeeded, run the steps below.
|
||||
|
||||
## What changed
|
||||
|
||||
v0.33.3.0 is the foundation pre-release for v0.34 Cathedral III. It ships the
|
||||
agent-facing MCP surface for the v0.20+ tree-sitter call graph plus the
|
||||
foundation fixes needed before recursive blast/flow and Leiden clusters
|
||||
land in v0.34.
|
||||
|
||||
**MCP exposure (NEW for agents):**
|
||||
|
||||
Four code-intelligence ops graduated from `CLI_ONLY` to first-class MCP ops:
|
||||
|
||||
- `code_callers(symbol, [limit, source_id, all_sources])` — find every
|
||||
caller of a symbol. Use BEFORE editing any function.
|
||||
- `code_callees(symbol, [limit, source_id, all_sources])` — trace what a
|
||||
function calls. Use when debugging unexpected behavior.
|
||||
- `code_def(symbol, [limit, lang])` — find a symbol's definition site(s).
|
||||
- `code_refs(symbol, [limit, lang])` — find every reference (comments,
|
||||
imports, type annotations, call sites).
|
||||
|
||||
The MCP tool descriptions are resolver-grade — they tell agents WHEN to
|
||||
reach for each op so plan-mode subagents route to structural retrieval
|
||||
instead of falling through to text search.
|
||||
|
||||
**Foundation fixes:**
|
||||
|
||||
- **Source-routing fix** (Codex finding #2): `query` op now threads
|
||||
`ctx.sourceId` to `hybridSearch`. Two-pass retrieval honors `sourceId`
|
||||
at both the `nearSymbol` lookup and unresolved-edge resolution sites.
|
||||
Multi-source brains stop cross-contaminating structural retrieval.
|
||||
- **CLI source-scoping default flipped** (Codex finding #7): `gbrain
|
||||
code-callers <symbol>` without `--source` now resolves to your brain's
|
||||
default source (the only source on single-source brains; explicit
|
||||
error listing valid ids on multi-source brains). Pre-v0.33 the
|
||||
default silently was global — multi-repo brains cross-resolved every
|
||||
same-named symbol.
|
||||
|
||||
**Within-file two-pass symbol resolution:**
|
||||
|
||||
A new cycle phase `resolve_symbol_edges` runs after `extract` on every
|
||||
autopilot tick. It walks `content_chunks.edges_backfilled_at IS NULL`
|
||||
chunks in 200-row batches, matching each `to_symbol_qualified` against
|
||||
the same-file `symbol_name_qualified`, and writes the outcome to
|
||||
`code_edges_symbol.edge_metadata`:
|
||||
|
||||
- `{resolved_chunk_id: N}` — one unambiguous match
|
||||
- `{ambiguous: true, candidates: [...]}` — 2+ matches in the same file
|
||||
- (no metadata change) — zero matches; caller's two-pass walk handles
|
||||
cross-file resolution
|
||||
|
||||
## Schema migration v51 — what it adds
|
||||
|
||||
```sql
|
||||
ALTER TABLE content_chunks ADD COLUMN IF NOT EXISTS edges_backfilled_at TIMESTAMPTZ;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_code_edges_symbol_resolver
|
||||
ON code_edges_symbol (source_id, to_symbol_qualified);
|
||||
CREATE INDEX IF NOT EXISTS idx_content_chunks_symbol_lookup
|
||||
ON content_chunks (page_id, symbol_name_qualified)
|
||||
WHERE symbol_name_qualified IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_content_chunks_edges_backfill
|
||||
ON content_chunks (edges_backfilled_at)
|
||||
WHERE edges_backfilled_at IS NULL;
|
||||
```
|
||||
|
||||
All idempotent. Existing brains see the column + indexes added; the resolver
|
||||
walks the corpus lazily over the next several autopilot cycles.
|
||||
|
||||
**First-run cost:** ~5-15 minutes of background resolver work on a 10K-chunk
|
||||
brain spread across cycles (~2000 chunks/tick). No user-visible interruption
|
||||
unless you watch `gbrain doctor`'s autopilot stats.
|
||||
|
||||
## What the agent should do
|
||||
|
||||
If you're an agent (Claude Code, OpenClaw, Cursor) running through MCP:
|
||||
|
||||
1. **Before editing any function**, reach for `code_callers` to surface
|
||||
every caller. The MCP tool description tells you this — follow it.
|
||||
2. **When tracing execution**, reach for `code_callees` from the entry
|
||||
point.
|
||||
3. **When looking up a definition**, reach for `code_def` not `search`.
|
||||
It returns line numbers + snippet directly.
|
||||
4. **When planning a rename**, reach for `code_refs` to find every
|
||||
literal mention.
|
||||
5. **Honor source scoping**: multi-source brains require explicit
|
||||
`source_id` or `all_sources: true`. Single-source brains auto-resolve.
|
||||
|
||||
The previously available CLI commands (`gbrain code-callers <symbol>`)
|
||||
still work but now default to source-scoped on multi-source brains.
|
||||
Pass `--all-sources` to get the pre-v0.33 cross-source default.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# 1. Confirm upgrade
|
||||
gbrain --version # 0.33.3.0
|
||||
|
||||
# 2. Confirm migration v51 ran
|
||||
gbrain apply-migrations --status | grep edges_backfilled_at_v0_34
|
||||
|
||||
# 3. Confirm the new MCP ops are exposed
|
||||
gbrain --tools-json | jq '.[] | select(.name | startswith("code_")) | .name'
|
||||
# Should show: code_callers, code_callees, code_def, code_refs
|
||||
|
||||
# 4. Try one end-to-end (replace `parseMarkdown` with any symbol you index)
|
||||
gbrain code-callers parseMarkdown --json | jq '.count'
|
||||
|
||||
# 5. (Optional) inspect the resolver's per-source progress
|
||||
gbrain doctor --json | jq '.checks | to_entries[] | select(.key | contains("code"))'
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
If you need to roll back: `git checkout` an older binary and run
|
||||
`gbrain doctor`. The v51 column + indexes stay in your DB (no-op for
|
||||
older binaries; idempotent re-add on upgrade). No data is destroyed.
|
||||
|
||||
## What v0.34 will add on top
|
||||
|
||||
Per the v0.34 design doc (`/Users/garrytan/.gstack/projects/garrytan-gbrain/garrytan-garrytan-miami-design-20260510-194145.md`):
|
||||
|
||||
- Recursive `code_blast` / `code_flow` MCP ops on top of the resolved graph
|
||||
- Leiden community detection (`code_clusters_list`, `code_cluster_get`) +
|
||||
inline mermaid diagrams
|
||||
- `gbrain wiki` zero-LLM aggregator CLI
|
||||
- Per-op graph-traversal eval metrics extending v0.25.0's eval-capture
|
||||
- `imports` and `references` edge types for JS/TS/TSX + Python
|
||||
- Receiver-type scope walkers (e.g. `obj.method()` → `Class.method`)
|
||||
@@ -46,7 +46,7 @@ export async function runEvalCommand(engine: BrainEngine, args: string[]): Promi
|
||||
process.exit(await runEvalCrossModal(args.slice(1)));
|
||||
}
|
||||
if (sub === 'code-retrieval') {
|
||||
// v0.33.2 pre-w0 — code-retrieval baseline / gate harness. Needs a brain
|
||||
// v0.33.3 pre-w0 — code-retrieval baseline / gate harness. Needs a brain
|
||||
// for the baseline (BaselineStrategy calls hybridSearch); --compare
|
||||
// mode reads JSON only but the engine is already connected by this
|
||||
// dispatcher.
|
||||
|
||||
+5
-5
@@ -71,7 +71,7 @@ export const ALL_PHASES: CyclePhase[] = [
|
||||
// The empty-fence guard refuses to run if pre-v51 legacy facts are
|
||||
// pending the v0_32_2 backfill (Codex R2-#7).
|
||||
'extract_facts',
|
||||
// v0.33.2 W0c — within-file two-pass symbol resolution. Runs AFTER
|
||||
// v0.33.3 W0c — within-file two-pass symbol resolution. Runs AFTER
|
||||
// extract + extract_facts so any code edges sync emitted (still bare-token)
|
||||
// get resolved into {resolved_chunk_id: N} / {ambiguous: true,
|
||||
// candidates: [...]} edge_metadata entries before downstream phases read
|
||||
@@ -112,7 +112,7 @@ const NEEDS_LOCK_PHASES: ReadonlySet<CyclePhase> = new Set([
|
||||
'extract',
|
||||
// v0.32.2 — wipes + re-inserts facts per affected page.
|
||||
'extract_facts',
|
||||
// v0.33.2 W0c — writes code_edges_symbol.edge_metadata + content_chunks.edges_backfilled_at.
|
||||
// v0.33.3 W0c — writes code_edges_symbol.edge_metadata + content_chunks.edges_backfilled_at.
|
||||
'resolve_symbol_edges',
|
||||
'patterns',
|
||||
// v0.29 — writes pages.emotional_weight column.
|
||||
@@ -731,13 +731,13 @@ async function runPhaseExtractFacts(
|
||||
}
|
||||
|
||||
/**
|
||||
* v0.33.2 W0c — resolve_symbol_edges phase.
|
||||
* v0.33.3 W0c — resolve_symbol_edges phase.
|
||||
*
|
||||
* Walks at most BATCH_SIZE*10 chunks per invocation where
|
||||
* `edges_backfilled_at` is NULL or older than EDGE_EXTRACTOR_VERSION_TS.
|
||||
* Resumable across cycles via the watermark; quick-cycle compatible.
|
||||
*
|
||||
* Source scoping: walks every registered source. Pre-v0.33.2 silently
|
||||
* Source scoping: walks every registered source. Pre-v0.33.3 silently
|
||||
* crossed sources; now each source is walked independently so symbol
|
||||
* resolution stays within its source boundary (matches the W0a fix).
|
||||
*/
|
||||
@@ -1169,7 +1169,7 @@ export async function runCycle(
|
||||
await safeYield(opts.yieldBetweenPhases);
|
||||
}
|
||||
|
||||
// ── v0.33.2 W0c: resolve_symbol_edges (between extract_facts + patterns) ──
|
||||
// ── v0.33.3 W0c: resolve_symbol_edges (between extract_facts + patterns) ──
|
||||
// Walks chunks whose edges_backfilled_at is null/stale. Resumable
|
||||
// across cycles via the watermark. Quick-cycle compatible — caps at
|
||||
// BATCH_SIZE * 10 chunks per invocation so a 60s watchdog tick stays
|
||||
|
||||
@@ -2969,6 +2969,55 @@ export const MIGRATIONS: Migration[] = [
|
||||
ON search_telemetry (date DESC);
|
||||
`,
|
||||
},
|
||||
{
|
||||
version: 58,
|
||||
name: 'edges_backfilled_at_v0_33_3',
|
||||
// v0.33.3 W0c — resumable symbol-resolution backfill watermark.
|
||||
// (Originally claimed v55; renumbered to v58 on merge with master's
|
||||
// v55/v56/v57 search-lite migrations.)
|
||||
//
|
||||
// The within-file two-pass resolver (src/core/chunkers/symbol-resolver.ts)
|
||||
// walks every content_chunks row that has unresolved edges
|
||||
// (rows in code_edges_symbol whose to_symbol_qualified has not been
|
||||
// matched against same-file symbol_name_qualified yet) and writes the
|
||||
// resolution outcome to code_edges_symbol.edge_metadata. On a 96K-chunk
|
||||
// brain that is a 5-15 minute backfill the first time it runs.
|
||||
//
|
||||
// `edges_backfilled_at` is the resume watermark. Backfill runs in
|
||||
// 200-chunk batches; on batch success the column is set to NOW() for
|
||||
// every chunk in the batch. Resume picks up chunks where the watermark
|
||||
// is NULL or older than EDGE_EXTRACTOR_VERSION_TS (a constant bumped
|
||||
// when the extractor's shape changes). Crashes lose at most one batch.
|
||||
//
|
||||
// Composite + partial indexes for the lookup hot path (D11 from eng
|
||||
// review):
|
||||
// - idx_code_edges_symbol_resolver (source_id, to_symbol_qualified)
|
||||
// — every code_edges_symbol row is unresolved by construction
|
||||
// (the table has no to_chunk_id column; that lives on code_edges_chunk).
|
||||
// This composite index supports the resolver's per-source lookups.
|
||||
// - idx_content_chunks_symbol_lookup (page_id, symbol_name_qualified)
|
||||
// WHERE symbol_name_qualified IS NOT NULL — file-batched lookup
|
||||
// used by both the resolver and the cluster recompute phase (W4-5).
|
||||
// - idx_content_chunks_edges_backfill (edges_backfilled_at)
|
||||
// WHERE edges_backfilled_at IS NULL — find unresumed rows quickly.
|
||||
//
|
||||
// Idempotent: IF NOT EXISTS on column + indexes. Backfill itself runs
|
||||
// separately via the resolve_symbol_edges cycle phase.
|
||||
sql: `
|
||||
ALTER TABLE content_chunks ADD COLUMN IF NOT EXISTS edges_backfilled_at TIMESTAMPTZ;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_code_edges_symbol_resolver
|
||||
ON code_edges_symbol (source_id, to_symbol_qualified);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_content_chunks_symbol_lookup
|
||||
ON content_chunks (page_id, symbol_name_qualified)
|
||||
WHERE symbol_name_qualified IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_content_chunks_edges_backfill
|
||||
ON content_chunks (edges_backfilled_at)
|
||||
WHERE edges_backfilled_at IS NULL;
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
export const LATEST_VERSION = MIGRATIONS.length > 0
|
||||
|
||||
@@ -91,8 +91,8 @@ export const FIND_CONTRADICTIONS_DESCRIPTION =
|
||||
"`gbrain eval suspected-contradictions` for that.";
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
// v0.33.2 Cathedral III foundation — code-intelligence ops (MCP-exposed).
|
||||
// Pre-v0.33.2 the callers/callees/def/refs commands were CLI-only — agents
|
||||
// v0.33.3 Cathedral III foundation — code-intelligence ops (MCP-exposed).
|
||||
// Pre-v0.33.3 the callers/callees/def/refs commands were CLI-only — agents
|
||||
// reached for grep because the MCP surface didn't expose them. These
|
||||
// descriptions are resolver-grade so the LLM tool-selection prompt routes
|
||||
// plan-mode questions straight to the right op.
|
||||
|
||||
@@ -1118,9 +1118,9 @@ const query: Operation = {
|
||||
? undefined
|
||||
: sourceIdParam
|
||||
: ctx.sourceId;
|
||||
// v0.32.x search-lite: route through hybridSearchCached so semantic cache
|
||||
// + token budget + intent weighting fire automatically. Plain hybridSearch
|
||||
// remains the bare API for callers that opt out.
|
||||
// v0.32.x search-lite: route the query op through hybridSearchCached so
|
||||
// semantic cache + token budget + intent weighting fire automatically.
|
||||
// Plain hybridSearch remains the bare API for callers that opt out.
|
||||
const results = await hybridSearchCached(ctx.engine, queryText, {
|
||||
limit: (p.limit as number) || 20,
|
||||
offset: (p.offset as number) || 0,
|
||||
@@ -3079,7 +3079,7 @@ export const operations: Operation[] = [
|
||||
find_contradictions,
|
||||
// v0.33: expertise + relationship-proximity routing
|
||||
find_experts,
|
||||
// v0.33.2: Cathedral III code-intelligence (MCP-exposed; were CLI_ONLY pre-v0.33.2)
|
||||
// v0.33.3: Cathedral III code-intelligence (MCP-exposed; were CLI_ONLY pre-v0.33.3)
|
||||
code_callers, code_callees, code_def, code_refs,
|
||||
// v0.34 W3: recursive code_blast + code_flow
|
||||
code_blast, code_flow,
|
||||
|
||||
@@ -381,7 +381,7 @@ describe('runCycle — yieldBetweenPhases hook', () => {
|
||||
// v0.29: 10 phases (added `recompute_emotional_weight`).
|
||||
// v0.31: 11 phases (added `consolidate` between recompute and embed).
|
||||
// v0.32.2: 12 phases (added `extract_facts` between extract and patterns).
|
||||
// v0.33.2: 13 phases (added `resolve_symbol_edges` between extract_facts and patterns) → 13 yield calls.
|
||||
// v0.33.3: 13 phases (added `resolve_symbol_edges` between extract_facts and patterns) → 13 yield calls.
|
||||
expect(hookCalls).toBe(13);
|
||||
});
|
||||
|
||||
@@ -392,7 +392,7 @@ describe('runCycle — yieldBetweenPhases hook', () => {
|
||||
throw new Error('synthetic hook error');
|
||||
},
|
||||
});
|
||||
// v0.33.2: 13 phases (v0.32.2's 12 + resolve_symbol_edges).
|
||||
// v0.33.3: 13 phases (v0.32.2's 12 + resolve_symbol_edges).
|
||||
expect(report.phases.length).toBe(13);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,7 +104,7 @@ describeE2E('E2E: runCycle against real Postgres', () => {
|
||||
// v0.29 = 10 (added `recompute_emotional_weight` between patterns and embed)
|
||||
// v0.31 = 11 (added `consolidate` between recompute_emotional_weight and embed)
|
||||
// v0.32.2 = 12 (added `extract_facts` between extract and patterns)
|
||||
// v0.33.2 = 13 (added `resolve_symbol_edges` between extract_facts and patterns)
|
||||
// v0.33.3 = 13 (added `resolve_symbol_edges` between extract_facts and patterns)
|
||||
expect(report.phases.length).toBe(13);
|
||||
|
||||
// Nothing got written.
|
||||
|
||||
@@ -105,7 +105,7 @@ const EXPECTED_PHASES: CyclePhase[] = [
|
||||
'synthesize',
|
||||
'extract',
|
||||
'extract_facts', // v0.32.2 — reconcile fence → DB facts index
|
||||
'resolve_symbol_edges', // v0.33.2 — within-file symbol resolution
|
||||
'resolve_symbol_edges', // v0.33.3 — within-file symbol resolution
|
||||
'patterns',
|
||||
'recompute_emotional_weight', // v0.29
|
||||
'consolidate', // v0.31
|
||||
|
||||
Reference in New Issue
Block a user