← Back to Chronicles

System Architecture

How qino Chronicles transforms ecosystem evolution into story worlds

qino-chronicles System Reference

Complete reference for the qino-chronicles publishing and display system.

System Overview

qino-chronicles transforms markdown-based chronicles from source repositories into a web reading experience with generated images. The system spans three repositories and uses Cloudflare infrastructure (Workers, R2, Queues, KV).

High-Level Architecture

The complete system: source repos publish to backend, which stores content and queues image generation. Frontend serves markdown while browsers fetch images directly from public R2.

┌─────────────────────┐                        ┌──────────────────────┐
│   Source Repos      │     GitHub Action      │  Backend Worker      │
│   (qino-claude,     │ ──────────────────────▶│                      │
│    concepts-repo)   │     POST /publish      │  ┌────────────────┐  │
└─────────────────────┘                        │  │ Publish Route  │  │
                                               │  └───────┬────────┘  │
                                               │          │           │
                                               │  ┌───────▼────────┐  │
                                               │  │ Image Queue    │  │
                                               │  └───────┬────────┘  │
                                               │          │           │
                                               │  ┌───────▼────────┐  │
                                               │  │ Queue Consumer │──┼──▶ AI Image APIs
                                               │  └────────────────┘  │    (Gemini, FLUX)
                                               └──────────┬───────────┘
                                                          │
                              ┌────────────────────────────┼────────────────────────────┐
                              │                            │                            │
                              ▼                            ▼                            ▼
                    ┌─────────────────┐          ┌─────────────────┐          ┌─────────────────┐
                    │  R2: Content    │          │  R2: Images     │          │  KV: Status     │
                    │  (markdown)     │          │  (public URL)   │          │  (generation)   │
                    └────────┬────────┘          └────────┬────────┘          └─────────────────┘
                             │                            │
                             │                            │ direct browser access
                             ▼                            ▼
                    ┌─────────────────────────────────────────────────────────┐
                    │                   Frontend Worker                       │
                    │                   (TanStack Start)                      │
                    └─────────────────────────────────────────────────────────┘
                                               │
                                               ▼
                                          ┌─────────┐
                                          │ Browser │
                                          └─────────┘

Publishing Flow

When chapter content changes in a source repo, a GitHub Action posts it to the backend, which stores markdown in R2 and queues image generation jobs.

┌──────────────┐    ┌─────────────────┐    ┌─────────────┐    ┌─────────────┐
│ Git Push     │───▶│ GitHub Action   │───▶│ Backend     │───▶│ R2 Content  │
│ chronicle/** │    │                 │    │ /publish    │    │             │
└──────────────┘    └─────────────────┘    └──────┬──────┘    └─────────────┘
                                                  │
                                                  ▼
                                           ┌─────────────┐
                                           │ Image Queue │
                                           └─────────────┘

Image Generation Flow

Queue consumer processes jobs asynchronously: classifies tokens, builds prompts from chapter context, calls AI APIs, and stores results in public R2.

┌─────────────┐    ┌─────────────────┐    ┌─────────────┐    ┌─────────────┐
│ Image Queue │───▶│ Queue Consumer  │───▶│ AI APIs     │───▶│ R2 Images   │
│             │    │ (prompt build,  │    │ (Gemini,    │    │ (public)    │
│             │    │  classification)│    │  FLUX)      │    │             │
└─────────────┘    └─────────────────┘    └─────────────┘    └─────────────┘

Reading Flow

Browser requests go to frontend worker for markdown content. Images load directly from public R2 URL, bypassing the worker for better performance.

┌─────────┐    ┌─────────────────┐    ┌─────────────┐
│ Browser │───▶│ Frontend Worker │───▶│ R2 Content  │  (markdown via worker)
│         │    │                 │    └─────────────┘
│         │    └─────────────────┘
│         │
│         │─────────────────────────▶ R2 Images (public URL, direct access)
└─────────┘

Repositories

1. Source Repositories (e.g., qino-claude)

Location: ~/Code/qinolabs/qino-claude/

Chronicle content lives here. Each source repo contains:

chronicle/
├── manifest.json      # Chronicle metadata and chapter index
├── world-seed.md      # The world's seed/setting with Setting Foundation frontmatter
├── world.md           # Living world-state (characters, locations, pressures)
├── arcs.md            # All arcs (in motion + completed) with chapter ranges
└── chapters/
    ├── 001-the-arrival/
    │   ├── chapter.md   # Chapter content
    │   ├── world.md     # Snapshot at write time
    │   └── arcs.md      # Snapshot at write time
    └── ...

Key Files:

FilePurpose
manifest.jsonChronicle title, chapter list with slugs/titles/dates/git refs
world-seed.mdSetting Foundation (YAML frontmatter) + prose atmosphere (seed for generation)
world.mdCharacters, locations, pressures, wanderer state, season, current breath
arcs.mdNarrative arcs with chapter ranges for temporal display
chapters/*/chapter.mdIndividual chapter content with World Tokens sections
chapters/*/world.mdSnapshot of world.md at the time the chapter was written
chapters/*/arcs.mdSnapshot of arcs.md at the time the chapter was written

Example (qino-claude): chapters/001-what-the-water-brings/ holds the first chapter where The Mender token is introduced; its world.md snapshot does not yet include upstream locations that appear in later chapters.

GitHub Action: .github/workflows/chronicle-publish.yml

Triggers on push to main when chronicle/** changes. Reads all chronicle files and POSTs to backend.

2. Backend (qino-chronicles-backend)

Location: ~/Code/qinolabs/qinolabs-repo/apps/qino-chronicles-backend/

Cloudflare Worker that receives published content, stores in R2, and manages image generation.

Key Files:

FilePurpose
src/routes/publish.tsReceives chronicle content, stores in R2, queues image jobs
src/routes/status.tsReturns image generation status for chapters
src/routes/manifest.tsReturns merged image manifests for chapters
src/queue-handler.tsProcesses image generation jobs from queue
wrangler.jsoncCloudflare bindings (R2, KV, Queue)

Cloudflare Bindings:

BindingTypePurpose
CONTENT_BUCKETR2Stores markdown content (world.md, arcs.md, chapters)
IMAGES_BUCKETR2Stores generated images
IMAGE_STATUSKVTracks image generation status per chapter
IMAGE_QUEUEQueueQueues image generation jobs

3. Frontend (qino-chronicles)

Location: ~/Code/qinolabs/qinolabs-repo/apps/qino-chronicles/

TanStack Start app that reads from R2 and displays chronicles.

Key Files:

FilePurpose
src/server/get-chronicles.tsServer functions to fetch/parse R2 content
src/lib/chronicles.tsHelper functions for parsing world.md
src/routes/$repoName/$chapterSlug/index.tsxChapter display route
src/components/chapter-end-matter.tsxArcs and tokens display
src/components/journal-book.tsxArc details dialog

Data Flow

Phase 1: Publishing (Source → Backend → R2)

┌──────────────┐    ┌─────────────────┐    ┌─────────────┐    ┌─────┐
│ Git Push to  │───▶│ GitHub Action   │───▶│ POST        │───▶│ R2  │
│ main branch  │    │ chronicle-      │    │ /publish    │    │     │
│              │    │ publish.yml     │    │             │    │     │
└──────────────┘    └─────────────────┘    └─────────────┘    └─────┘

Step-by-step:

1. Git push to source repo's main branch with changes in chronicle/**

2. GitHub Action (chronicle-publish.yml) triggers and reads files:

chronicle/world.md → WORLD_CONTENT
chronicle/world-seed.md → THEME_CONTENT
chronicle/arcs.md → ARCS_CONTENT
chronicle/manifest.json → MANIFEST_JSON
chronicle/chapters/*/chapter.md → CHAPTERS_JSON array (with optional world/arcs snapshots)

Example payload rows:
- `chapters[0]`: slug `001-the-arrival` (concepts-repo) with world/arcs snapshots taken after Chapter 1 edits
- `chapters[1]`: slug `001-what-the-water-brings` (qino-claude) with tokens **The Mender**, **The Meeting Point**

3. POST to backend /publish endpoint:

{
  "repoName": "qino-claude",
  "world": "# The World\n...",
  "theme": "# Theme\n...",
  "arcs": "# Arcs\n...",
  "manifest": { "title": "...", "lastGitRef": "..." },
  "chapters": [
    { "slug": "001-first", "content": "# Title\n..." }
  ],
  "force": false
}

4. Backend processes (publish.ts):

  • Validates API key and repo allowlist
  • Extracts metadata from chapters (title, dates, git refs)
  • Updates repos.json index
  • Queues image generation jobs
  • Stores in R2:
chronicle-content/
└── qino-claude/
    ├── manifest.json    (generated with chapter metadata)
    ├── world.md
    ├── world-seed.md
    ├── arcs.md
    └── chapters/
        ├── 001-first/
        │   ├── chapter.md   (chapter content)
        │   ├── world.md     (snapshot)
        │   └── arcs.md      (snapshot)
        └── ...

Phase 2: Reading (Frontend ← R2)

┌──────────┐    ┌─────────────────────┐    ┌─────┐
│ Browser  │───▶│ Frontend Worker     │───▶│ R2  │
│ Request  │    │ get-chronicles.ts   │    │     │
└──────────┘    └─────────────────────┘    └─────┘

Step-by-step:

1. Browser requests /{repoName}/{chapterSlug}/

2. Route loader calls server functions:

// get-chronicles.ts
const result = await getChapterData({
  data: { repoName, chapterSlug }
});

3. Server functions fetch from R2:

// Fetches in parallel:
// - manifest.json → chapter list, metadata
// - world.md → characters, locations, pressures
// - arcs.md → all arcs with chapter ranges
// - chapters/{slug}/chapter.md → chapter content (snapshots stored alongside)

Example (concepts-repo Chapter 2): Loader pulls canonical `world.md` (now includes "The Alcove"), but hover cards favor Chapter 2 tokens for **Selin** ("Eyes like deep water. Watches what appears in adjacency.") over the canonical description.

4. Parsing:

// get-chronicles.ts
const arcs = parseArcs(arcsContent);  // → Arc[]

// chronicles.ts
const world = parseWorld(worldContent);  // → { characters, locations } with descriptions

5. Entity highlighting (builds map for hover cards):

// highlighted-markdown.tsx
const entityInfoMap = buildEntityInfoMap(
  world.characters,  // from world.md with descriptions
  world.locations,   // from world.md with descriptions
  chapter.tokens,    // current chapter's World Tokens
  historicalTokens,  // tokens from previous chapters
);

6. Arc filtering (in route loader):

// Filter arcs for current chapter
const currentChapterNum = chapterIndex + 1;
const { arcsInMotion, completedArcs } = filterArcsForChapter(
  chronicle.arcs,
  currentChapterNum
);

Example (qino-claude, Chapter 6): arcsInMotion includes "The Journey Upstream" (6-) and "The Wanderer's Hands" (1-); completedArcs includes "The Pattern-Keeper's Question" (2-3).

7. Render with filtered data


Key Interfaces

arcs.md Format

# Arcs

*Narrative threads — in motion and complete.*

---

## [Arc Name]

*Shape:* [hidden / motion / building / between / calling outward]
*Chapters:* [start]-[end] or [start]- (ongoing)

**In motion:**
*Holds:* [the question this arc explores]
*Moves toward:* [direction, not script]

**Completed:**  (only for completed arcs)
*Resolved:* [how it ended]
*Yielded:*
- [outcome 1]
- [outcome 2]
*Changed between people:*
- [relational shift]

Chapter range syntax:

  • *Chapters:* 2-5 — Completed arc (chapters 2 through 5)
  • *Chapters:* 6- — In-motion arc (started chapter 6, ongoing)

Example (qino-claude): ## The Journey UpstreamShape: calling outward, Chapters: 6-, holds "What opened in the interior? What is the pouch?" and moves toward "Arrival at the coordinates — and whatever waits there."

Arc TypeScript Interface

// get-chronicles.ts
export interface Arc {
  name: string;
  shape: string;
  startChapter: number;
  endChapter: number | null;  // null = in motion
  // In motion fields (always present)
  holds: string;
  movesTo: string;
  // Completed fields (only if endChapter !== null)
  resolved?: string;
  yielded?: string[];
  changedBetweenPeople?: string[];
}

Chronicle Interface

export interface Chronicle {
  repoName: string;
  manifest: ChronicleManifest;
  world: string;
  arcs: Arc[];           // Unified arcs from arcs.md
  journal: JournalEntry[]; // Legacy, kept for compatibility
  chapters: Chapter[];
}

Publish Payload Schema

// publish.ts
const publishPayloadSchema = z.object({
  repoName: z.string().min(1),
  chapters: z.array(chapterSchema).min(1),
  world: z.string().optional().default(""),
  theme: z.string().optional().default(""),
  journal: z.string().optional().default(""),  // Legacy
  arcs: z.string().optional().default(""),     // Unified arcs
  manifest: sourceManifestSchema.optional(),
  removedChapters: z.array(z.string()).optional(),
  force: z.boolean().optional().default(false),
});

Arc Filtering Logic

The frontend filters arcs based on chapter number for temporal display:

function filterArcsForChapter(
  arcs: Arc[],
  chapterNum: number
): { arcsInMotion: Arc[]; completedArcs: Arc[] } {
  const arcsInMotion: Arc[] = [];
  const completedArcs: Arc[] = [];

  for (const arc of arcs) {
    // In motion: started at/before this chapter AND not yet completed
    const isInMotion =
      arc.startChapter <= chapterNum &&
      (arc.endChapter === null || arc.endChapter > chapterNum);

    // Completed: has endChapter at or before this chapter
    const isCompleted =
      arc.endChapter !== null && arc.endChapter <= chapterNum;

    if (isInMotion) {
      arcsInMotion.push(arc);
    } else if (isCompleted) {
      completedArcs.push(arc);
    }
  }

  return { arcsInMotion, completedArcs };
}

Example: For a chronicle with these arcs:

  • Arc A: Chapters 1-3 (completed)
  • Arc B: Chapters 2-5 (completed)
  • Arc C: Chapters 4- (in motion)
Reading ChapterArcs in MotionCompleted Arcs
Chapter 1A-
Chapter 2A, B-
Chapter 3BA
Chapter 4B, CA
Chapter 5CA, B
Chapter 6CA, B

Entity Highlighting & Hover Cards

The frontend highlights character and location names in chapter text and displays hover cards with contextual information.

Data Sources

SourceScopeContains
world.md Characters/LocationsWorldCanonical list with descriptions
Chapter World TokensChapterEvocative presence text for this chapter
Historical TokensCumulativeTokens from previous chapters

Presence Priority Chain

When determining what to show in a hover card, the system uses this priority:

1. Current chapter's token presence  (most specific)
         ↓ if not found
2. Historical token presence         (from previous chapters)
         ↓ if not found
3. world.md description              (canonical fallback)

Example:

EntityHas Token?Hover Card Shows
SelinYes (Ch2 token)"Eyes like deep water. Watching what the wanderer will do next."
The Entrance HallYes (Ch1 token)"Where the work happens after dark..."
The Mountain SpaceportNo tokenworld.md description: "A single berth carved into the mountainside..."

Concrete content:

  • Concepts-repo Chapter 1 token for The Entrance Hall → hover text shows warm arguments and cold tea; reading Chapter 2 still uses this unless overridden.
  • qino-claude Chapter 1 token for The Mender → hover text stays "Works where river meets sea..." even when Chapter 3 adds inland arcs, until a new token updates her presence.
  • Presence shifts over time: if concepts-repo Chapter 3 later adds a token for The Entrance Hall about midnight quiet, that new token would take precedence for that chapter, while earlier chapters remain anchored to the Chapter 1 presence.

How It Works

1. parseWorld() extracts characters and locations from world.md with their descriptions:

interface WorldEntity {
  name: string;
  content?: string;  // Description text from world.md
}

2. buildEntityInfoMap() creates a lookup map:

  • Adds all characters and locations from world.md
  • For each entity, resolves presence using the priority chain
  • Assigns highlight colors by type (warm for characters, cool for locations)

3. HighlightedMarkdown component:

  • Scans text for entity name matches
  • Wraps matches in colored spans
  • If presence exists, adds hover card trigger

world.md Format (pressures, breath, season, entities)

Top sections carry ambient context that accumulates:

  • Pressures: What's building/calling. Example (concepts-repo): The Dark Below → "Something moved down there, patient as stone."
  • The World's Breath: Current atmosphere snapshot. Example: "Morning light through narrow windows. The crystals have shifted their alignment..."
  • The Season: Temporal anchor. Example: "Early in the wanderer's time... Something is beginning — not yet visible, but present..."
  • The Wanderer: State and carries.
  • Characters / Locations: Canonical descriptions (see parsing below).
## Characters

### Selin
First appeared: Chapter 1 (as "the woman by the window"), named Chapter 2

Cartographer, but not of distances. Eyes like deep water — a color
that shifts with the light. Studies arrivals the way the crystals
study the sky.

### The Tea-Bearer
First appeared: Chapter 1, in the entrance hall

Young or ageless — hard to categorize. Moves between groups with
a tray, tending the nightly ritual.

## Locations

### The Mountain Spaceport
A single berth carved into the mountainside. Amber lamps. The path
to the observatory is visible as a pale line against darker rock.

Notes:

  • ### Name headers define entity names
  • "First appeared:" lines are stripped from descriptions
  • All text after the header until the next ### becomes the description

Chapter World Tokens Format

## World Tokens

**The Entrance Hall**
Where the work happens after dark. Clusters of conversation,
arguments over charts, tea grown cold during debate.

**Selin**
Eyes like deep water. Watching what the wanderer will do next.

World Tokens provide chapter-specific "presence" — how an entity feels in this moment, not its canonical description.

Key Files

FilePurpose
src/lib/chronicles.tsparseWorld() — extracts entities with descriptions
src/components/highlighted-markdown.tsxbuildEntityInfoMap() — presence resolution
src/components/entity-hover-card.tsxHover card UI component

Image Generation

The backend generates images for World Tokens in each chapter using AI image generation.

What Gets Images

Image TypeSourceTrigger
HeroChapter content analysisNew/changed chapter
Wanderer## The Wanderer sectionNew/changed wanderer section
AtmosphereMid-chapter breath pointNew/changed chapter
Tokens## World Tokens sectionNew/changed tokens

Differential Regeneration

The backend uses content hashing to avoid regenerating unchanged images:

// Content hashes stored in manifest
contentHashes: {
  chapter: "abc123",    // Hash of chapter content
  wanderer: "def456",   // Hash of wanderer section
  theme: "ghi789",      // Hash of world-seed.md
  world: "jkl012",      // Hash of world.md
  tokens: {             // Hash per token
    "selin": "mno345",
    "the-entrance-hall": "pqr678"
  }
}

Regeneration triggers:

  • Theme or world changes → regenerate everything (affects visual style)
  • Chapter content changes → regenerate hero + atmosphere
  • Wanderer section changes → regenerate wanderer image
  • Token presence changes → regenerate that specific token
  • New token → generate it
  • Removed token → clean up image

Token-to-Image Flow

Chapter published with World Tokens
        │
        ▼
Backend extracts tokens from chapter.md
        │
        ▼
Compare token hashes with existing manifest
        │
        ├─► New token (no hash) → Generate image
        ├─► Changed token (hash differs) → Regenerate
        └─► Unchanged token → Skip (keep existing)

Key implication: For an entity to get an image, it must appear as a World Token in some chapter. Entities only in world.md (no token) get hover cards but no images.

Example (qino-claude): The Mender token in Chapter 1 triggers image generation using Chapter 1's snapshots; even though later chapters mention inland coordinates, the token image sticks to the harbor context unless a new token updates it.

Prompt Building Pipeline

The backend uses a multi-stage pipeline to build image prompts:

world-seed.md ──────────────────────────────────────────────────────────────┐
├── YAML frontmatter → SettingFoundation (13 structured fields)            │
└── prose content → visualLanguage (AI-distilled rendering style)          │
                                                                            ▼
world.md ───► worldBreath (current atmosphere)                    ┌─────────────────┐
                                                                  │ synthesize      │
chapter.md ──► heroDirection / tokenVisualization (AI-classified) │ SceneContext    │
                                                                  └────────┬────────┘
                                                                           │
                                                                           ▼
                                                              ┌─────────────────────┐
                                                              │ Prompt Builder      │
                                                              │ (deterministic)     │
                                                              └──────────┬──────────┘
                                                                         │
                                                                         ▼
                                                                   Image API

Setting Foundation (world-seed.md YAML frontmatter):

13 fields that capture the world's visual vocabulary:

FieldPurpose
genrePrimary classification (fantasy, science-fiction, contemporary, horror, hybrid)
subgenreSpecific tone (e.g., "gentle secondary world — patient wonder, age of sail")
scaleNarrative scope (e.g., "intimate and local — a single coastal town")
geographyTerrain, atmosphere, sky
eraVisual time period/aesthetic
architectureBuilt environment vocabulary
lightingDominant light quality
technologyTechnology visual vocabulary
transportVehicles/movement (critical for avoiding anachronisms)
floraPlants/vegetation
faunaAnimals/creatures
inhabitantsWho populates this world
magicHow magic LOOKS (or "none — hard science fiction")

Token Classification:

Before generating a token image, AI classifies the visualization type:

TypeApproach
character-portraitPerson rendered as portrait with face, body, human presence
location-establishingPlace rendered as establishing shot of the space
object-studyItem rendered as intimate object study
abstract-conceptIdea/force rendered as abstract visualization

Scene Context Synthesis:

To prevent context contamination (e.g., ships appearing in a ledger close-up), the backend synthesizes per-image context:

interface SceneContext {
  renderingStyle: string;    // Filtered for THIS subject (~50-80 chars)
  environmentHint: string;   // Relevant environment only (~30-50 chars)
  atmosphereHint: string;    // Grounding for THIS scene (~30-50 chars)
}

The AI filters full world context (visualLanguage, foundation, worldBreath) to extract only what's relevant for depicting the specific subject. Intimate close-ups exclude transport and geography; establishing shots include full environmental context.

Chapter Snapshots

Each chapter directory contains snapshots of world.md and arcs.md:

chapters/001-the-arrival/
├── chapter.md      # Chapter content
├── world.md        # Snapshot at write time
└── arcs.md         # Snapshot at write time

Why snapshots exist:

  • Image generation uses the world state from when the chapter was written
  • Ensures visual consistency (characters appear as they were described then)
  • Enables accurate retroactive regeneration

Snapshot timing: Created AFTER Phase 6 of scribe workflow (after world.md is updated with new characters/locations from this chapter).


R2 Storage Structure

chronicle-content/                    # CONTENT_BUCKET
├── repos.json                        # Index of all chronicles
├── qino-claude/
│   ├── manifest.json                 # Generated with chapter metadata
│   ├── world.md
│   ├── world-seed.md                 # (renamed from theme.md)
│   ├── arcs.md
│   └── chapters/
│       ├── 001-what-the-water-brings/  # qino-claude Chapter 1
│       │   ├── chapter.md            # Chapter content
│       │   ├── world.md              # Snapshot of world.md for this chapter
│       │   └── arcs.md               # Snapshot of arcs.md for this chapter
│       └── ...
└── concepts-repo/
    └── chapters/001-the-arrival/      # concepts-repo Chapter 1
        ├── chapter.md
        ├── world.md
        └── arcs.md

chronicle-images/                     # IMAGES_BUCKET
├── qino-claude/
│   └── 001-first-chapter/
│       ├── manifest.json             # Image manifest for chapter
│       ├── hero.webp
│       ├── the-wanderer.webp
│       └── ...
└── ...

qino-scribe Integration

The qino-scribe tool (in source repos) generates chronicle content using a three-agent architecture.

Agent Architecture

PREP AGENT (scribe-prep)
  │
  │  Reads: world-seed.md, world.md, arcs.md, git diff, recent chapters
  │  Produces: prep.md (through 3 interactive checkpoints)
  │
  ├── World Layer       → scene seeds        → user chooses
  ├── Disturbance Layer → world behavior     → user chooses
  └── Beat Layer        → story lens + beat  → user chooses
  │
  │  [hard cut — prep agent context ends]
  │
PROSE AGENT (scribe-prose)
  │
  │  Reads ONLY: prep.md + world-seed.md
  │  Produces: chapter.md (draft)
  │
  │  [hard cut — prose agent context ends]
  │
EDITORIAL AGENT (scribe-editorial)
  │
  │  Reads ONLY: draft chapter file + voice.md patterns
  │  Evaluates: execution quality, not story decisions
  │  Returns: APPROVED or NEEDS REVISION (N issues)
  │
  └── If issues: prose agent revises specific lines

This staged architecture forces fresh invention — the prose agent can't recycle descriptions since it only sees prep.md constraints. The editorial agent evaluates execution independently, without access to prep context.

Reference Files

FilePurpose
layers.mdStaged architecture flow and checkpoint formats
voice.mdUnified craft patterns (line-level and chapter-level)
story-lenses.mdTwelve story lenses with sensitivities and territories
disturbance.mdHow git diffs become world behavior
principles.mdRelational principles (mutual revealing, what remains unspoken)
foundation.mdSetting Foundation guide (13 fields for visual vocabulary)
calibration-notes.mdCraft devices discovered through prose exploration
craft.mdFormat specs, arcs.md structure

world-seed.md Configuration

The world-seed.md file now includes:

  • YAML frontmatter — Setting Foundation (13 fields: genre, subgenre, scale, geography, era, architecture, lighting, technology, transport, flora, fauna, inhabitants, magic)
  • disposition field — World hospitality (hospitable, guarded, indifferent, hostile)
  • Prose content — Atmosphere and generative grammar

Arc Workflow (in layers.md)

Arc updates happen automatically after chapter completion:

  • New arc: Add with *Chapters:* N-
  • Advanced: Update "In motion" section
  • Completed: Change to *Chapters:* N-M, add "Completed" section

Token Sync Workflow (World Update Layer)

For each World Token written in a chapter:

  1. Is this a character? → Add to world.md ## Characters if not already there
  2. Is this a location? → Add to world.md ## Locations if not already there
  3. Is this something else (object, concept, phenomenon)? → No sync needed

This ensures entities mentioned meaningfully in one chapter get hover cards in all future chapters, even without new tokens.

Commands

CommandPurpose
/qino-scribe:chapterWrite next chapter (or retroactive with from_ref/to_ref)
/qino-scribe:surveyAssess git history before writing chapters
/qino-scribe:rewindRestore world state to before last chapter
/qino-scribe:diagnoseAssess system integrity after changes

Environment Configuration

Backend (wrangler.jsonc)

{
  "name": "qino-chronicles-backend-local",
  "r2_buckets": [
    { "binding": "CONTENT_BUCKET", "bucket_name": "chronicle-content" },
    { "binding": "IMAGES_BUCKET", "bucket_name": "chronicle-images" }
  ],
  "kv_namespaces": [
    { "binding": "IMAGE_STATUS", "id": "..." }
  ],
  "queues": {
    "producers": [{ "binding": "IMAGE_QUEUE", "queue": "chronicle-images" }]
  },
  "vars": {
    "ALLOWED_REPOS": "qino-claude,concepts-repo"
  }
}

Frontend (wrangler.jsonc)

{
  "name": "qino-chronicles-local",
  "r2_buckets": [
    { "binding": "CONTENT_BUCKET", "bucket_name": "chronicle-content-local" }
  ],
  "vars": {
    "R2_IMAGES_URL": "https://<public-r2-bucket-url>",
    "BACKEND_URL": "http://localhost:4006",
    "ALLOWED_REPOS": "qino-claude,concepts-repo"
  }
}

Note: Images are served via public R2 URL (R2_IMAGES_URL) rather than a bucket binding, allowing direct browser access without worker proxy overhead.

Source Repo Secrets

GitHub repository secrets needed:

  • CHRONICLE_PUBLISH_KEY — API key for backend authentication

Debugging

Check R2 content

# From qino-chronicles-backend directory
pnpm exec wrangler r2 object get chronicle-content/qino-claude/arcs.md --remote
pnpm exec wrangler r2 object list chronicle-content --prefix=qino-claude/ --remote

Check image generation status

curl -s "https://qino-chronicles-backend.philhradecs.workers.dev/status/qino-claude/001-what-the-water-brings"

Manual R2 upload

pnpm exec wrangler r2 object put chronicle-content/qino-claude/arcs.md \
  --file=/path/to/arcs.md \
  --content-type="text/markdown" \
  --remote

Force image regeneration

Trigger GitHub action with force: true or POST directly:

curl -X POST "$BACKEND_URL/publish" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -d '{"repoName":"qino-claude",...,"force":true}'

Content Scopes & Context

  • Canonical (persists/accumulates): Root world.md and arcs.md hold the ever-growing, current state. Scribes append new entities/arcs here after each chapter. Stored in R2 at {repo}/world.md and {repo}/arcs.md; used by loaders for metadata and as the last-resort context for hover cards.
  • Chapter snapshot (frozen per-chapter): Each chapters/{slug}/ directory contains chapter.md, world.md, and arcs.md captured at publish time. These freeze the state that shaped the chapter so image prompts and retroactive regeneration stay historically correct even if the canonical world changes later. Stored in R2 at {repo}/{slug}/....
  • World Tokens (momentary presence): The ## World Tokens section in chapter.md expresses how entities feel in that chapter. When tokens introduce new characters/locations, qino-scribe syncs just the entity entry into canonical world.md; the vivid presence text stays scoped to the chapter. Tokens drive both hover-card specificity and token image prompts.
  • Context resolution for display: On read, presence is resolved in strict order: (1) tokens from the current chapter, (2) historical tokens from earlier chapters, (3) canonical world.md description. This lets the UI show chapter-specific tone while retaining fallbacks.
  • Image prompt context: The queue consumer builds prompts from chapter.md plus its snapshot world.md/arcs.md (and world-seed.md). Hashes govern regeneration, but the textual context always comes from the chapter's frozen scope, not today's canonical files. This ensures visuals align with the world as it was when written.

Scope Relationships (what persists vs. what freezes)

Canonical (grows)          Chapter snapshot (frozen per chapter)
world.md  arcs.md  world-seed.md ──┐
                              │ publish
                              ▼
                     chapters/{slug}/
                     ├─ chapter.md          (momentary)
                     ├─ world.md snapshot   (frozen)
                     └─ arcs.md snapshot    (frozen)

World Tokens live in chapter.md only (per-chapter presence).
New character/location tokens ⇒ add entity entry to canonical world.md (persists),
but the token text itself stays in the chapter scope (does not persist).

Display Context Resolution

1) Current chapter tokens     ┐ highest precedence for hover cards
2) Historical tokens          ┴ carry presence forward
3) Canonical world.md         → fallback description

Image Prompt Inputs

chapter.md  ─┐
world.md (snapshot) ├─► Prompt builder ─► AI image APIs ─► R2 images/{chapter}
arcs.md  (snapshot) ┘
world-seed.md (canonical)

Concrete Examples (concepts-repo)

  • Canonical world entry: world.md holds durable descriptions, e.g. Selin → "Cartographer, but not of distances... keeps an alcove of objects that don't fit on charts." (used when no chapter token is available).
  • Chapter token presence: Chapter 2 chapters/002-the-alcove/chapter.md defines **Selin** → "Eyes like deep water. Watches what appears in adjacency." This overrides canonical presence when reading Chapter 2.
  • Historical token reuse: Chapter 1 tokens include **The Tea-Bearer** ... moves between the groups with a tray... When reading Chapter 2, hover cards for The Tea-Bearer pull this historical presence if Chapter 2 has no fresh token.
  • Snapshot freeze: chapters/001-the-arrival/world.md captures the world at Chapter 1; even after canonical world.md adds "The Alcove" in Chapter 2, any regeneration for Chapter 1 still uses its frozen snapshot.
  • Prompt context: The token image for "The Ring" (introduced in Chapter 2) is built from Chapter 2's chapter.md + its snapshot world.md/arcs.md + canonical world-seed.md, ensuring the image reflects the moment the ring was placed between clusters.
  • qino-claude harbor scope: Chapter 1 tokens **The Mender** and **The Meeting Point** define a harbor baseline; Chapter 3 reuses that harbor presence for hover cards unless it provides new tokens for those entities.

File Reference

Source Repo Files

PathPurpose
chronicle/manifest.jsonChapter index and metadata
chronicle/world-seed.mdSetting Foundation (frontmatter) + prose atmosphere
chronicle/world.mdCharacters, locations, pressures
chronicle/arcs.mdAll arcs with chapter ranges
chronicle/chapters/*/chapter.mdChapter content
chronicle/chapters/*/world.mdChapter-specific snapshot of world.md
chronicle/chapters/*/arcs.mdChapter-specific snapshot of arcs.md
.github/workflows/chronicle-publish.ymlPublishing workflow

Backend Files

PathPurpose
src/routes/publish.tsContent ingestion, R2 storage, job queuing
src/routes/status.tsImage generation status
src/routes/manifests.tsImage manifest retrieval
src/queue/image-generator.tsQueue consumer, differential regeneration
src/services/token-classifier.tsAI classification of token types
src/services/prompt-builder.tsBuilds image generation prompts
src/services/image-generator.tsCalls Gemini/FLUX for image generation

Frontend Files

PathPurpose
src/server/get-chronicles.tsR2 fetching, Arc/Chronicle types, parseArcs()
src/lib/chronicles.tsparseWorld() for characters/locations with descriptions
src/routes/$repoName/$chapterSlug/index.tsxChapter display, arc filtering
src/components/highlighted-markdown.tsxEntity highlighting, buildEntityInfoMap()
src/components/entity-hover-card.tsxHover card UI with presence text
src/components/chapter-end-matter.tsxArcs and tokens display
src/components/chapter-images.tsxToken images in hover cards and end matter
src/components/journal-book.tsxArc details dialog

qino-scribe Reference Files

PathPurpose
tools/qino-scribe/references/qino-scribe/craft.mdFormat specs
tools/qino-scribe/references/qino-scribe/process.mdWriting workflow