Files
gbrain/test/cycle-dream-output-root.test.ts
T
1833d95896 fix(dream,chronicle): synthesize/concepts output family — source scope, output root, retrieval reach, durable provenance, honest judge failures (#1586 #2415 #2163 #2569 #2606) (#2939)
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>
2026-07-17 15:01:27 -07:00

113 lines
4.6 KiB
TypeScript

/**
* #2415 — configurable dream output namespace (`dream.synthesize.output_root`).
*
* The synthesize + patterns phases previously hardcoded `wiki/` in the
* subagent prompt slug templates, the patterns reflection lookup, and the
* trusted-workspace allow-list loaded from skills/_brain-filing-rules.json.
* This suite pins:
* - default 'wiki' → byte-identical prompt + verbatim filing-rule globs
* (zero behavior change unless the key is set);
* - a custom root remaps prompt slug templates and the allow-list globs;
* - loadOutputRoot validates against the slug grammar (bad values fall
* back to 'wiki');
* - the patterns phase gathers reflections under the configured root.
*/
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import { PGLiteEngine } from '../src/core/pglite-engine.ts';
import { __testing, loadAllowedSlugPrefixes, loadOutputRoot } from '../src/core/cycle/synthesize.ts';
import { runPhasePatterns } from '../src/core/cycle/patterns.ts';
import type { DiscoveredTranscript } from '../src/core/cycle/transcript-discovery.ts';
const { buildSynthesisPrompt } = __testing;
const transcript: DiscoveredTranscript = {
filePath: '/tmp/t.txt',
basename: 't',
content: 'User: hello world',
contentHash: 'abcdef0123456789',
inferredDate: '2026-07-17',
} as DiscoveredTranscript;
describe('#2415: buildSynthesisPrompt output root', () => {
test('defaults to wiki/ slug templates', () => {
const prompt = buildSynthesisPrompt(transcript, 'chunk', 0, 1);
expect(prompt).toContain('wiki/personal/reflections/2026-07-17-');
expect(prompt).toContain('wiki/originals/ideas/2026-07-17-');
});
test('custom root replaces wiki/ in both slug templates', () => {
const prompt = buildSynthesisPrompt(transcript, 'chunk', 0, 1, '', 'notes');
expect(prompt).toContain('notes/personal/reflections/2026-07-17-');
expect(prompt).toContain('notes/originals/ideas/2026-07-17-');
expect(prompt).not.toContain('wiki/personal/reflections/');
expect(prompt).not.toContain('wiki/originals/ideas/');
});
});
describe('#2415: loadAllowedSlugPrefixes remap', () => {
// Runs from the repo root, so skills/_brain-filing-rules.json resolves.
test("default 'wiki' returns the filing-rule globs verbatim", async () => {
const globs = await loadAllowedSlugPrefixes();
expect(globs).toContain('wiki/personal/reflections/*');
expect(globs).toContain('dream-cycle-summaries/*');
});
test('custom root remaps only wiki/-rooted globs', async () => {
const globs = await loadAllowedSlugPrefixes('notes');
expect(globs).toContain('notes/personal/reflections/*');
expect(globs).toContain('notes/originals/*');
expect(globs).toContain('notes/personal/patterns/*');
// Non-wiki globs pass through untouched.
expect(globs).toContain('dream-cycle-summaries/*');
expect(globs.some(g => g.startsWith('wiki/'))).toBe(false);
});
});
describe('#2415: loadOutputRoot validation + patterns gather scope', () => {
let engine: PGLiteEngine;
beforeAll(async () => {
engine = new PGLiteEngine();
await engine.connect({});
await engine.initSchema();
});
afterAll(async () => {
await engine.disconnect();
});
test('unset → wiki; trailing slash trimmed; invalid → wiki fallback', async () => {
expect(await loadOutputRoot(engine)).toBe('wiki');
await engine.setConfig('dream.synthesize.output_root', 'notes/');
expect(await loadOutputRoot(engine)).toBe('notes');
await engine.setConfig('dream.synthesize.output_root', '../escape');
expect(await loadOutputRoot(engine)).toBe('wiki');
await engine.setConfig('dream.synthesize.output_root', 'Bad_Root');
expect(await loadOutputRoot(engine)).toBe('wiki');
});
test('patterns phase gathers reflections under the configured root', async () => {
await engine.setConfig('dream.synthesize.output_root', 'notes');
for (let i = 0; i < 3; i++) {
await engine.putPage(`notes/personal/reflections/2026-07-17-r${i}`, {
type: 'note',
title: `R${i}`,
compiled_truth: `reflection ${i}`,
timeline: '',
frontmatter: {},
});
}
// A wiki/-rooted reflection must NOT be counted under the custom root.
await engine.putPage('wiki/personal/reflections/2026-07-17-old', {
type: 'note',
title: 'Old',
compiled_truth: 'legacy reflection',
timeline: '',
frontmatter: {},
});
const result = await runPhasePatterns(engine, { brainDir: '/tmp', dryRun: true });
expect(result.status).toBe('ok');
expect(result.details?.reflections_considered).toBe(3);
});
});