Merge branch 'main' of ssh://ramanujan.glitch.university:2222/glitch-university/gupi-osint-board
This commit is contained in:
@@ -1,92 +0,0 @@
|
|||||||
{
|
|
||||||
"slug": "barricelli-inventor-proof",
|
|
||||||
"name": "Scene 7 · Prove Barricelli was an inventor",
|
|
||||||
"title": "The Barricelli Files",
|
|
||||||
"subtitle": "Scene 7 · Demonstrate OSINT skill",
|
|
||||||
"brief": {
|
|
||||||
"body": "DEMONSTRATE OSINT SKILL\n\nProve that Nils Aall Barricelli was an inventor. Find a reliable source online, take a screenshot, and paste it directly onto this board. Connect the source to the authored claim with red thread, explain what the evidence proves, and submit the Case Report.",
|
|
||||||
"concepts": []
|
|
||||||
},
|
|
||||||
"documents": [],
|
|
||||||
"folders": [],
|
|
||||||
"claims": [
|
|
||||||
{
|
|
||||||
"key": "nils-inventor",
|
|
||||||
"statement": "Nils Aall Barricelli was an inventor.",
|
|
||||||
"x": 940,
|
|
||||||
"y": 360,
|
|
||||||
"width": 330,
|
|
||||||
"height": 190
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"report": {
|
|
||||||
"title": "Barricelli Inventor Finding",
|
|
||||||
"requiredForCompletion": true
|
|
||||||
},
|
|
||||||
"goals": [
|
|
||||||
{
|
|
||||||
"key": "barricelli.inventor-proof",
|
|
||||||
"title": "Prove Nils Aall Barricelli was an inventor",
|
|
||||||
"instructions": "Paste a reliable screenshot, connect it to the claim, and submit a properly cited Case Report.",
|
|
||||||
"completionMessage": "CASE REPORT ACCEPTED — NILS AALL BARRICELLI: INVENTOR",
|
|
||||||
"requiredFlags": ["scene7.nils_inventor_proved"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"evidenceMatchRules": [
|
|
||||||
{
|
|
||||||
"name": "Google Patents · GB695913A",
|
|
||||||
"sourceLabel": "Google Patents · GB695913A · Improved chest of drawers",
|
|
||||||
"sourceUri": "https://patents.google.com/patent/GB695913A/en",
|
|
||||||
"flagKey": "scene7.nils_inventor_proved",
|
|
||||||
"minimumAnchorMatches": 2,
|
|
||||||
"anchors": [
|
|
||||||
{
|
|
||||||
"phrase": "GB695913A Improved chest of drawers",
|
|
||||||
"minimumSimilarity": 0.7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"phrase": "Nils Aall Barricelli",
|
|
||||||
"minimumSimilarity": 0.72
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"phrase": "695913 Chests of drawers BARRICELLI N A May 31 1951",
|
|
||||||
"minimumSimilarity": 0.68
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Nasjonalbiblioteket · Patent 93585",
|
|
||||||
"sourceLabel": "Nasjonalbiblioteket · Patent 93585 · Koffert-kommode",
|
|
||||||
"sourceUri": "https://www.nb.no/items/921545b51acef0b05054cfc1f4666975?page=9&searchText=baricelli",
|
|
||||||
"flagKey": "scene7.nils_inventor_proved",
|
|
||||||
"minimumAnchorMatches": 2,
|
|
||||||
"anchors": [
|
|
||||||
{
|
|
||||||
"phrase": "Nr 75 348 Kl 33 b-9 Fra 31 mai 1948 93585 Koffert-kommode",
|
|
||||||
"minimumSimilarity": 0.68
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"phrase": "Niels Aall Baricelli Oslo",
|
|
||||||
"minimumSimilarity": 0.72
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"phrase": "Patentpaastand Kommode som er satt sammen av flere enkeltdeler som hver er utfort som koffert",
|
|
||||||
"minimumSimilarity": 0.62
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"evidenceSemanticRules": [
|
|
||||||
{
|
|
||||||
"goalKey": "barricelli.inventor-proof",
|
|
||||||
"name": "Barricelli inventor claim",
|
|
||||||
"targetSubject": "Nils Aall Barricelli",
|
|
||||||
"relatedSubject": "Nils Aall Barricelli's father",
|
|
||||||
"assertion": "The source states or directly demonstrates that Nils Aall Barricelli was an inventor or a named patent applicant for an invention.",
|
|
||||||
"successFlagKey": "scene7.nils_inventor_proved",
|
|
||||||
"relatedFlagKey": "scene7.father_inventor_discovered",
|
|
||||||
"minimumConfidence": 0.88,
|
|
||||||
"evaluatorVersion": "barricelli_inventor_v1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -9,6 +9,7 @@ import jwt from 'jsonwebtoken'
|
|||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||||
import type { CaseState, DocumentExhibit, EventExhibit, FolderExhibit, NoteExhibit, PartyExhibit, TimelineView } from '../src/types.js'
|
import type { CaseState, DocumentExhibit, EventExhibit, FolderExhibit, NoteExhibit, PartyExhibit, TimelineView } from '../src/types.js'
|
||||||
import { runMigrations } from './migrations.js'
|
import { runMigrations } from './migrations.js'
|
||||||
|
import type { StoryGraphDto } from './storyGraphRepository.js'
|
||||||
|
|
||||||
const { Client } = pg
|
const { Client } = pg
|
||||||
const baseDatabaseUrl = process.env.TEST_DATABASE_URL
|
const baseDatabaseUrl = process.env.TEST_DATABASE_URL
|
||||||
@@ -252,6 +253,7 @@ suite('normalized level persistence API', () => {
|
|||||||
const { importMysteryTemplate } = await import('../scripts/importMysteryTemplate.js')
|
const { importMysteryTemplate } = await import('../scripts/importMysteryTemplate.js')
|
||||||
const manifestPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'mysteries', 'barricelli-scene-7', 'mystery.json')
|
const manifestPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'mysteries', 'barricelli-scene-7', 'mystery.json')
|
||||||
const imported = await importMysteryTemplate(manifestPath, baseUrl, adminAuthorization.replace(/^Bearer /, ''))
|
const imported = await importMysteryTemplate(manifestPath, baseUrl, adminAuthorization.replace(/^Bearer /, ''))
|
||||||
|
expect(imported.mystery).toEqual({ slug:'barricelli-inventor-proof' })
|
||||||
expect(imported.playableLevel).toMatchObject({ title:'The Barricelli Files',exhibits:[expect.objectContaining({ type:'claim',statement:'Nils Aall Barricelli was an inventor.' })],report:expect.objectContaining({
|
expect(imported.playableLevel).toMatchObject({ title:'The Barricelli Files',exhibits:[expect.objectContaining({ type:'claim',statement:'Nils Aall Barricelli was an inventor.' })],report:expect.objectContaining({
|
||||||
title:'Barricelli Inventor Finding',requiredForCompletion:true,status:'draft',
|
title:'Barricelli Inventor Finding',requiredForCompletion:true,status:'draft',
|
||||||
}),goals:[expect.objectContaining({
|
}),goals:[expect.objectContaining({
|
||||||
@@ -270,6 +272,12 @@ suite('normalized level persistence API', () => {
|
|||||||
expect.objectContaining({ goalKey:'barricelli.inventor-proof',targetSubject:'Nils Aall Barricelli',
|
expect.objectContaining({ goalKey:'barricelli.inventor-proof',targetSubject:'Nils Aall Barricelli',
|
||||||
relatedFlagKey:'scene7.father_inventor_discovered' }),
|
relatedFlagKey:'scene7.father_inventor_discovered' }),
|
||||||
])
|
])
|
||||||
|
const mysteries = await (await adminFetch(`${baseUrl}/api/admin/mysteries`)).json() as { id:string;slug:string }[]
|
||||||
|
const mysteryId = mysteries.find(mystery => mystery.slug === 'barricelli-inventor-proof')!.id
|
||||||
|
const graph = await (await adminFetch(`${baseUrl}/api/admin/mysteries/${mysteryId}/graph`)).json() as StoryGraphDto
|
||||||
|
expect(graph.nodes).toHaveLength(2)
|
||||||
|
expect(graph.nodes.find(node => node.nodeType === 'level')).toMatchObject({ label:'Demonstrate OSINT skill',levelTemplateVersionId:expect.any(String) })
|
||||||
|
expect(graph.nodes.find(node => node.nodeType === 'merit')).toMatchObject({ label:'The Barricelli Luggage',awardsFlag:'barricelli_luggage' })
|
||||||
|
|
||||||
const fixtureDir = path.join(path.dirname(manifestPath), 'fixtures')
|
const fixtureDir = path.join(path.dirname(manifestPath), 'fixtures')
|
||||||
const fatherUpload = new FormData()
|
const fatherUpload = new FormData()
|
||||||
|
|||||||
@@ -144,8 +144,12 @@ suite('narrative graph runtime', () => {
|
|||||||
const mysteries = await (await authFetch(`${baseUrl}/api/admin/mysteries`, adminAuthorization)).json() as { id:string;slug:string }[]
|
const mysteries = await (await authFetch(`${baseUrl}/api/admin/mysteries`, adminAuthorization)).json() as { id:string;slug:string }[]
|
||||||
const mysteryId = mysteries.find(mystery => mystery.slug === 'goal-mystery')!.id
|
const mysteryId = mysteries.find(mystery => mystery.slug === 'goal-mystery')!.id
|
||||||
expect((await authFetch(`${baseUrl}/api/admin/mysteries/${mysteryId}/graph`, adminAuthorization, { method:'POST',headers:json,
|
expect((await authFetch(`${baseUrl}/api/admin/mysteries/${mysteryId}/graph`, adminAuthorization, { method:'POST',headers:json,
|
||||||
body:JSON.stringify({ entry:'level',nodes:[{ key:'level',type:'level',label:'Prove it',templateSlug:'goal-chapter',x:0,y:0,
|
body:JSON.stringify({ entry:'level',nodes:[
|
||||||
terminals:[{ key:'continue',label:'Continue',to:null }] }] }) })).status).toBe(201)
|
{ key:'level',type:'level',label:'Prove it',templateSlug:'goal-chapter',x:0,y:0,
|
||||||
|
terminals:[{ key:'continue',label:'Continue',to:'merit' }] },
|
||||||
|
{ key:'merit',type:'merit',label:'Inventor Merit',awardsFlag:'demo.inventor-merit',x:200,y:0,
|
||||||
|
terminals:[{ key:'continue',label:'Accept',to:null }] },
|
||||||
|
] }) })).status).toBe(201)
|
||||||
|
|
||||||
const created = await authFetch(`${baseUrl}/api/playthroughs`, undefined, { method:'POST',headers:json,body:JSON.stringify({ mystery:'goal-mystery' }) })
|
const created = await authFetch(`${baseUrl}/api/playthroughs`, undefined, { method:'POST',headers:json,body:JSON.stringify({ mystery:'goal-mystery' }) })
|
||||||
expect(created.status).toBe(201)
|
expect(created.status).toBe(201)
|
||||||
@@ -167,10 +171,15 @@ suite('narrative graph runtime', () => {
|
|||||||
|
|
||||||
const completed = await authFetch(`${baseUrl}/api/playthroughs/${playthroughId}/advance`, undefined, { method:'POST',headers:json,body:'{}' })
|
const completed = await authFetch(`${baseUrl}/api/playthroughs/${playthroughId}/advance`, undefined, { method:'POST',headers:json,body:'{}' })
|
||||||
expect(completed.status).toBe(200)
|
expect(completed.status).toBe(200)
|
||||||
expect((await completed.json() as PlaythroughState).playthrough.status).toBe('finished')
|
expect(await completed.json()).toMatchObject({ playthrough:{ status:'active' },node:{ kind:'merit',label:'Inventor Merit',awardsFlag:'demo.inventor-merit' } })
|
||||||
expect(await (await authFetch(`${baseUrl}/api/playthroughs/${playthroughId}/achievements`, undefined)).json()).toContain('demo.inventor-proved')
|
expect(await (await authFetch(`${baseUrl}/api/playthroughs/${playthroughId}/achievements`, undefined)).json())
|
||||||
|
.toEqual(expect.arrayContaining(['demo.inventor-proved','demo.inventor-merit']))
|
||||||
const achievement = await appPool.query<{ awarded_by_node_id:string | null }>(
|
const achievement = await appPool.query<{ awarded_by_node_id:string | null }>(
|
||||||
'SELECT awarded_by_node_id FROM osint.achievements WHERE playthrough_id=$1 AND flag_key=$2', [playthroughId,'demo.inventor-proved'])
|
'SELECT awarded_by_node_id FROM osint.achievements WHERE playthrough_id=$1 AND flag_key=$2', [playthroughId,'demo.inventor-proved'])
|
||||||
expect(achievement.rows[0].awarded_by_node_id).toBe(atLevel.node!.id)
|
expect(achievement.rows[0].awarded_by_node_id).toBe(atLevel.node!.id)
|
||||||
|
|
||||||
|
const finished = await authFetch(`${baseUrl}/api/playthroughs/${playthroughId}/advance`, undefined, { method:'POST',headers:json,body:'{}' })
|
||||||
|
expect(finished.status).toBe(200)
|
||||||
|
expect((await finished.json() as PlaythroughState).playthrough.status).toBe('finished')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ export function createStoryGraphRepository(pool: Pool): StoryGraphRepository {
|
|||||||
if (!versionId) throw new Error(`Graph node ${node.key}: unknown level template ${node.templateSlug}`)
|
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,awards_flag) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)',
|
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])
|
[id, mysteryId, node.type, node.label || node.type, node.x, node.y, Boolean(node.utterances?.length), versionId, node.componentKey || null, node.awardsFlag?.trim()|| null])
|
||||||
for (const [index, terminal] of (node.terminals || []).entries()) {
|
for (const [index, terminal] of (node.terminals || []).entries()) {
|
||||||
const terminalId = randomUUID(); terminalIds.set(`${node.key}:${terminal.key}`, terminalId)
|
const terminalId = randomUUID(); terminalIds.set(`${node.key}:${terminal.key}`, terminalId)
|
||||||
let npcId: string | null = null
|
let npcId: string | null = null
|
||||||
|
|||||||
Reference in New Issue
Block a user