Ctrl K
ComunidadGuías…

CLAUDE.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# CLAUDE.md — songbird

songbird is a personal, self-hosted app for **annotating Scripture**: read a translation,
highlight a verse, attach a rich Markdown note anchored to it, find it later by tag or
meaning.

**songbird is built on top of Concord.** Concord runs on the network and exposes its REST
endpoints; **songbird is a separate app that consumes those endpoints** over HTTP to read
Scripture, search, and look up geography. Concord is the foundation and the data/API
provider; songbird is the client that sits on top of it. songbird depends on Concord;
Concord knows nothing about songbird.

The design canon is `docs/v1/SPEC.md`. Read it. This file is the always-on rules.

---

## The posture: built to last, built small

songbird is the app intended to grow (soap-journal was the learning exercise; Concord is the
foundation). "Built to last" means **clean bones, named boundaries, spec-first,
smallest-slice-first** — not big on day one. Ambition lives in the roadmap; discipline lives
in the slices. Every slice is a thin vertical cut through the whole app that ships on its own
— never a horizontal layer built in isolation.

## Hard invariants (do not violate)

1. **songbird consumes Concord over HTTP. It does not embed Concord, and it does not bundle
   Bible text.** songbird calls Concord's REST endpoints (`/v1/verses`, `/v1/chapters/...`,
   `/v1/translations`, `/v1/search`, later `/v1/semantic-search`, `/v1/places...`,
   `/v1/cross-references/...`) for all Scripture, search, and geography. songbird never
   stores or ships its own copy of the Bible text — it comes from Concord at request time.

2. **Concord's location is configuration, never an assumption.** songbird reads a single
   `CONCORD_BASE_URL` and calls whatever is there. Concord may be on the **same host** or
   **any other machine on the LAN**, on any port — the location is pure config. A localhost
   default is fine (zero-config convenience), but **never hardcode "same server" / localhost
   as a requirement**. The requirement is that Concord is *reachable* at that URL.

3. **When Concord is unreachable, error — no fallback.** If a Concord call fails (server
   down, network gone, wrong URL), songbird surfaces a clear error. It does **not** attempt
   offline operation, a bundled copy of the text, or graceful degradation. Concord is a hard
   runtime HTTP dependency; its absence is an error state, not a mode to design around.

4. **The canonical-coordinate bridge.** Annotation anchors are **always** canonical — USFM
   book code + chapter + verse — **never** a translation-specific id. An annotation is pinned
   to an *address* (e.g. `JHN 3:16`), not to a verse in some translation's rendering. This is
   clean over HTTP because Concord's endpoints already return canonical coordinates: songbird
   reads a chapter from Concord, overlays its annotations by matching on those same
   coordinates, and a note shows correctly in every translation it's scoped to — pinned to
   the verse *address*, not the *text*. This is the invariant that keeps songbird from
   inheriting soap-journal's "a note made in NKJV won't match a BSB lookup" limitation. It is
   tested. Protect it like a load-bearing wall.

5. **songbird owns only its annotation data.** songbird's **own** SQLite database (under a
   configurable `DATA_DIR`) holds annotations, tags, and users — songbird's SQLAlchemy
   models, Alembic-migrated. No Bible text lives in it. (Caching Concord responses for
   performance is a possible later optimization, not a change to this ownership.)

6. **Notes are stored as Markdown** — durable, portable, editor-agnostic. Never store
   editor-native (TipTap/ProseMirror) JSON as the canonical form. If the editor is swapped,
   the notes must survive as readable text.

7. **Never break a shipped slice.** Each merged slice leaves `main` working and usable.
   Additive growth; no regressions.

## Git workflow

- **Branch per slice**, off `main`: `slice/N-short-name`.
- **Scoped conventional commits**, small and atomic: `feat(api)`, `feat(web)`,
  `feat(reader)`, `feat(annotations)`, `fix(...)`, `test(...)`, `docs(...)`, `chore(...)`.
- **PR per slice** via `gh`. The PR body states what landed, the open-question answers, and
  how it was verified.
- **Claude Code merges its own pull requests:** assign Kris (`kbennett2000`) to the PR, confirm
  Kris shows as assignee, then merge once its checks are green, with a merge commit. If assigning
  or merging fails, stop and tell Kris. **Never push to `main`. Never `--force`.**
- Plan Mode for every slice: produce a plan, Kris approves, then implement.

## Engineering principles

- **Tests are required**, and the canonical-coordinate bridge (invariant 4) gets explicit
  tests. Mark slow/integration tests (including any that need a live Concord) so the fast
  suite stays fast.
- **Type-checked and linted**: backend Pyright-strict-clean and Ruff-clean; frontend
  TypeScript-strict and lint-clean. Green before a PR.
- **Migrations**: schema changes go through Alembic. No ad-hoc drift.
- **Single deployable unit**: a multi-stage Dockerfile builds the Vite SPA, then one uvicorn
  process serves the static SPA + songbird's API.
- **Talking to Concord**: use one configured HTTP client against `CONCORD_BASE_URL`; handle
  failures explicitly (invariant 3); don't scatter base URLs or assume co-location.
- **Dependency discipline**: songbird is deliberately lean — new dependencies need a reason.
  Note the payoff of the HTTP model: the heavy ML (the embedding model + ONNX runtime) stays
  in **Concord**; songbird reaches semantic search with an HTTP call and stays light. No ML
  stack in songbird.
- **ADRs** in `docs/adr/` for genuinely new architectural decisions (not routine work).
- **Spec is canon — until reality corrects it.** If implementation reveals the spec is wrong
  (it happened repeatedly building Concord), fix the spec in the same PR with a `docs:`
  commit and note why. Reality wins; the spec gets updated, not worked around.

## Documentation
### The one rule everything serves

**Write for one real reader — and run every line past them.** Picture a specific person: curious,
non-technical, has never done this before, one frustration from closing the tab. Before anything
ships, hold each sentence and each step up to them and ask three things — *would they know what this
means? would they know how to do it? would they see why they're doing it?* Any "no" → cut it, explain
it, or move it. Every rule below is an instance of this one.

### The voice rules (each paired with the mistake it prevents — the mistake is what makes it stick)

- **Break the wall — a scannable page is itself reassurance.** Short paragraphs, frequent plain
  subheads, numbered steps for anything sequential, a code block for anything they'd type, one bold
  phrase per section. *Prevents:* a dense wall that signals "this is hard" before they've read a word.
- **No unexplained jargon — especially tooling.** Gloss and link every tool or term on first mention
  (the editor, the terminal, the package manager, the language). *Prevents:* the reader stranded on a
  word — and the subtler error of naming a scary unknown to "reassure" ("don't worry, no [X]"), which
  only teaches them [X] is something to fear.
- **Just-in-time, not just-in-case.** Explain a thing in the step that needs it, never earlier. Put
  the roadmap at the *end* as encouragement, not a syllabus at the front. *Prevents:* front-loaded
  caveats — and, worst, answering an unasked question, because a reassurance *plants* the fear it
  meant to soothe.
- **Motivation is timed, not cut.** The "you did it / this is real / you're becoming a builder" beats
  land hardest right after a win, at a section's close — never as opening preamble. *Prevents:*
  identity and proof beats falling flat because they arrived before the reader earned the feeling.
- **Show the win before the explanation.** Get the reader to a working, visible result as fast as
  possible, *then* explain how it works for the reader who's now curious. *Prevents:* the teach-then-do
  wall — making someone read a full explanation before anything happens.
- **Set up once; don't re-gate.** Establish setup a single time; later steps assume it's done and link
  back rather than re-explaining. Setup reappears only as symptom-tied troubleshooting. *Prevents:*
  re-charging a toll every section, which reads as friction and mild condescension.

### Two traps to name explicitly (we hit both, repeatedly)

- **Break-to-test.** Never ask the reader to do something whose only purpose is to exercise the work —
  break something, stop a service, force an error. That's the builder's job, already done for them.
  The reader only does what a real user would naturally do, and meets error states through reassurance
  ("if you ever see this, here's what it means"), never by being told to cause them. *(This one slipped
  past every specific rule above — it's the reason the root rule has to exist.)*
- **Completeness as a reflex.** "Everything true" is not "everything useful." If a sentence isn't
  helping the reader do the thing in front of them right now, it's costing them attention and
  confidence. Cut it.

### Before you call a page done

**Read it back as the reader, not the author.** Walk it top to bottom as that first-timer following it
literally — not as the writer confirming it works. Every noun explained or obvious; every step has a
how and a why; nothing asks them to break, inventory, or pre-learn. If you wouldn't follow it
comfortably with zero background, fix it before you ship.

## Out of scope (no build without an explicit scope decision)

- A native mobile app. (The web UI **is** kept responsive — phone/tablet layouts and mobile
  fixes are in scope; a packaged iOS/Android app is not.)
- Offline operation / Bible-text fallback when Concord is unreachable (invariant 3 — its
  absence is an error).
- Sub-verse / word-level highlighting (whole-verse is the deliberate v1 granularity).
- Cloud / multi-tenant hosting (self-hosted single-unit is the model).

## Don't

- Don't embed Concord or bundle Bible text (invariant 1) — consume the HTTP API.
- Don't hardcode localhost / same-server as a requirement (invariant 2) — it's config.
- Don't invent offline fallback when Concord is down (invariant 3) — error.
- Don't anchor annotations to anything translation-specific (invariant 4).
- Don't store Bible text in songbird's database (invariant 5).
- Don't store notes as editor-native JSON (invariant 6).
- Don't put an ML stack in songbird — that's Concord's job.
- Don't merge a PR before Kris is its assignee and its checks are green. Don't push to `main` or
  `--force`.
- Don't build a horizontal layer in isolation — slice vertically.