Seed phone + merit nodes (and NPC contacts) via the graph seeder

authorGraph now seeds merit nodes' awards_flag and phone-node terminals
bound to an NPC (npc key -> npc_id, the callee dialed); authorMystery seeds
cast phone_number/email. Importer types updated so mystery.json can carry
these, matching how Glass Harbour is seeded — the Barricelli graph can now
be authored as data.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 17:49:09 +02:00
co-authored by Claude Opus 4.8
parent 13a270911f
commit 9c496bfe19
3 changed files with 19 additions and 13 deletions
+12 -6
View File
@@ -3,8 +3,8 @@ import type { Pool, PoolClient } from 'pg'
export type GraphSpecNode = {
key: string; type: StoryNodeType; label?: string; x: number; y: number
componentKey?: string; templateSlug?: string; version?: number
terminals?: { key: string; label?: string; to?: string | null }[]
componentKey?: string; templateSlug?: string; version?: number; awardsFlag?: string
terminals?: { key: string; label?: string; to?: string | null; npc?: string }[]
utterances?: { npc?: string; pose?: string; text: string; utterer?: Utterer }[]
}
export type GraphSpec = { entry: string; nodes: GraphSpecNode[] }
@@ -261,12 +261,18 @@ export function createStoryGraphRepository(pool: Pool): StoryGraphRepository {
versionId = version.rows[0]?.id ?? null
if (!versionId) throw new Error(`Graph node ${node.key}: unknown level template ${node.templateSlug}`)
}
await client.query('INSERT INTO osint.story_nodes (id,mystery_id,node_type,label,xpos,ypos,has_utterances,level_template_version_id,component_key) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)',
[id, mysteryId, node.type, node.label || node.type, node.x, node.y, Boolean(node.utterances?.length), versionId, node.componentKey || null])
await client.query('INSERT INTO osint.story_nodes (id,mystery_id,node_type,label,xpos,ypos,has_utterances,level_template_version_id,component_key,awards_flag) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)',
[id, mysteryId, node.type, node.label || node.type, node.x, node.y, Boolean(node.utterances?.length), versionId, node.componentKey || null, node.awardsFlag || null])
for (const [index, terminal] of (node.terminals || []).entries()) {
const terminalId = randomUUID(); terminalIds.set(`${node.key}:${terminal.key}`, terminalId)
await client.query('INSERT INTO osint.story_node_terminals (id,parent_node_id,terminal_key,label,sort_order) VALUES ($1,$2,$3,$4,$5)',
[terminalId, id, terminal.key, terminal.label || terminal.key, index])
let npcId: string | null = null
if (terminal.npc) { // phone-node terminal bound to an NPC (the callee)
const npc = await client.query<{ id: string }>('SELECT id FROM osint.npcs WHERE npc_key=$1 AND mystery_id IS NULL', [terminal.npc])
npcId = npc.rows[0]?.id ?? null
if (!npcId) throw new Error(`Graph node ${node.key}: terminal ${terminal.key} references unknown NPC ${terminal.npc}`)
}
await client.query('INSERT INTO osint.story_node_terminals (id,parent_node_id,terminal_key,label,sort_order,npc_id) VALUES ($1,$2,$3,$4,$5,$6)',
[terminalId, id, terminal.key, terminal.label || terminal.key, index, npcId])
}
}
// Wire terminals now that all nodes exist.