Adding scene7 todo
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
Reference in New Issue
Block a user