mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-28 06:23:01 +00:00
Five verified-open fixes to the dream/synthesize output family: - #1586: thread the cycle's resolved sourceId (cycleSourceId) through runPhaseSynthesize -> SubagentHandlerData.source_id -> subagent tool OperationContext, and stamp collected refs + summary page with the same source, so synthesized pages stop landing in 'default'. - #2415: new config knob dream.synthesize.output_root (default 'wiki', zero behavior change unless set) drives the synthesize prompt slug templates, the patterns reflection lookup + prompt, and remaps the filing-rule allow-list globs. Registered in KNOWN_CONFIG_KEYS. - #2163: synthesize_concepts writes concept pages through importFromContent (put_page's parse->chunk->embed pipeline) instead of bare engine.putPage, so concepts/ pages are chunked + embedded and reachable by retrieval. - #2569: stampDreamProvenance persists dream_generated + dream_cycle_date into pages.frontmatter (JSONB merge via executeRawJsonb) at write time, so generated pages are DB-queryable and put_page write-through can't erase the marker. - #2606: chronicle judge detects stopReason 'length' truncation and no-JSON-array parse failures as distinct skipped reasons (judge_truncated / judge_parse_failed) instead of a false terminal no_events; output cap raised to 4000 and configurable via chronicle.judge_max_tokens. Co-authored-by: Sinabina <sinabina@Sinabinas-MacBook-Pro-4.local> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Garry Tan <garrytan@gmail.com>
93 lines
3.6 KiB
TypeScript
93 lines
3.6 KiB
TypeScript
/**
|
|
* Unit tests for the patterns phase (v0.21).
|
|
*
|
|
* The phase invokes a subagent and queues real Minions work, so this
|
|
* file leans on structural assertions over the source + a single
|
|
* end-to-end driver run that exercises the skip-paths.
|
|
*
|
|
* Full LLM behavior is exercised by E2E tests in test/e2e/.
|
|
*/
|
|
|
|
import { describe, test, expect } from 'bun:test';
|
|
import { readFileSync } from 'fs';
|
|
|
|
const patternsSrc = readFileSync(
|
|
new URL('../src/core/cycle/patterns.ts', import.meta.url),
|
|
'utf-8',
|
|
);
|
|
|
|
describe('patterns phase wiring', () => {
|
|
test('imports queue + waitForCompletion + types', () => {
|
|
expect(patternsSrc).toContain("import { MinionQueue }");
|
|
expect(patternsSrc).toContain('waitForCompletion');
|
|
expect(patternsSrc).toContain('SubagentHandlerData');
|
|
});
|
|
|
|
test('threads allowed_slug_prefixes from filing-rules JSON', () => {
|
|
expect(patternsSrc).toContain('allowed_slug_prefixes');
|
|
expect(patternsSrc).toContain('_brain-filing-rules.json');
|
|
expect(patternsSrc).toContain('dream_synthesize_paths');
|
|
});
|
|
|
|
test('reads min_evidence + lookback_days config', () => {
|
|
expect(patternsSrc).toContain('dream.patterns.min_evidence');
|
|
expect(patternsSrc).toContain('dream.patterns.lookback_days');
|
|
});
|
|
|
|
test('uses subagent_tool_executions for slug provenance (Codex #2 fix)', () => {
|
|
expect(patternsSrc).toContain('subagent_tool_executions');
|
|
expect(patternsSrc).toContain("tool_name = 'brain_put_page'");
|
|
});
|
|
|
|
test('gates on gateway provider reachability, not ANTHROPIC_API_KEY (PR #2279)', () => {
|
|
// The gate must probe the RESOLVED patterns model through the gateway
|
|
// (any configured provider can run patterns), not hardcode the Anthropic
|
|
// env var — that misclassified non-Anthropic stacks as "no upstream".
|
|
expect(patternsSrc).toContain('probeChatModel');
|
|
expect(patternsSrc).toContain('normalizeModelId');
|
|
expect(patternsSrc).toContain('no_provider');
|
|
expect(patternsSrc).not.toContain('process.env.ANTHROPIC_API_KEY');
|
|
});
|
|
|
|
test('skips when reflections below min_evidence', () => {
|
|
expect(patternsSrc).toContain('insufficient_evidence');
|
|
});
|
|
|
|
test('reverse-writes pages to disk via serializeMarkdown', () => {
|
|
expect(patternsSrc).toContain('serializeMarkdown');
|
|
expect(patternsSrc).toContain('writeFileSync');
|
|
});
|
|
|
|
test('runs after extract — queries fresh graph', () => {
|
|
// Documented invariant: pattern phase MUST run after extract.
|
|
// The cycle.ts dispatcher enforces order; this just confirms the
|
|
// patterns module doesn't try to compute its own auto-link layer
|
|
// (which would be a subtle regression).
|
|
expect(patternsSrc).not.toContain('runAutoLink');
|
|
expect(patternsSrc).not.toContain('extractPageLinks(');
|
|
});
|
|
|
|
test('does NOT use raw_data table (Codex #3 fix)', () => {
|
|
expect(patternsSrc).not.toContain('putRawData');
|
|
expect(patternsSrc).not.toContain('getRawData');
|
|
});
|
|
});
|
|
|
|
describe('patterns scope filter', () => {
|
|
test('filters reflections by slug LIKE <output_root>/personal/reflections/%', () => {
|
|
// #2415: the namespace root is configurable (dream.synthesize.output_root,
|
|
// default 'wiki') and bound as a parameter — the scope filter itself and
|
|
// the reflections sub-path stay pinned.
|
|
expect(patternsSrc).toContain('slug LIKE $2');
|
|
expect(patternsSrc).toContain('/personal/reflections/%');
|
|
});
|
|
|
|
test('orders by updated_at DESC for recency-bias', () => {
|
|
expect(patternsSrc).toContain('ORDER BY updated_at DESC');
|
|
});
|
|
|
|
test('caps gather to 100 reflections (cost control)', () => {
|
|
expect(patternsSrc).toContain('LIMIT 100');
|
|
});
|
|
});
|