diff --git a/src/core/cycle/synthesize.ts b/src/core/cycle/synthesize.ts index 7f335bc01..25e65e4a4 100644 --- a/src/core/cycle/synthesize.ts +++ b/src/core/cycle/synthesize.ts @@ -1139,7 +1139,8 @@ async function collectChildPutPageSlugs( FROM subagent_tool_executions WHERE job_id = ANY($1::int[]) AND tool_name = 'brain_put_page' - AND status = 'complete'`, + AND status = 'complete' + ORDER BY id`, [childIds], ); const rewritten = new Map(); @@ -1149,7 +1150,10 @@ async function collectChildPutPageSlugs( const finalSlug = meta && meta.chunkTotal > 1 ? rewriteChunkedSlug(r.slug, meta.hash6, meta.idx) : r.slug; - if (!rewritten.has(finalSlug)) rewritten.set(finalSlug, r.job_id); + // Last writer wins, in execution-row order (ORDER BY id): if two children + // collide on a final slug, the pages row holds the LAST put_page write, so + // the stamp must attribute that child's transcript — not an arbitrary one. + rewritten.set(finalSlug, r.job_id); } return [...rewritten.entries()] .sort(([a], [b]) => a.localeCompare(b)) diff --git a/test/cycle-synthesize-slug-collection.test.ts b/test/cycle-synthesize-slug-collection.test.ts index be22662ad..12a4ddc7e 100644 --- a/test/cycle-synthesize-slug-collection.test.ts +++ b/test/cycle-synthesize-slug-collection.test.ts @@ -174,6 +174,25 @@ describe('#2285: orchestrator-owned deterministic transcript frontmatter', () => } }); + test('slug collision across children attributes the LAST writer (matches surviving putPage)', async () => { + const db = (engine as any).db; + // Jobs 1001 then 1002 write the same slug; the pages row would hold + // 1002's content (last put_page wins), so the ref must carry jobId 1002. + await db.query( + `INSERT INTO subagent_tool_executions (job_id, message_idx, tool_use_id, tool_name, status, input) + VALUES (1001, 9, 'tool_dup_a', 'brain_put_page', 'complete', $1::jsonb)`, + [JSON.stringify({ slug: 'wiki/agents/test/collision', body: 'first' })], + ); + await db.query( + `INSERT INTO subagent_tool_executions (job_id, message_idx, tool_use_id, tool_name, status, input) + VALUES (1002, 9, 'tool_dup_b', 'brain_put_page', 'complete', $1::jsonb)`, + [JSON.stringify({ slug: 'wiki/agents/test/collision', body: 'second' })], + ); + const refs = await collectChildPutPageSlugs(engine as any, [1001, 1002], new Map()); + const hit = refs.find((r: { slug: string }) => r.slug === 'wiki/agents/test/collision'); + expect(hit?.jobId).toBe(1002); + }); + test('stampDreamProvenance merges the transcript metadata into DB frontmatter', async () => { const slug = 'wiki/originals/ideas/2026-07-17-transcript-meta-abc123'; await engine.putPage(slug, {