diff --git a/bin/gstack-gbrain-sync.ts b/bin/gstack-gbrain-sync.ts index e98629718..4e3034b3a 100644 --- a/bin/gstack-gbrain-sync.ts +++ b/bin/gstack-gbrain-sync.ts @@ -1561,7 +1561,14 @@ export function parseResolvedEdges(out: string): number | null { export function classifyDreamOutcome(out: string): string | null { // The active schema pack doesn't declare the code-symbol extraction phase, so // no symbols are extracted and resolve_symbol_edges has nothing to match. - if (/does not declare this phase/i.test(out)) { + // #2341: anchor the match to a GRAPH phase. The bare phrase false-positived + // on every base-pack brain — gbrain's only emitters of "active pack does not + // declare this phase" are the CONTENT phases (extract_atoms, + // synthesize_concepts), which base packs legitimately skip while + // resolve_symbol_edges still runs and builds the graph. Matching the bare + // phrase sent users pack-churning ("switch schema packs") for nothing and + // masked real graph bugs behind a wrong diagnosis. + if (/(resolve_symbol_edges|extract_code_symbols)[^\n]*does not declare/i.test(out)) { return ( "dream ran, but this source's schema pack does not extract code symbols, " + "so the call graph stays empty. Switch this source to a code-aware schema " + diff --git a/test/gbrain-dream-stage.test.ts b/test/gbrain-dream-stage.test.ts index d53a6568c..c5f3b6814 100644 --- a/test/gbrain-dream-stage.test.ts +++ b/test/gbrain-dream-stage.test.ts @@ -160,15 +160,31 @@ describe("CLI gate wiring (dry-run subprocess — never spawns a real dream)", ( // Canned `gbrain dream` cycle logs (verbatim shapes observed against a real // 0.41.x brain). These let us test the post-flight guard WITHOUT a real cycle. const LOG = { - // Pack lacks the code-symbol phase: extract_atoms is undeclared AND the edge - // resolver matches nothing. Both signals present — pack message must win. - notCodeAware: + // #2341: the DEFAULT base packs legitimately skip the CONTENT phases + // (extract_atoms, synthesize_concepts) while resolve_symbol_edges still runs + // — gbrain's only emitters of "does not declare this phase" are those + // content phases, so the bare-phrase match fired on EVERY base-pack brain + // and told users to churn schema packs for nothing. This shape (content + // phase undeclared, resolver ran, resolved 0) must classify as the 0-edge + // outcome, not the pack-capability one. + basePackZeroEdges: "[cycle.extract] done\n" + " - extract_atoms extract_atoms: active pack does not declare this phase\n" + "[cycle.resolve_symbol_edges] start\n" + "[cycle.resolve_symbol_edges] done\n" + " ✓ resolve_symbol_edges 3864 chunk(s) walked; resolved 0, ambiguous 0, unmatched 0\n" + " totals: extracted=0 embedded=1\n", + // #2341 headline shape: base pack skips content phases AND the graph built + // fine — a healthy run that used to WARN. + basePackBuiltEdges: + " - extract_atoms extract_atoms: active pack does not declare this phase\n" + + " ✓ resolve_symbol_edges 6001 chunk(s) walked; resolved 42, ambiguous 0, unmatched 0\n" + + " - synthesize_concepts synthesize_concepts: active pack does not declare this phase\n", + // The GRAPH phase itself is undeclared: the one shape where the + // pack-capability WARN is the right diagnosis. + graphPhaseUndeclared: + " - resolve_symbol_edges resolve_symbol_edges: active pack does not declare this phase\n" + + " totals: extracted=0 embedded=1\n", // Embed phase failed for a missing key (isolated: no pack-capability line). embedFailed: "[cycle.embed] start\n" + @@ -202,8 +218,19 @@ describe("parseResolvedEdges", () => { }); describe("classifyDreamOutcome — post-flight truth guard", () => { - it("flags a non-code-aware schema pack (wins over the 0-edge signal)", () => { - const w = classifyDreamOutcome(LOG.notCodeAware); + it("base-pack content-phase skips classify as 0-edge, NOT pack-capability (#2341)", () => { + const w = classifyDreamOutcome(LOG.basePackZeroEdges); + expect(w).not.toBeNull(); + expect(w).toContain("resolved 0"); + expect(w).not.toContain("code-aware"); + }); + + it("a healthy base-pack run with a built graph is clean (#2341 headline)", () => { + expect(classifyDreamOutcome(LOG.basePackBuiltEdges)).toBeNull(); + }); + + it("flags pack capability only when the GRAPH phase itself is undeclared", () => { + const w = classifyDreamOutcome(LOG.graphPhaseUndeclared); expect(w).not.toBeNull(); expect(w).toContain("schema pack"); expect(w).toContain("code-aware");