# GUPI OSINT Board roadmap

This is the ordered implementation roadmap following the accepted exhibit model. Work generally proceeds from top to bottom. It is not a substitute for migrations or implementation issues.

The narrative layer — campaigns, NPC cutscenes, the admin authoring panel, and the LLM cognitive shim that keeps players oriented through complex real-world scam cases — is tracked separately in [narrative-todo.md](narrative-todo.md). It depends on Milestone 5 (Case Report / Claims) for the assistant's read-only view of the player's reasoning.

## Working agreement

- Spend roughly 60% of development time on features and model work, 25% on tests and bug fixing, and 15% on repository and deployment hygiene.
- Stop feature work for interaction-breaking, data-loss, migration, or deployment bugs. Batch cosmetic defects separately.
- A new exhibit behavior is complete only when its PostgreSQL representation, API behavior, frontend widget, persistence, and focused tests agree.
- Mystery content must be authored as template data through supported application operations, never hard-coded into React components.

## Milestone 0: trustworthy baseline

- [x] Track `.gitignore`, `.dockerignore`, and `.env.example`; confirm that secrets and generated artifacts cannot enter Git accidentally.
- [x] Commit the current working POC and tag the checkpoint `poc-pre-exhibit-model`.
- [x] Confirm `main` is pushed to the Ramanujan-hosted `origin` repository.
- [x] Keep `npm run build` green and make `npm test` run real tests rather than an empty suite.

## Milestone 1: focused safety net

- [x] Test screen/board coordinate conversion across pan and zoom levels.
- [x] Test timeline date-to-pixel projection and recomputation after viewport resizing.
- [x] Test the interaction boundary between exhibit dragging, hand-tool panning, desktop wheel zoom, and mobile-only pinch zoom.
- [x] Test folder open/close behavior, retained file positions, and containment-band state.
- [x] Test document upload, metadata persistence, board save/reload, and reset.
- [x] Run migrations and API integration tests against disposable PostgreSQL, not SQLite or mocked persistence.
- [x] Add one browser smoke test: open an isolated level, drag an exhibit, pan, board-zoom, open a folder, move a file, reload, and verify persistence.

## Milestone 2: exhibit-schema cutover

- [x] Add boards, exhibits, exhibit types, subtype tables, immutable template versions, and mutable levels.
- [x] Cut over the explicitly disposable POC database directly; no transitional data existed to backfill or compare.
- [x] Implement template instantiation, version selection, reset, and “save level as template” as transactional clone operations.
- [x] Introduce a server-side repository/service boundary so SQL and cloning transactions do not live in Express route handlers.
- [x] Move exhibit and document rendering to a typed frontend widget registry backed by the normalized API.
- [x] Remove transitional `widgets`, `widget_relations`, and `playthrough_*` tables in the canonical cutover migration.

## Milestone 3: events and parties

### Events and narrative

- [x] Implement Event exhibits with occurrence time and investigator-authored narrative text.
- [x] Implement normalized Event-to-Evidence links and their distinct board visualization.
- [x] Present chronologically ordered events as the emerging investigation story.

### Party exhibits

- [x] Add a Party exhibit supertype representing an investigation participant.
- [x] Add a Person subtype with display name, normalized aliases, and extensible structured identity fields.
- [x] Add an Organization subtype covering businesses, public bodies, associations, and informal groups.
- [x] Add normalized many-to-many Party-to-Evidence associations with an optional explanatory note.
- [ ] Add typed Party-to-Party relationships such as employment, ownership, membership, control, and representation.
- [x] Build distinct Person and Organization dossier presentations that reveal associated evidence without using folder ownership semantics.
- [x] Include brief concepts, parties, aliases, evidence associations, and party relationships in template/level cloning.

## Milestone 4: first playable mystery

- [x] Design a small mystery that exercises documents, folders, notes, events, people, organizations, connections, and the timeline.
- [x] Create it as an immutable level-template version using the same supported operations available to an author.
- [x] Instantiate and solve a cloned level without modifying the template or relying on hard-coded case behavior.
- [x] Turn the successful solve path into an end-to-end acceptance test.
- [x] Perform a manual playability and visual-polish pass before production deployment.

## Milestone 5: claim-driven case report

The purpose of this milestone is the gameplay loop, not a knowledge graph: a Claim is a pinned proposition, source exhibits connect to it with red thread, and each thread's luggage-tag text explains how that source supports the Claim. Those explanations become the evidence section of the Case Report.

### 5.1 Lock the gameplay and temporal rules
- [x] Define a Claim as an independent pinned Exhibit which can receive one or more supporting document threads.
- [x] Treat a claim-to-document thread label as an evidentiary statement; new threads begin with `Proof that…`.
- [x] Use the same persisted connection statement on the luggage tag and in the report.
- [ ] Derive the Claim date from the earliest non-null temporal date of its two endpoint exhibits; never use `created_at` or the current time.
- [ ] Use Event `occurred_at` and a Document's primary timeline date as direct endpoint dates. For a Folder, use the earliest dated contained Document. Leave a Claim undated when neither endpoint supplies a date.
- [ ] Place undated Claims after dated Claims in the initial report order while keeping them fully editable and reorderable.
- [ ] Treat the derived date as the initial chronological suggestion only. Manual report ordering must not rewrite exhibit or Claim dates.
### 5.2 Add normalized persistence

- [x] Add normalized `claim_exhibits` as an Exhibit subtype; Claims are not encoded as connection labels.
- [ ] Move luggage-tag presentation fields out of `exhibit_connections`; retain evidentiary statement, curve tightness, and endpoints on the connection.
- [x] Add one board-owned `case_report` plus immutable level-owned submissions and normalized submission issues.
- [x] Assign stable, board-local display numbers to cite exhibits as `Exhibit 3` independently of board position, z-index, or report order.
- [ ] Enforce same-board ownership for the Claim's connection, both endpoint exhibits, report, and report membership.
- [ ] Delete a Claim and its report membership transactionally when its luggage tag is removed, while retaining the now-untagged thread.
- [ ] Delete both the Claim and connection when the thread itself is removed.
- [x] Clone board-owned Claims with fresh IDs during template creation and instantiation.
- [x] Make reset discard player-created Claims/report submissions and restore exactly the source template report configuration.
- [ ] Keep uploaded binary evidence in MinIO. Reports and Claims reference Document exhibits and asset metadata; they never duplicate or embed asset bytes.

### 5.3 Expose a focused API contract

- [ ] Add a typed Claim DTO to an investigative connection instead of exposing a free-form connection `label`.
- [ ] Return each Claim's derived date and its two stable exhibit citations in the report response.
- [ ] Add granular operations to create, edit, and remove a Claim without replacing the complete board state.
- [ ] Add a report endpoint that returns ordered Claim rows and an atomic reorder operation.
- [ ] Reject empty Claim text, invalid connection ownership, duplicate Claims on one connection, and report orders containing foreign or duplicate Claim IDs.
- [ ] Protect Claim and report writes with the board revision so concurrent saves cannot silently overwrite reasoning.

### 5.4 Turn luggage tags into Claim widgets

- [ ] Change the thread prompt from “Add tag” to “Explain this connection,” with a secondary option to leave the thread untagged.
- [ ] Create the Claim and its luggage-tag presentation in one interaction, preserving the current tightening animation.
- [ ] Keep both `LUGGAGE` and `COMPACT` as visual presentations of the same Claim entity while playtesting them.
- [ ] Preserve draggable percentage and tension-constrained lateral offset as Claim presentation state.
- [ ] Rename tag-oriented frontend types and commands to Claim terminology without changing the established visual design.
- [ ] Give a newly created Claim a subtle “added to report” ink animation or badge without interrupting board work.

### 5.5 Build the typewriter case report
The end goal is that a case report is prepopulated by the claims the player made during the investigation so that a skeleon of the case solution is present. The player must simply edit the case report and submit it. 

- [x] Add **Case Report** as a primary menu item and implement its first persistent report surface, separate from exhibits and the timeline.
- [ ] Provide an empty state that explains that explaining red threads will create the report, without revealing a solution or forcing a tutorial.
- [ ] Initially arrange Claim rows chronologically by derived date, using the order parameters on the claim as tie-breaker and undated Claims last, below a horizontal rule that says (missing date)
- [x] Render the Scene 7 Claim with typewritten, stable Exhibit citations and editable date/source provenance.
- [ ] It needs to be possible to add free text before and after the claims. Coloured inline text (use span elements)  have a specific class and id can be edited
- [ ] -Make Claim text editable inline. Persist through the Claim API so the luggage tag updates immediately.
- [ ] Support pointer and keyboard reordering of Claim rows and persist the resulting explicit report order into order column of the claim. 
- [ ] Clicking a Claim must minimize the report as appropriate, center its thread, and briefly illuminate the curve and luggage tag.
- [ ] Clicking an exhibit citation must locate and highlight that exhibit using the existing tray/board locator treatment.
- [x] Ensure the first Case Report surface is legible and operable on portrait mobile layouts as well as desktop.
- [ ] Add restrained typewriter, paper, and ink feedback while respecting reduced-motion preferences.

### 5.6 Test the reasoning loop

- [ ] Unit-test endpoint date resolution for Event, Document, Folder, partially dated, fully undated, and invalid-date cases.
- [ ] Migration-test the one-Claim-per-thread constraint, cascading behavior, same-board enforcement, and report ordering constraints.
- [ ] Integration-test create/edit/remove Claim, two-way text synchronization, report reorder, reload persistence, reset, and template cloning.
- [ ] Verify that Claims citing uploaded Document exhibits survive cloning while their immutable assets remain shared through MinIO.
- [ ] Add a browser test that creates several connections out of chronological order, explains them, opens the report, edits and reorders the Claims, then returns to each highlighted thread.
- [ ] Play the Glass Harbor mystery using Claims as the primary reasoning mechanism and record whether the generated report makes the conclusion emerge naturally.
- [ ] Revise prompts, animation, initial ordering, and report typography based on that playtest before adding automated evaluation.

### 5.7 Deliberately deferred

- [ ] Design a hidden, template-versioned solution rubric only after the deterministic Claim/report loop is enjoyable and dependable.
- [ ] Add LLM report evaluation as a later milestone with structured citations, calibrated uncertainty, and reproducible evaluator output.
- [ ] Do not add general-purpose semantic edge roles, Claim hubs, Claim-to-Claim links, or a knowledge-graph ontology as part of this milestone.

### Milestone 5 definition of done
A player can connect exhibits, explain each connection with a luggage-tag Claim, discover those exact words in a chronological typewriter report, improve and reorder the Claims, follow every citation back to the board, reload without loss, and reset safely to the template. No LLM is required for this experience to work.

