Cleanup: drop vestigial utterance columns, sync story-graph doc

- migration 022 drops utterances.advances_to_utterance_id and .effect, which the
  parent-only/child-count dialogue model never used; purge their references.
- rewrite docs/story-graph.md to match what was built (parent-child utterances,
  vertical mystery graph, graph runtime, gate stubs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 16:28:11 +02:00
co-authored by Claude Opus 4.8
parent ddb3a386f0
commit aa789bbadb
5 changed files with 130 additions and 190 deletions
+11 -11
View File
@@ -21,8 +21,8 @@ export type LevelTemplateOption = { versionId: string; slug: string; name: strin
export type Utterer = 'npc' | 'player'
export type UtteranceDto = {
id: string; nodeId: string; utterer: Utterer; npcId: string | null; poseKey: string | null; text: string
parentUtteranceId: string | null; advancesToUtteranceId: string | null; terminalId: string | null
effect: string | null; xpos: number; ypos: number; sortOrder: number
parentUtteranceId: string | null; terminalId: string | null
xpos: number; ypos: number; sortOrder: number
}
// A sensible starter terminal set so a freshly dropped node is immediately wireable.
@@ -46,7 +46,7 @@ export interface StoryGraphRepository {
listLevelTemplates(): Promise<LevelTemplateOption[]>
listUtterances(nodeId: string): Promise<UtteranceDto[]>
createUtterance(nodeId: string, input: { utterer: Utterer; xpos: number; ypos: number; text?: string }): Promise<UtteranceDto | null>
updateUtterance(id: string, input: Partial<{ text: string; utterer: Utterer; npcId: string | null; poseKey: string | null; xpos: number; ypos: number; parentUtteranceId: string | null; advancesToUtteranceId: string | null; terminalId: string | null; effect: string | null }>): Promise<{ ok: boolean; error?: string }>
updateUtterance(id: string, input: Partial<{ text: string; utterer: Utterer; npcId: string | null; poseKey: string | null; xpos: number; ypos: number; parentUtteranceId: string | null; terminalId: string | null }>): Promise<{ ok: boolean; error?: string }>
deleteUtterance(id: string): Promise<boolean>
authorGraph(mysteryId: string, spec: GraphSpec): Promise<{ nodes: number } | null>
}
@@ -179,7 +179,7 @@ export function createStoryGraphRepository(pool: Pool): StoryGraphRepository {
async listUtterances(nodeId) {
const result = await pool.query<UtteranceRow>(
`SELECT id,node_id,utterer,npc_id,pose_key,text,parent_utterance_id,advances_to_utterance_id,terminal_id,effect,xpos,ypos,sort_order
`SELECT id,node_id,utterer,npc_id,pose_key,text,parent_utterance_id,terminal_id,xpos,ypos,sort_order
FROM osint.utterances WHERE node_id=$1 ORDER BY sort_order,id`, [nodeId])
return result.rows.map(mapUtterance)
},
@@ -192,7 +192,7 @@ export function createStoryGraphRepository(pool: Pool): StoryGraphRepository {
await pool.query('INSERT INTO osint.utterances (id,node_id,utterer,text,xpos,ypos,sort_order) VALUES ($1,$2,$3,$4,$5,$6,$7)',
[id, nodeId, input.utterer, input.text || '', input.xpos, input.ypos, order.rows[0].next])
const created = await pool.query<UtteranceRow>(
`SELECT id,node_id,utterer,npc_id,pose_key,text,parent_utterance_id,advances_to_utterance_id,terminal_id,effect,xpos,ypos,sort_order FROM osint.utterances WHERE id=$1`, [id])
`SELECT id,node_id,utterer,npc_id,pose_key,text,parent_utterance_id,terminal_id,xpos,ypos,sort_order FROM osint.utterances WHERE id=$1`, [id])
return mapUtterance(created.rows[0])
},
@@ -201,7 +201,7 @@ export function createStoryGraphRepository(pool: Pool): StoryGraphRepository {
if (!owner.rows[0]) return { ok: false, error: 'Utterance not found' }
const nodeId = owner.rows[0].node_id
// Same-node integrity for the three links.
for (const link of ['parentUtteranceId', 'advancesToUtteranceId'] as const) {
for (const link of ['parentUtteranceId'] as const) {
const value = input[link]
if (value) {
const target = await pool.query<{ node_id: string }>('SELECT node_id FROM osint.utterances WHERE id=$1', [value])
@@ -214,7 +214,7 @@ export function createStoryGraphRepository(pool: Pool): StoryGraphRepository {
}
const columns: Record<string, string> = {
text: 'text', utterer: 'utterer', npcId: 'npc_id', poseKey: 'pose_key', xpos: 'xpos', ypos: 'ypos',
parentUtteranceId: 'parent_utterance_id', advancesToUtteranceId: 'advances_to_utterance_id', terminalId: 'terminal_id', effect: 'effect',
parentUtteranceId: 'parent_utterance_id', terminalId: 'terminal_id',
}
const sets: string[] = []
const values: unknown[] = [id]
@@ -302,13 +302,13 @@ export function createStoryGraphRepository(pool: Pool): StoryGraphRepository {
type UtteranceRow = {
id: string; node_id: string; utterer: Utterer; npc_id: string | null; pose_key: string | null; text: string
parent_utterance_id: string | null; advances_to_utterance_id: string | null; terminal_id: string | null
effect: string | null; xpos: number; ypos: number; sort_order: number
parent_utterance_id: string | null; terminal_id: string | null
xpos: number; ypos: number; sort_order: number
}
function mapUtterance(row: UtteranceRow): UtteranceDto {
return {
id: row.id, nodeId: row.node_id, utterer: row.utterer, npcId: row.npc_id, poseKey: row.pose_key, text: row.text,
parentUtteranceId: row.parent_utterance_id, advancesToUtteranceId: row.advances_to_utterance_id, terminalId: row.terminal_id,
effect: row.effect, xpos: row.xpos, ypos: row.ypos, sortOrder: row.sort_order,
parentUtteranceId: row.parent_utterance_id, terminalId: row.terminal_id,
xpos: row.xpos, ypos: row.ypos, sortOrder: row.sort_order,
}
}