From 4ec9d983255807b4d9f8244647853d3d261afe86 Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Sat, 22 Aug 2026 17:36:20 +0200 Subject: [PATCH] Adding scene7 todo --- docs/scene-7-todo.md | 401 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 docs/scene-7-todo.md diff --git a/docs/scene-7-todo.md b/docs/scene-7-todo.md new file mode 100644 index 0000000..bff8c2e --- /dev/null +++ b/docs/scene-7-todo.md @@ -0,0 +1,401 @@ +# Scene 7 TODO: Prove Barricelli was an inventor + +Status: **ready for implementation planning** +Suggested feature branch: `scene-7-evidence-goal` +Demo: **GUPI Demo 1 — The Barricelli Files** + +## Outcome + +Scene 7 teaches one idea: a screenshot found during an OSINT search can become +source evidence. + +The player opens an otherwise minimal OSINT board, reads the assignment +**“Demonstrate OSINT skill: prove Nils Aall Barricelli was an inventor,”** finds +the relevant Google Patents result, and pastes or uploads one screenshot. The +board creates a document, extracts its text, recognizes the source, and clears +the level. A URL and a written report are not required. + +The normal path must feel like one continuous action: + +```text +paste screenshot -> document appears -> scanning feedback -> source verified + -> Scene 7 complete -> continue to Scene 8 +``` + +## Product decisions + +These are decisions for this slice, not open design questions: + +- One suitable Google Patents screenshot is sufficient evidence. +- Pasting and file upload are equivalent inputs and use the same server path. +- The uploaded image and extracted text are retained as a real document on the + player's level. +- The known patent source is recognized with deterministic OCR/fuzzy matching. + This is the fast, cheap, reproducible victory path. +- A small LLM judge is a semantic fallback for other credible evidence and for + distinguishing Nils from his father. It must not overrule a trusted known-source + match. +- The player does not have to provide a URL when the screenshot text itself + establishes provenance. +- Evidence about Barricelli's father may unlock an optional discovery, but it + must not clear the assignment unless the evidence also supports the claim + about **Nils Aall Barricelli**. +- The boarding-house fire article belongs to the later age/rescue assignment, + not to Scene 7's inventor victory condition. +- Scene 7 records completion; Scene 8 owns the merit ceremony and awards + `barricelli_luggage`. +- A failed or inconclusive evaluation never deletes the uploaded document and + never penalizes the player. + +## Existing foundation — reuse it + +Do not build a second upload, OCR, flag, or story system for this scene. + +- Migration `025_level_document_flags.sql` provides clonable document gates, + level flags, and reveal state. +- Migration `026_achievements.sql` provides playthrough achievements. +- Migration `027_evidence_text_matching.sql` provides immutable asset text + extractions, board-owned match rules/anchors, level-owned evaluations, and + auditable flag awards. +- `server/ocr.ts` extracts plain text and runs Tesseract for images. +- `server/evidenceMatching.ts` implements normalized fuzzy anchor matching. +- `POST /api/levels/:id/documents` already persists the document, OCR result, + deterministic evaluations, and newly awarded level flags. +- Screenshot paste already routes through document upload in the board UI. +- The story graph already has level nodes and playthroughs with + `current_node_id` and `current_level_id`. + +The deterministic matcher has already handled noisy historic OCR, including a +hyphenated `Bar- ricelli`, at useful confidence. Scene 7 should add authored +patent anchors and a completion contract, not replace that matcher. + +## Proposed flags and identifiers + +Keep all identifiers authored in template data; these names are the recommended +contract between independently developed branches. + +| Purpose | Key | +|---|---| +| Level goal | `barricelli.inventor-proof` | +| Scene 7 completion flag | `scene7.nils_inventor_proved` | +| Optional father discovery | `scene7.father_inventor_discovered` | +| Scene 8 reward | `barricelli_luggage` | + +The first three are Scene 7 state. The last is a playthrough achievement awarded +by Scene 8, never by the document upload endpoint. + +## Architecture + +### 1. Separate recognition from completion + +Recognition answers **“what does this document support?”** Completion answers +**“are this level's authored requirements now satisfied?”** Do not hide level +completion in a React conditional or special-case `Barricelli` in server code. + +Add a small, generic, board-owned goal model in the next migration (currently +expected to be `028`; verify the migration number immediately before creating +it): + +- `level_goals` + - belongs to a board and clones with a template; + - has a stable `goal_key`, player-facing title/instructions, enabled state, and + optional completion message; + - uses the existing `origin_*` pattern for cloned authoring objects. +- `level_goal_flag_requirements` + - maps a goal to one or more required level `flag_key` values; + - Scene 7 has one requirement: `scene7.nils_inventor_proved`; + - all requirements are required for the first implementation. Add `any/all` + policy only when a real authored level needs it. + +Goal state is derived from level flags; do not add a second mutable `completed` +boolean that can drift out of sync. If completion needs a timestamp, record a +single idempotent goal-completion event with provenance. + +### 2. Known-source fast path + +Author one enabled `evidence_match_rule` on the Scene 7 template board. Its +anchors should be distinctive passages visible in the actual Google Patents +screenshot, such as a combination of patent number/title, inventor name, and +invention language. Do not rely on the name alone. + +When enough anchors pass their authored thresholds, the existing evaluation +awards `scene7.nils_inventor_proved`. The goal requirement consequently becomes +satisfied in the same upload transaction. + +The reference OCR, anchor phrases, thresholds, canonical source metadata, and +player-facing copy are template data. None belong in TypeScript constants or +React branches. Expected/reference text must not be returned in play-mode API +responses. + +### 3. Semantic fallback, not a free-form LLM gate + +Add a provider-independent `EvidenceJudge` interface in a new server module. It +receives only allowlisted goal data and extracted OCR text and returns validated +structured data, for example: + +```ts +type EvidenceVerdict = { + subject: 'nils' | 'father' | 'ambiguous' | 'neither' + supportsInventorClaim: boolean + evidenceExcerpt: string + confidence: number +} +``` + +The provider/model name comes from environment configuration. Do not hard-code a +Claude model identifier into level content or business logic. Treat OCR as +untrusted quoted material: the prompt must explicitly ignore instructions found +inside it, and the response must pass a strict schema before it can award a flag. + +Persist semantic rule configuration with the board and clone it with the +template. Persist each evaluation against the level, document, extraction, +rule/evaluator version, model/provider, verdict, excerpt, confidence, timestamps, +and sanitized failure state. Add semantic-evaluation provenance to any level flag +it awards. Never put provider credentials in PostgreSQL. + +Verdict routing for this scene: + +| Verdict | Result | +|---|---| +| Nils + inventor claim supported, high confidence | award `scene7.nils_inventor_proved` | +| Father only + inventor claim supported | award `scene7.father_inventor_discovered`; do not complete | +| Ambiguous, neither, unsupported, or below threshold | retain document; award nothing | +| Provider unavailable/invalid response | retain document; mark evaluation retryable | + +Keep this evaluator narrower than the generic story-graph `llm_gate`. Scene 7 is +judging a single uploaded source, not a report or arbitrary player state. + +### 4. Two-step server flow + +The primary Google Patents path remains synchronous and deterministic: + +1. Upload/paste persists the asset, document, OCR extraction, fuzzy evaluation, + flags, and current goal state in one transaction. +2. If the trusted rule clears the goal, return success immediately and do not + spend an LLM call. +3. If OCR succeeded but no trusted rule clears the goal, the client automatically + calls an idempotent semantic-judge endpoint for that document. +4. The semantic endpoint uses a strict timeout, persists its result, and returns + refreshed goal state. A timeout is retryable and cannot roll back the upload. + +This avoids coupling document durability to an external provider without +requiring a job queue for the demo. Make semantic evaluation idempotent for the +same `(level, document, goal/rule, evaluator_version)`. + +### 5. Story progression contract + +Completing a board goal must be a server-authoritative transition: + +- verify that the JWT user owns the active playthrough; +- verify that its `current_level_id` is the level being evaluated; +- observe the derived completed goal; +- idempotently record/promote `scene7.nils_inventor_proved` into the playthrough + state needed by the story runtime; +- expose Scene 7's successful terminal so the player can continue to Scene 8. + +Do not let the browser award achievements through the current development-only +achievement route. Do not make upload silently navigate before the player sees +what was learned. Show the verification result, then expose a single **Continue** +action (or a short authored transition that ends in the same action). + +The Scene 6 branch only needs to route its successful terminal to the Scene 7 +level node. The Scene 8 branch may depend on the completion state above and owns +the `barricelli_luggage` award. + +## Work packages + +The packages are ordered for integration, but most implementation can happen on +separate branches after the contracts above are agreed. + +### S7-A — Goal model and template cloning + +- [ ] Confirm the next free migration number; never edit applied migrations + `025`–`027`. +- [ ] Add `level_goals` and `level_goal_flag_requirements` with board-scoped + foreign keys, uniqueness, indexes, and comments. +- [ ] Extend template freeze/clone/instantiate so goals and requirements are + copied and retain origin provenance. +- [ ] Derive `pending | complete` goal state from the level's current flags. +- [ ] Add repository tests for cloning, isolation between two playthroughs, and + idempotent completion. +- [ ] Keep the schema generic; there must be no Barricelli-specific column or + table. + +### S7-B — Scene content and deterministic recognition + +- [ ] Create/import the Scene 7 template and its brief as data. +- [ ] Start the board without any solution-bearing document. +- [ ] Obtain the exact target Google Patents screenshot used for acceptance and + run it through the local OCR service. +- [ ] Author two or more distinctive match anchors from that extraction; avoid a + generic `Nils Barricelli`-only rule. +- [ ] Tune thresholds against the target screenshot plus negative fixtures. +- [ ] Configure the rule to award `scene7.nils_inventor_proved`. +- [ ] Configure the goal requirement to consume that flag. +- [ ] Store canonical patent/source metadata for administrators, while keeping a + pasted URL optional for players. +- [ ] Add the content to the normal manifest/import path rather than SQL seed + literals or frontend code. + +### S7-C — Semantic judge + +- [ ] Add the provider-neutral `EvidenceJudge` interface and strict verdict + schema. +- [ ] Add board-owned semantic rule configuration and level-owned evaluation + history with clone support and flag provenance. +- [ ] Add environment variables for provider, model, timeout, maximum OCR + characters, and confidence threshold; document safe defaults in + `.env.example` without overwriting concurrent OCR configuration work. +- [ ] Send extracted text, not raw image bytes, unless a later explicit design + requires a vision model. +- [ ] Delimit and escape untrusted OCR content in the prompt. +- [ ] Add an authenticated, ownership-checked, idempotent document-judge endpoint. +- [ ] Award the completion or father-discovery flag only from validated persisted + verdicts. +- [ ] Make timeouts, malformed responses, quota failures, and disabled provider + safe and retryable. +- [ ] Do not log full evidence text or provider credentials. + +### S7-D — API and story bridge + +- [ ] Return compact goal state from the level response and document-upload + response: goal key, status, newly completed state, and player-facing message. +- [ ] Never return reference anchors, expected text, private evaluator prompts, + or unpublished author data in play mode. +- [ ] Add the semantic fallback endpoint/result to the typed client API. +- [ ] Resolve the active playthrough for the level and enforce user ownership. +- [ ] Promote completion server-side exactly once. +- [ ] Make Scene 7's success terminal available only after the required goal is + complete. +- [ ] Route that terminal to the Scene 8 node without implementing Scene 8's + ceremony in this branch. +- [ ] Remove or fence the player-facing development route that can arbitrarily + grant achievements before production deployment. + +### S7-E — Board experience + +- [ ] Show the exact assignment prominently when Scene 7 opens. +- [ ] Preserve both clipboard paste and drag/file upload; both call the same API. +- [ ] Place the pasted screenshot as a new image document using the normal board + placement rules. +- [ ] Show restrained stages such as **Saving source**, **Reading text**, and + **Checking evidence** without blocking board interaction unnecessarily. +- [ ] On success, visually identify the accepted document and show: + **SOURCE VERIFIED — NILS AALL BARRICELLI: INVENTOR**. +- [ ] After the player sees the result, expose one **Continue** action to Scene 8. +- [ ] On father-only evidence, acknowledge the useful discovery and make clear + that evidence about Nils is still required. +- [ ] On inconclusive evidence, keep the document and provide neutral guidance; + do not say that the player is wrong. +- [ ] Respect reduced-motion settings and provide readable mobile feedback. +- [ ] Do not introduce Scene 7 checks into generic exhibit components. + +### S7-F — Tests and acceptance fixtures + +- [ ] Add the actual Google Patents screenshot as a legally appropriate test + fixture, or store a compact derived OCR fixture if redistributing the image is + undesirable. +- [ ] Unit-test OCR normalization and fuzzy matching for realistic line breaks, + punctuation, cropping, and name hyphenation. +- [ ] Add negative fixtures: unrelated patent, father-only evidence, a generic + Barricelli biography, low-quality/empty OCR, and prompt-injection-like text. +- [ ] Contract-test the semantic judge with a fake provider; CI must not call a + paid external model. +- [ ] Integration-test target upload -> one document -> completion flag -> goal + complete, including a repeat upload/evaluation. +- [ ] Integration-test father-only -> discovery flag -> goal still pending. +- [ ] Integration-test provider failure -> document retained -> retry succeeds. +- [ ] Integration-test two users/playthroughs so one player's evidence cannot + complete another player's level. +- [ ] Browser-test clipboard paste through the success state and Continue action. +- [ ] Run migrations against an empty database and an existing database at + migration `027`. +- [ ] Run the full unit/integration suite, production build, and Docker smoke test. + +## API shape to converge on + +Exact route naming may follow the repository's conventions, but the frontend and +backend branches should agree on a compact result like this before coding: + +```ts +type LevelGoalState = { + key: string + title: string + status: 'pending' | 'complete' + newlyCompleted: boolean + message?: string +} + +type DocumentAnalysis = { + extractionStatus: 'succeeded' | 'unsupported' | 'failed' + matchedFlags: string[] + awardedFlags: string[] + semanticStatus: 'not_needed' | 'available' | 'pending' | 'succeeded' | 'failed' + goals: LevelGoalState[] +} +``` + +`newlyCompleted` describes this mutation's effect and is not persisted as goal +state. Re-fetching a completed level returns `status: 'complete'` and +`newlyCompleted: false`. + +## Security, privacy, and cost limits + +- Player endpoints require the same JWT identity and level ownership checks as + playthrough progression; admin authoring remains admin-only. +- Limit upload bytes, OCR text sent to the model, model output tokens, request + duration, and retries. +- Do not expose answer anchors or semantic judging instructions to the browser. +- Do not trust filenames, MIME declarations, OCR text, or model output. +- Use schema validation and a confidence threshold before mutating flags. +- Store enough provenance to explain why a level cleared without retaining + unnecessary provider request/response payloads. +- A deterministic trusted-source match saves cost and is authoritative. The LLM + is never called merely to reconfirm it. + +## Explicitly out of scope + +- Terminal game, Glitch University signup, Dobby, and Glitch Hunter scenes + (Scenes 1–6). +- Scene 8's ceremony/3D luggage implementation and Scene 9's fire mystery. +- A general knowledge graph, Case Report, claims, red-thread reasoning, or + multi-document synthesis. +- Crawling the web, fetching a pasted URL, or validating a URL as a victory + requirement. +- Training a custom OCR or language model. +- Generalizing the story graph's future `llm_gate`; this slice may share a + provider adapter later, but does not depend on that larger feature. +- Automatic rejection or deletion of irrelevant player evidence. + +## Merge guidance for independent branches + +Prefer new modules and narrow glue commits. Current high-conflict files include +`server/index.ts`, `server/narrativeRepository.ts`, `src/App.tsx`, `src/main.tsx`, +and the play entrypoint. Assign one integrator to make the final small changes in +those files after the isolated work lands. + +Suggested merge order: + +1. S7-A schema/repository and clone support. +2. S7-B authored content and deterministic fixtures. +3. S7-C judge service/evaluation persistence. +4. S7-D story/API glue. +5. S7-E UI. +6. S7-F acceptance hardening. + +Each branch should state its migration dependency and avoid renumbering an +already-shared migration silently. If two branches need schema changes, reserve +migration numbers before implementation or keep one branch schema-free. + +## Definition of done + +From a fresh playthrough, a player reaches Scene 7 and sees the inventor +assignment. They paste one accepted Google Patents screenshot. One source +document appears on their board, the server persists the asset and OCR, the +authored match rule records an auditable evaluation, and the level obtains +`scene7.nils_inventor_proved`. The UI clearly confirms what the evidence proved +and offers Continue; the story then enters Scene 8. Reloading preserves the +document and completed state, repeating the evaluation grants nothing twice, +another player's level is unaffected, no URL was required, and +`barricelli_luggage` has not yet been awarded. +