# V1 Implementation Contracts

Date: 2026-07-08

This file turns the completed V1 decisions into implementation-ready contracts for search, native share, and future AI discussion handoff.

## Contract 1: Public-Domain Search Result

V1 provider: Project Gutenberg catalog index.

### Required Result Shape

```json
{
  "provider": "project_gutenberg",
  "providerId": "string",
  "title": "string",
  "authors": ["string"],
  "language": "string",
  "rightsLabel": "Public domain",
  "formats": [
    {
      "type": "text/plain | application/epub+zip | text/html",
      "url": "string",
      "sizeBytes": 0
    }
  ],
  "sourceUrl": "string",
  "acquisitionUrl": "string",
  "updatedAt": "ISO-8601 string"
}
```

### Blocking Rules

Disable “Add to Understand” when any of these are missing:

- `providerId`
- `title`
- at least one `authors` item
- `language`
- `rightsLabel`
- at least one supported import `formats` item
- `acquisitionUrl`

### Result UI Rules

- Results should read as books, not files.
- Show title, author, language, and public-domain/source label.
- Keep cards compact under the dominant current-listening card.
- Do not show file paths, catalog internals, section counts, or scraped-page metadata.
- If a result is not import-ready, show “Unavailable for import” rather than a disabled mystery button.

### Acceptance Checks

- Search returns only rights-safe, importable results.
- Duplicate Gutenberg IDs collapse to one visible result.
- Wrong-language results are visibly labeled before import.
- Missing format/acquisition data blocks import.
- Import starts from the same Reader preparation flow as paste/upload.

## Contract 2: Text-Only Native Share

V1 scope: selected text only.

### Accepted Payload Shape

```json
{
  "kind": "shared_text",
  "text": "string",
  "sourceApp": "string | null",
  "sourceTitle": "string | null",
  "sourceUrl": "string | null",
  "receivedAt": "ISO-8601 string",
  "platform": "ios | android"
}
```

### Accepted Inputs

- Plain text.
- Rich text converted to plain text.
- Optional OS-provided title/app/URL metadata.

### Rejected Or Deferred Inputs

- PDF files.
- EPUB files.
- URLs without selected text.
- Images or screenshots.
- Mixed attachment bundles.
- Empty text after rich-text conversion.

### User-Facing Flow

1. User selects text in another app.
2. User shares to Understand.
3. Understand opens the Reader preparation state.
4. The shared text appears as the Original.
5. Retelling preparation starts or waits behind an honest preparing state.
6. If the payload is unsupported, Understand shows a concise unsupported state and does not create a Library item.

### Acceptance Checks

- Shared text from browser/article app opens Reader preparation.
- Shared text from notes app opens Reader preparation.
- Empty share shows unsupported state.
- URL-only share shows unsupported state.
- PDF/EPUB share shows “Use Upload for files” until native file share is V1.1.
- No in-app “Share into Understand” button is introduced as a workaround.

## Contract 3: Saved-Annotation Discuss Handoff

V1 scope: preserve intent and context; full answer output is V1.1.

### Required Handoff Shape

```json
{
  "kind": "annotation_discuss_handoff",
  "annotationId": "string",
  "sourceItemId": "string",
  "selectedText": "string",
  "selectionRange": {
    "start": 0,
    "end": 0
  },
  "userNote": "string | null",
  "userQuestion": "string | null",
  "readerMode": "original | retelling",
  "retellingStyle": "closer | balanced | explain",
  "sourceTitle": "string",
  "contextBefore": "string",
  "contextAfter": "string",
  "createdAt": "ISO-8601 string"
}
```

### V1 UI Rules

- Discuss appears after an annotation exists, not in the first selection popover.
- The saved note remains editable even if discussion generation fails or is unavailable.
- The handoff should feel like a continuation of reading, not a global chat launch.
- Do not introduce a separate AI inbox, chat history, or assistant tab in V1.

### V1.1 Gates Before Full Answers

- Answer must cite or visibly anchor to the selected text/context.
- Original remains one tap away.
- Retry, failure, delete, share/export, and accessibility states are defined.
- Follow-up questions stay attached to the annotation.
- Answer playback rules are decided before any read-aloud answer feature ships.

### Acceptance Checks

- Saved note opens Discuss handoff with selected text and note context.
- Saved question opens Discuss handoff with selected text and question context.
- Missing selected text blocks handoff and keeps the annotation intact.
- Generation unavailable state does not delete or mutate the note.
- V1 UI does not show fake answer content.

## Contract 4: Contents Chapter/Section Background Rewrite

V1 scope: a reader taps Rewrite on a chapter or Contents position, and that row begins rewriting in the background without moving the listening position. Multiple rows can be rewriting at once; there is no separate Contents-level rewrite panel.

Dedicated plan: `contents-background-rewrite-plan.md`.

### Required Payload Shape

```json
{
  "kind": "contents_background_rewrite",
  "jobId": "string",
  "sourceItemId": "string",
  "contentsPositionId": "string",
  "contentsIndex": 0,
  "title": "string",
  "structureLabel": "chapter | section | epub_chapter | fallback_position",
  "scope": "contents_position",
  "rewriteStatus": "idle | preparing | ready | failed | canceled | expired",
  "progress": 0,
  "etaLabel": "string",
  "route": "local | cloud | offline_waiting",
  "readerMode": "retelling",
  "retellingStyle": "closer | balanced | explain",
  "voiceId": "string",
  "fidelity": "close | balanced | explain",
  "styleSnapshotLabel": "string",
  "styleSnapshotId": "string",
  "usesPreviousStyle": false,
  "createdAt": "ISO-8601 string",
  "updatedAt": "ISO-8601 string",
  "readyAt": "ISO-8601 string",
  "errorCode": "string",
  "canRetryLocal": true,
  "canTryCloud": false,
  "canCancel": true,
  "canRemove": false
}
```

### UI Rules

- Contents rows must keep navigation and Rewrite as separate actions.
- Tapping the title area navigates only.
- Tapping Rewrite starts a background rewrite for that chapter/section using the current style.
- Tapping Rewrite does not move the listening position.
- There is no separate Contents-level rewrite section or batch rewrite action in V1.
- The row itself must show preparing/ready/failure state, progress cue, and ETA when available.
- Contents should imply that free rewrites can take several minutes through row-level status copy, not a separate explainer panel.
- Original remains available while rewrites prepare in the background.
- Fallback positions may be rewritten, but the UI must keep their fallback label; do not rename them as real chapters.
- Ready row rewrites wait for explicit Listen/Use; they do not auto-switch playback.
- Use applies to the prepared row only by default.
- If playback reaches a still-preparing row, keep playing the current saved retelling and show a quiet "still preparing" cue.
- If Retelling settings change after a row rewrite starts, the row must either retain its style snapshot or clearly say it was prepared with a previous style.
- Reader, Contents button, and Library may show small return cues, but row status remains the management surface.
- Storage cleanup can remove prepared row rewrites without deleting Original text.
- Prototype default: prepared row rewrites expire after 30 days, local starts first, cloud is recovery/acceleration, and cross-device sync is deferred.

### Acceptance Checks

- Rewrite on current Contents row starts background preparation without interrupting playback.
- Rewrite on non-current row does not move the reader.
- Multiple rows can show rewriting state at once.
- Rewriting rows show row-level status, progress cue, and ETA.
- Ready state waits for explicit use/listen action.
- Cancel/remove keeps the previous saved retelling.
- Weak-structure imports still say fallback position, not chapter.
- Reaching a preparing row does not block playback.
- Reaching a ready row offers a choice before applying the prepared retelling.
- A row prepared with an older style is labeled before the reader uses it.
- Contents button and Reader audio row expose preparing/ready counts without creating a separate management surface.

## Implementation Order

1. Build the Project Gutenberg catalog result adapter and importability checks.
2. Implement text-only native share payload handling on iOS and Android.
3. Prototype Contents background rewrite lifecycle from `contents-background-rewrite-plan.md` Phase 1-3.
4. Implement Contents chapter/section background rewrite payload handling.
5. Persist saved-annotation Discuss handoff payloads.
6. Render the deepest Library, Reader, Contents, Import, Annotation, and Settings states once before implementation handoff.
