Domain Model: pal-e-docs

arch-domain-pal-e-docs Architecture

architecture active

Domain Model: pal-e-docs

The knowledge platform's entity model. Notes hold metadata and legacy rendered HTML; blocks are the real content graph (heading/paragraph/list/table/code/mermaid). Tags, projects, boards, and board items provide cross-cutting classification and workflow. This is the what of pal-e-docs — the when (data flow) and where (deployment) live in sibling arch notes.

Diagram

Components

Component Purpose Notes
<strong>notes</strong> Page-level metadata + legacy rendered HTML SQLAlchemy model <code>Note</code>. <code>html_content</code> kept for back-compat; authoritative content lives in <code>blocks</code>. Indexes: PK, unique on <code>slug</code>. No index on <code>note_type</code> (opportunity for partial index — see ticket pal-e-api#252).
<strong>blocks</strong> Atomic structural content units (heading, paragraph, list, table, code, mermaid) SQLAlchemy model <code>Block</code>. Embedding column is <code>halfvec(2560)</code> with HNSW index for semantic search. Mermaid blocks are intentionally skipped by the embedding worker (diagram source is not semantically meaningful). Unique <code>(note_id, anchor_id)</code> enables surgical <code>get_section</code> reads.
<strong>projects</strong> Top-level grouping for notes, repos, and boards SQLAlchemy model <code>Project</code>. Every project optionally has a <code>page_note_id</code> pointing at its project-page note. One board per project.
<strong>repos</strong> Forgejo/GitHub repository records associated with a project SQLAlchemy model <code>Repo</code>. Platform + URL + role metadata.
<strong>tags</strong> Cross-cutting classification of notes (active, sop, template, convention, etc.) M:N via <code>note_tags</code>. Case-sensitive unique name.
<strong>note_links</strong> Directed graph of note-to-note references Source → Target edges. Used by link maintenance and orphan detection.
<strong>note_revisions</strong> Full-content history of every note edit Snapshots <code>html_content</code> per edit. Revision number monotonic per note. Enables rollback and audit.
<strong>compiled_pages</strong> Rendered HTML cache with TOC JSON One row per note. Invalidated on block changes via <code>content_hash</code>. Serves the read-heavy public browsing path.
<strong>boards</strong> Kanban board per project One-to-one with project. Slug-addressable.
<strong>board_items</strong> Kanban tickets (issue-backed or note-backed) Dual-addressing: <code>forgejo_issue_url</code> for issue items, <code>board_note_id</code>/<code>note_slug</code> for note-backed items (plan/phase/repo/project). Labels carry story:X, arch:Y, type:Z traceability triangle.
<strong>users</strong> Authenticated superusers of the API Email + hashed password + is_approved. Keycloak JWT is the actual runtime auth path; this table is the local user directory.

Key Decisions

  • Blocks are the source of truth, not html_content. The legacy notes.html_content column is preserved for back-compat and for the compiled-page cache, but block-first access (get_note_tocget_section) is the cheap path that agents use. Rationale: token efficiency — one section is ~500 tokens, the full note is ~5000.
  • Embeddings live on blocks, not notes. Atomic semantic units give better retrieval (a single procedure paragraph inside a 10KB SOP is findable). Mermaid blocks are explicitly skipped — diagram syntax is not embedding-friendly, and the blocks_embedding_trigger queue filters them out.
  • board_items dual-addresses notes and issues. A board item is either note-backed (plan, phase, repo, project, todo) or issue-backed (forgejo_issue_url). This preserves the kanban-over-plans migration: old phase notes still work, new tickets flow through Forgejo. The item_type enum enforces which address field is required.
  • note_type is a varchar, not an enum. Extensibility: new note types (board, project-page, architecture, skill, agent) were added without migrations. The trade-off is no DB-level validation — hooks enforce correctness at write time (see check-note-template.sh).
  • Tags are M:N, labels are comma-separated strings. Tags are note-level classification (normalized, queryable). Labels are board-item-level traceability (denormalized for display speed). They serve different query patterns and should not be unified.
  • No index on block_type or note_type. Current scale (23K blocks, 900 notes) makes seq-scans <5ms so it hasn't mattered. Partial indexes on first-class query paths (block_type='mermaid', note_type='architecture') are scoped in ticket pal-e-api#252 — motivation is semantic self-documentation of query paths, not performance.
  • project-pal-e-docs — the project this diagram describes
  • template-architecture — the triplet model prescribing this note
  • arch-dataflow-pal-e-docsTODO, the when sibling (sequenceDiagram of MCP → API → DB round-trips)
  • arch-deployment-pal-e-docsTODO, the where sibling (graph TB of k3s pods, CNPG, ingress)
  • convention-architecture-ids — how arch: labels on board items map to Components table rows