265 lines
11 KiB
Markdown
265 lines
11 KiB
Markdown
# Scene 7: prove Barricelli was an inventor
|
||
|
||
Status: **implemented and verified** on `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 a minimal OSINT board, reads **“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 written report are
|
||
not required.
|
||
|
||
```text
|
||
paste screenshot -> document appears -> text is read -> source is verified
|
||
-> Scene 7 complete -> continue to Scene 8
|
||
```
|
||
|
||
## Product decisions
|
||
|
||
- One suitable Google Patents screenshot is sufficient.
|
||
- Clipboard paste and file upload use the same server path.
|
||
- The image and OCR remain a real source document on the player's board.
|
||
- A known-source fuzzy match is the fast, deterministic victory path.
|
||
- A small semantic judge is a fallback for other credible sources and for
|
||
distinguishing Nils from his father. It cannot overrule the trusted path.
|
||
- Evidence about Barricelli's father may award an optional discovery but does not
|
||
clear the assignment unless it also supports the claim about Nils.
|
||
- The boarding-house fire belongs to the later age/rescue assignment, not Scene 7.
|
||
- Scene 7 records `scene7.nils_inventor_proved`. Scene 8 owns the ceremony and
|
||
awards `barricelli_luggage`.
|
||
- Inconclusive evaluation never deletes or penalizes uploaded evidence.
|
||
|
||
## Shared identifiers
|
||
|
||
| Purpose | Key |
|
||
|---|---|
|
||
| Goal | `barricelli.inventor-proof` |
|
||
| Scene 7 completion | `scene7.nils_inventor_proved` |
|
||
| Optional father discovery | `scene7.father_inventor_discovered` |
|
||
| Scene 8 reward | `barricelli_luggage` |
|
||
|
||
All identifiers and content are template data. There must be no Barricelli
|
||
conditional in React or server business logic.
|
||
|
||
## Existing foundation
|
||
|
||
- `025_level_document_flags.sql`: clonable document gates and level flags.
|
||
- `026_achievements.sql`: playthrough achievements.
|
||
- `027_evidence_text_matching.sql`: asset OCR, board match rules/anchors,
|
||
level-owned evaluations, and flag provenance.
|
||
- `028_level_goals.sql`: board-owned goals and normalized flag requirements.
|
||
- `029_semantic_evidence_judging.sql`: clonable semantic rules, level-owned
|
||
evaluations, and semantic flag provenance.
|
||
- `030_evidence_match_source_metadata.sql`: author-only canonical source metadata.
|
||
- `server/ocr.ts`: plain-text extraction and Tesseract.
|
||
- `server/evidenceMatching.ts`: OCR-tolerant fuzzy passage matching.
|
||
- `POST /api/levels/:id/documents`: persistent upload plus OCR and matching.
|
||
- The story runtime already tracks `current_node_id` and `current_level_id`.
|
||
|
||
## Architecture contract
|
||
|
||
### Recognition and completion are separate
|
||
|
||
Recognition answers what a document supports. A goal answers whether the level's
|
||
authored requirements have been satisfied. `level_goals` and
|
||
`level_goal_flag_requirements` clone with a template. Goal completion is derived
|
||
from level flags; there is no second mutable completion boolean.
|
||
|
||
Play mode receives a goal's key, title, instructions, completion copy, status,
|
||
and completion time. IDs, enabled state, required flags, target text, and judging
|
||
prompts remain author-only.
|
||
|
||
### Known-source fast path
|
||
|
||
The Scene 7 template owns an `evidence_match_rule` with distinctive text visible
|
||
in the real Google Patents result: a combination of patent number/title, inventor
|
||
name, and invention language. A name alone is too generic. When enough anchors
|
||
match, the existing matcher awards `scene7.nils_inventor_proved` in the upload
|
||
transaction and the goal becomes complete immediately.
|
||
|
||
Reference OCR, thresholds, source metadata, and copy live in the manifest/database,
|
||
not TypeScript constants. The expected text is never returned to play mode.
|
||
|
||
### Semantic fallback
|
||
|
||
A provider-neutral `EvidenceJudge` receives only allowlisted goal data and OCR
|
||
text. It returns strictly validated structured data:
|
||
|
||
```ts
|
||
type EvidenceVerdict = {
|
||
subject: 'target' | 'related' | 'ambiguous' | 'neither'
|
||
supportsClaim: boolean
|
||
evidenceExcerpt: string
|
||
confidence: number
|
||
}
|
||
```
|
||
|
||
OCR is untrusted quoted material. The judge prompt explicitly ignores instructions
|
||
inside it. Provider/model, timeout, input limit, and confidence threshold are
|
||
environment configuration. Semantic configuration clones with the board;
|
||
evaluation history belongs to the level/document/extraction and records evaluator
|
||
version, provider/model, verdict, excerpt, confidence, timestamps, and sanitized
|
||
failure state.
|
||
|
||
| Verdict | Mutation |
|
||
|---|---|
|
||
| Target + assertion supported at threshold | authored success flag (`scene7.nils_inventor_proved`) |
|
||
| Related subject only + assertion supported | authored related flag (`scene7.father_inventor_discovered`) |
|
||
| Ambiguous, unsupported, or below threshold | none |
|
||
| Provider failure | none; retryable |
|
||
|
||
The known-source pass avoids an LLM call. After a deterministic miss, the client
|
||
automatically calls an idempotent semantic endpoint. This keeps document upload
|
||
durable even if an external provider times out, without requiring a demo job queue.
|
||
|
||
### Story progression
|
||
|
||
Advancing from a level is server-authoritative. The server verifies the JWT user,
|
||
active playthrough/current level, and enabled goal state. It promotes the Scene 7
|
||
completion fact idempotently and only then follows the level terminal. The browser
|
||
cannot grant its own achievement.
|
||
|
||
The player must see the verification result before navigation. Success exposes a
|
||
single Continue action to Scene 8. Scene 6 only wires into the Scene 7 level node;
|
||
Scene 8 owns the luggage reward.
|
||
|
||
## Work packages
|
||
|
||
### S7-A — Goal model and cloning
|
||
|
||
- [x] Add normalized board goals and flag requirements in migration `028`.
|
||
- [x] Clone goals during freeze, instantiation, and reset.
|
||
- [x] Derive pending/complete state from level flags.
|
||
- [x] Hide goal requirements from play payloads.
|
||
- [x] Return newly completed goals from document upload.
|
||
- [x] Add admin CRUD repository/API contracts.
|
||
- [x] Test migration, cloning, deterministic completion, and isolation.
|
||
- [x] Clear level flags/reveal state when resetting from a template.
|
||
|
||
### S7-B — Barricelli content and trusted recognition
|
||
|
||
- [x] Add goals and evidence-match rules to the normal mystery manifest importer.
|
||
- [x] Create the Scene 7 template with its assignment and no solution-bearing
|
||
starting document.
|
||
- [x] Add a reproducible screenshot-paste acceptance path that runs through local OCR.
|
||
- [x] Author three distinctive patent anchors.
|
||
- [x] Tune against the target and unrelated/father-only negative fixtures.
|
||
- [x] Award `scene7.nils_inventor_proved` and require it for the level goal.
|
||
- [x] Store canonical source metadata for administrators; keep player URL optional.
|
||
|
||
### S7-C — Semantic judge
|
||
|
||
- [x] Add provider-neutral interface and strict verdict validation.
|
||
- [x] Add clonable semantic rule configuration and level-owned evaluations.
|
||
- [x] Add semantic evaluation provenance to level flags.
|
||
- [x] Add provider/model/timeout/input/confidence environment configuration.
|
||
- [x] Send OCR text rather than raw image bytes.
|
||
- [x] Add ownership-checked, idempotent judge endpoint.
|
||
- [x] Route target and father verdicts to their authored flags.
|
||
- [x] Make disabled provider, timeout, quota, and malformed output safe/retryable.
|
||
- [x] Do not log evidence text or secrets.
|
||
|
||
### S7-D — Story bridge
|
||
|
||
- [x] Require completed enabled goals before a playthrough can leave a level.
|
||
- [x] Verify the current playthrough belongs to the JWT user and owns the level.
|
||
- [x] Promote required completion facts exactly once.
|
||
- [x] Return a useful pending-goal error rather than advancing early.
|
||
- [x] Return to the generic story runtime after success; the Scene 6/8 graph owner
|
||
wires the actual neighboring nodes.
|
||
- [x] Keep the arbitrary achievement-grant route development-only.
|
||
|
||
### S7-E — Board experience
|
||
|
||
- [x] Present the active goal prominently on Scene 7.
|
||
- [x] Preserve clipboard paste, drag/drop, and file picker equivalence.
|
||
- [x] Show source import/OCR and semantic checking stages.
|
||
- [x] Highlight the accepted document and show **SOURCE VERIFIED — NILS AALL
|
||
BARRICELLI: INVENTOR**.
|
||
- [x] Show Continue only after server-confirmed completion.
|
||
- [x] Acknowledge father-only discovery while asking for evidence about Nils.
|
||
- [x] Keep inconclusive evidence and offer neutral guidance.
|
||
- [x] Respect reduced-motion and mobile layouts.
|
||
|
||
### S7-F — Verification
|
||
|
||
- [x] Add a legally safe derived OCR fixture.
|
||
- [x] Test line breaks, punctuation, cropping, name hyphenation, unrelated patent,
|
||
father-only text, empty OCR, and prompt-injection-like text.
|
||
- [x] Contract-test with a fake semantic provider; CI never calls a paid model.
|
||
- [x] Integration-test target upload -> document -> flag -> completed goal.
|
||
- [x] Test duplicate evaluation, provider failure/retry, two-user isolation, and
|
||
story progression before/after completion.
|
||
- [x] Browser-test clipboard image paste through OCR and verification. Continue
|
||
is covered by the generic story-gate integration until the Scene 8 node lands.
|
||
- [x] Run migrations on an empty database and one currently at `027`.
|
||
- [x] Run unit, integration, build, and Docker smoke tests.
|
||
|
||
## API shape
|
||
|
||
```ts
|
||
type LevelGoal = {
|
||
key: string
|
||
title: string
|
||
instructions: string
|
||
completionMessage: string
|
||
status: 'pending' | 'complete'
|
||
completedAt?: string
|
||
newlyCompleted: boolean
|
||
}
|
||
|
||
type DocumentAnalysis = {
|
||
extractionStatus: 'succeeded' | 'unsupported' | 'failed'
|
||
matchedFlags: string[]
|
||
awardedFlags: string[]
|
||
goals: LevelGoal[]
|
||
}
|
||
```
|
||
|
||
`newlyCompleted` is response-local: a reload returns complete with
|
||
`newlyCompleted: false`.
|
||
|
||
## Security and cost limits
|
||
|
||
- Require identity and level ownership on player mutations.
|
||
- Limit upload bytes, OCR/model characters, output tokens, duration, and retries.
|
||
- Never expose reference anchors or judge instructions to play mode.
|
||
- Treat filenames, MIME declarations, OCR, and model output as untrusted.
|
||
- Validate model output and confidence before mutating flags.
|
||
- Persist enough provenance to explain completion without storing unnecessary raw
|
||
provider payloads.
|
||
|
||
## Out of scope
|
||
|
||
- Scenes 1–6, Scene 8's ceremony/3D model, and Scene 9's fire mystery.
|
||
- Knowledge graph, Case Report, claims, red-thread reasoning, or multi-document
|
||
synthesis.
|
||
- Web crawling, URL fetching, or requiring a URL for victory.
|
||
- Custom model training or the general story graph `llm_gate`.
|
||
- Deleting irrelevant evidence.
|
||
|
||
## Merge guidance
|
||
|
||
Prefer new modules and narrow glue commits. High-conflict files are
|
||
`server/index.ts`, `server/narrativeRepository.ts`, `src/App.tsx`, `src/main.tsx`,
|
||
and `src/play.tsx`; one integrator should own their final changes.
|
||
|
||
Suggested order: S7-A schema -> S7-B deterministic content -> S7-C judge -> S7-D
|
||
story bridge -> S7-E UI -> S7-F hardening. Reserve migration numbers before
|
||
parallel schema work and never renumber an already-shared migration silently.
|
||
|
||
## Definition of done
|
||
|
||
From a fresh playthrough, the player reaches Scene 7 and pastes one accepted
|
||
Google Patents screenshot. One source document appears; MinIO asset, OCR, match
|
||
evaluation, and `scene7.nils_inventor_proved` are persisted; the goal completes;
|
||
the UI explains what was proved and offers Continue; and the story enters Scene 8.
|
||
Reload and duplicate evaluation are idempotent, another player's level is
|
||
unaffected, no URL was required, and `barricelli_luggage` is not awarded until
|
||
Scene 8.
|