Files
PubScript/CHUNK#9.md
T

6.2 KiB

CHUNK #9 — AI Roles & Suggestions

Duration: ~3 days · Prereqs: CHUNK #8 (providers, token budget), CHUNK #4 (entities), CHUNK #3 (editor edit API) · Follows: CHUNK #8 · Unblocks: CHUNK #11 (parity: AI works in web + desktop identically)

Purpose: Bring the four agentic roles (Editor, Reader, Publisher, Co-Author) to life with robust base prompts, a context builder that pulls from the author's own data, AI SDK tool-calls, and — the signature feature — the suggestion protocol that renders AI feedback as inline, adoptable/rejectable/pinnable edits anchored to exact passages.


Steps

1. Base role prompts (packages/ai/src/roles/)

  1. Role type (packages/ai/src/roles/types.ts): Role = 'editor' | 'reader' | 'publisher' | 'co-author'. Each has a systemPrompt, temperature default, and tools[].
  2. Editor (line editor): tighten prose, kill filter words, sharpen dialogue tags, fix pacing, flag weak openings/closings, check consistency against character/world data. Emits targeted structured edits with rationale. Respects the author's voice.
  3. Reader (honest first-time reader): what was confusing, where attention dropped, what was telegraphed too early, what paid off. Reports as a reader, not in craft jargon.
  4. Publisher (market & packaging): comps, hook, pacing vs. genre, title/cover/blurb ideas, series potential, format decisions.
  5. Co-Author (collaborator with full project knowledge): drafts scenes/chapters in the author's voice, proposes plot turns, writes dialogue, expands world-building — always as suggestions the author accepts or rejects, never silently rewriting canon.
  6. Prompts live in docs/ROLE_PROMPTS.md as the human reference; packages/ai/src/roles/prompts.ts imports them.

2. Context builder (packages/ai/src/context.ts)

  1. buildContext(project, { targetDoc, role, maxTokens }):
    • Pull relevant characters (mentioned in target doc + top entities), world items, linked plot beats, prior chapter summaries.
    • Pack in priority order until the context budget (Chunk 8) is reached.
    • Return { system, contextText, sources: {entityId, title}[] } — sources powers the "what I used" panel.
  2. History: last N messages from the AISession, truncated to the history budget.

3. Tools (packages/ai/src/tools/)

AI SDK tool-calls so a role acts on the workspace:

  1. applySuggestion({ target, before, after, rationale, kind }) — registers a suggestion (Chunk 5 model).
  2. updateEntity({ id, fields }) — edits a character/world field (pending author approval in UI).
  3. addNote({ body, linkedTo }) — creates a note.
  4. draftChapter({ title, outline }) — seeds a chapter doc.
  5. queryProject({ q }) — search entities/chapters (returns snippets for the role).

4. Suggestion protocol (the signature feature)

  1. Structured output: roles emit JSON { target: {docId, range|nodePath}, before, after, rationale, kind: 'replace'|'insert'|'delete'|'entity-update'|'draft', status: 'pending' }. Enforce with the provider's JSON-mode + a local zod schema validator (retry on parse failure).
  2. Inline rendering (packages/editor/src/decorations/suggestions.ts): render each pending suggestion as an inline decoration over the exact passage — green for insert, yellow for replace, red for delete. Range is a node anchor, not a raw offset (re-resolved on every transaction — prevents drift, Chunk 3 risk).
  3. Actions: hover a decoration → Accept (applies the edit via applyEdit), Reject (removes it), Pin (copies it into a pinned note for later). Panel lists all pending suggestions with rationale + "what I used" sources.
  4. Co-Author drafts: render as a side-by-side diff view (original vs. draft) with accept/reject per paragraph.

5. Session UI (apps/web/src/features/ai/)

  1. AI panel (AiPanel.tsx): role selector, provider/model picker, temperature/thinking/context sliders, "Ask" button, streaming response view, pending suggestions counter.
  2. Sessions (AiSessionsView.tsx): list past sessions (from AISession), reopen to continue, export session transcript.
  3. Feedback loop: after accepting/rejecting, log the choice → future context can deprioritize rejected styles (v1: simple counter, not ML).

6. Tests

  1. Unit: buildContext packs the right entities within budget; sources populated.
  2. Unit: zod schema rejects malformed suggestion JSON.
  3. Unit: node-anchor resolution survives a doc edit (insert before range → range shifts correctly).
  4. E2E: start an Editor session → suggestion appears inline → Accept → text changes → suggestion cleared; Pin → note created.
  5. E2E: Co-Author draft → diff view → Reject → original intact.

Acceptance criteria (all must pass)

  • All four roles respond with role-appropriate output; base prompts enforced.
  • Suggestions render inline over the exact passage; accept/reject/pin all work.
  • Accepting applies the edit; rejecting removes it; Pin creates a note.
  • "What I used" lists the entities/context the role drew from.
  • Node-anchored ranges stay correct across typing (no drift).
  • Co-Author draft shows a paragraph-level diff; accept/reject work.
  • Sessions persist; reopen continues the conversation.
  • Contract suite still green on both adapters.
  • Web UI integration (apps/web/src/features/ai/AI.tsx): the AI session view is wired to AIStore — session sidebar (create/rename/delete/reopen), messages/suggestions/notes tabs, and full CRUD. apps/web/src/routes/AI.tsx now renders this feature view.

Definition of done

AI feedback is integrated into the writing tools, not a chat sidebar: every suggestion is anchored to a passage and adoptable/rejectable/pinnable, and the roles act on the workspace via tools. This is the signature PubScript feature.

Risks & fallbacks

  • Range drift over live editing → node anchors, not raw offsets; re-resolve on every transaction (built into Chunk 3's decoration pipeline).
  • Scope creep in roles → roles are prompt + tools, not new UI surfaces; the suggestion protocol is the single integration point.
  • LLM JSON reliability → strict zod validation + retry; fall back to a structured template if the model refuses JSON.