System Rules · v1.02

The Dossyer Engineering Rules

Rules for building, changing, and extending the codebase. Every change strengthens the foundation. No bandaids.

1. The Core Principle

Every change is a system change. When you fix something, you're not fixing one instance — you're creating or improving the component that handles that class of problem. If the fix doesn't work for the next case too, it's not done.

This means:

2. Rules for Every Change

Rule 0: Systems, Not Bandaids

Every fix, addition, or change should be treated as building a foundation for a scalable system — never as a one-off patch. Before implementing anything, ask: Does a system already exist for this? Should this be a repeatable component? Will this approach work when there are 10 of these instead of 1? If the answer reveals that you're about to write a one-off, stop and design the system first.

This is the meta-rule. It governs how you approach every other rule. The difference between a bandaid and a system:

The test: if someone asks "why is it done this way?" the answer should be "because the system works this way" — not "because we needed to fix that one thing."

Rule 1: Token First

Never use a raw value. Colors, spacing, font sizes, shadows, radii, transitions — all come from CSS variables. If the value you need doesn't exist as a token, create the token first, then use it.

This applies to: hex colors, pixel values, font-family strings, timing functions, and any value that could conceivably change globally.

Rule 2: Component, Not Instance

If a pattern appears in more than one place, it's a component. Build the component, then use it everywhere. When the design changes, you change the component once.

Examples: modal overlays, dark header bars, chat input rows, citation footnotes, label typography, card hover states.

Rule 3: One Function, Multiple Contexts

If JS logic follows the same algorithm with different inputs, it's one function with parameters. Don't copy-paste and change the IDs.

The chat send flow (get input → inject user bubble → show typing → call API → render response → show suggest chip) is one algorithm. It should be one function that takes a config object: {inputId, msgsId, btnId, history, systemPrompt, chipVariant}.

Rule 4: Extend, Don't Override

Use base classes + modifier classes, not separate parallel definitions. A snap modal header isn't a different thing from a story modal header — it's the same .bar-header component with a .bar-header--sticky modifier.

Rule 5: Scale, Don't Guess

When you need a value between two tokens, build the scale first. Don't pick a hex value by eye — create the intermediate steps as tokens, then choose from the scale. (See: --white-1 through --white-5.)

Rule 6: Version Everything

Every code change gets a new version number. Never overwrite a previous version. The title tag, footer, and filename must all match.

Naming Convention

Files are named: the_dossyer_vMM.NN.html — two-digit major version, two-digit minor version, with dots preserved.

SegmentMeaning
MM (major)00 = prototype phase. Increments to 01 when we move to production-grade code, separate files, or a build system.
NN (minor)Increments by 1 for every change that touches code. Bug fixes, UI tweaks, feature additions — all get a bump.

What Counts as a Version Bump

Where the Version Appears

Version History Convention

The blueprint document (the_dossyer_blueprint_v2.html) maintains a running version history. Each entry follows this format:

v00.XX — Brief description of what changed. What was added or fixed, not how. The code itself is the how.

Keep entries concise — what changed and why, not how. The code itself is the how.

Support Documents

Each support document is versioned independently with a vN suffix in its filename. The blueprint maintains a running version history for the app. Support docs are updated when their scope changes — not on every app patch.

DocumentFile Pattern
Product Blueprintthe_dossyer_blueprint_vN.html
Voice & Tone Biblethe_dossyer_voice_bible_vN.html
System Prompt Architecturethe_dossyer_system_prompt_vN.html
System Rulesthe_dossyer_system_rules_vN.html
Audit Promptthe_dossyer_audit_prompt.html

Rule 7: Audit Before Building

Before adding a new component, check if an existing one can be extended. The default action is reuse. The exception is "this genuinely needs different behavior, not just different content."

3. Component Registry

These are the shared components in the system. Any new feature should use these before creating new ones.

CSS Components

Token Scale: Colors
--white--white-1 through --white-5--paper--paper-2--paper-3 for surface colors. --ink--ink-mid--ink-light--ink-faint for text. Brand color scales: --color-red-50 through --color-red-900, --color-navy-50 through --color-navy-900. Supporting: --color-gold, --color-green, --color-teal, --color-purple. Three-tier architecture: primitive → semantic → component.
Used everywhere. No raw hex outside :root.
Token Scale: Spacing
--space-1 (4px) through --space-16 (64px). 4px base grid. Legacy aliases --sp-N still work but canonical names are --space-N.
Used everywhere. No raw pixel values for spacing.
Token Scale: Shadows
--shadow-xs--shadow-sm--shadow--shadow-lg--shadow-xl. Layered composites for depth.
Cards, modals, dropdowns.
Token Scale: Radius
--radius-sm (4px), --radius (6px), --radius-lg (10px), --radius-xl (14px), --radius-full (50%). Semantic: --radius-badge, --radius-card, --radius-input, --radius-modal.
Every bordered element.
Dark Header Bar
.bar-dark base class (ink bg, flex, space-between, mono label) + .bar-dark-label + .bar-dark-note. One component for all dark headers.
Consolidated ✓ — sidebar blocks, story modal, snap modal all use .bar-dark.
Modal Overlay
.overlay base (fixed, inset, backdrop blur, opacity transition) + .overlay-box (white bg, radius, shadow, scale transition). Variants: .overlay-box--wide (760px), .overlay-box--narrow (600px). Modifier: .overlay--center for vertically centered modals.
Consolidated ✓ — story modal (wide) and snap modal (narrow, centered) both use shared base.
Citation System
Citation Registry pattern. createCitationRegistry(footnotes, sourceLinks)registry.resolve(sourceText) for globally sequential numbering. renderCitationItem({num, text, link}) — single render function for all citations. buildFootnotesHTML() builds collapsible section. appendCitationsToDOM() adds chat citations to shared section. Citations are numbered; additional coverage sources are not.
Fully unified ✓ — story prose, snap prose, and chat responses all use the registry.
Chat System
sendChatMessage(cfg) — unified engine. Config: {msgsId, btnId, inputId, q, history, systemPrompt, chipClass, chipAction, citeRegistry, fnSectionId}. Thin wrappers: sendSnapMsg(), sendStory(), sendAskMessage(). CSS: .td-chat BEM component with __label, __hints, __chip, __msgs, __input-row, __input. No avatars — user bubble right-aligned, response prose left-aligned, matching story typography.
Fully consolidated ✓ — one engine, one CSS component, three placements.
Story Update Highlights
.story-update + type modifiers (.update-breaking, .update-revised). Data: updates:[{type, time, content}].
Iran story, Fed story. Any story can use it. ✓
Unread State
.unread-dot + .is-read on card + .has-update + .updated-badge. Managed by readStories Set and markAsRead().
All story cards. ✓
Icon System
Inline SVG registry: ICONS object mapping names to SVG paths + icon(name, class) render function. No CDN dependency. 31 UI icons + 8 brand logos (official colors, exempt from token system). Sizing: .icon-xs (11px), .icon-sm (16px), .icon (20px), .icon-lg (24px). Rule: icons ~1.2× companion text size.
All icons throughout the app. ✓
Button System
.btn base reset + types: .btn-primary (filled), .btn-ghost/.btn-ghost-dark (transparent), .btn-chip/.btn-chip-dark (pill). Icon sizing: .btn-icon (34px), .btn-icon-sm (28px), .btn-icon-lg (40px). Modifier: .btn-danger (red variant).
All buttons: send, close, refresh, nav tabs. ✓

Utility Classes Needed

These utility classes are implemented and applied:

ClassWhat it doesReplaces
.label-monofont-mono, size-nano, letter-spacing 3px, uppercase, ink-faint11+ inline declarations of the same pattern
.label-mono-smSame but size 8px, spacing 1.5pxTab labels, toggle labels
.text-prosefont-prose, size-body, lh-prose, ink-mid, measure-prose auto-centeredRepeated prose container declarations

4. Refactoring Priority

Ordered by impact — what to consolidate first:

  1. Chat send function ✓ — consolidated into sendChatMessage(config) with thin wrappers.
  2. Modal system ✓ — merged into .overlay + .overlay-box with --wide/--narrow variants.
  3. Dark header bar ✓ — one .bar-dark base class.
  4. Utility classes ✓ — .label-mono applied via grouped selector to 20 selectors.
  5. Close functions — still two parallel patterns. Low priority since they work.

5. Data Structure Rules

Story Object Schema

Every story follows this shape. All fields are required unless marked optional.

FieldTypeNotes
imgstringUnsplash URL with w/h/fit params
imgCaptionstringPhoto credit line
badgestringCSS class: b-breaking, b-markets, b-tech, etc.
badgeTextstringDisplay text for badge row
headlinestringGeorgia headline — tonal register must match content
bylinestringFormat: "[N] sources · [status] · Last updated [time]"
proseHTML stringMain body. Citations as [Source, Date].
quote{text, attribution}Optional. Empty text = no quote rendered.
prose2HTML stringOptional. Continuation after quote.
knownstring[]Confirmed facts list
unknownstring[]Not yet known list
cov{l, c, r}Coverage distribution percentages. Must sum to 100.
sourcesstring[]All source names
sourceLinksobjectOptional. Maps citation text → URL
updatesarrayOptional. [{type, time, content}]. Types: breaking, new, revised

Snap Object Schema

FieldTypeNotes
platformstringDisplay name for source
srcstringSource attribution line
velHTML stringVelocity badge HTML
titlestringHeadline
textHTML stringBody. Citations as [Source, Date] or sup.cite wrapped.
askQstringPre-seeded chat placeholder question
sourceLinksobjectOptional. Maps citation text → URL

6. Naming Conventions

ThingConventionExample
CSS variablesLowercase, hyphenated--ink-mid, --shadow-lg
CSS classesLowercase, hyphenated. BEM-lite for modifiers..story-card, .update-breaking
JS functionscamelCasesendChatMessage, markAsRead
JS constantsUPPER_SNAKESTORIES, SNAPS, AI_CONTEXT
HTML IDsLowercase, hyphenatedask-chat-input, card-0
Filesthe_dossyer_v00.XX.htmlTwo-digit minor, dots preserved

7. Accessibility Rules

Accessibility is a system, not a checklist. Build reusable utilities: a11yInteractive(selector) for keyboard support, a11yModalOpen/Close for focus management, announceToSR() for screen reader announcements. One function call covers a new element.

8. Spacing & Typography Rules

No text below 10px. The font size floor is --size-micro: 10px. Every size token from micro through body must be ≥ 10px. If something needs to be smaller, it probably doesn't need to be text.

9. Notification System Rules

Two components doing one job is a system problem. If two UI elements convey the same concept ("what's new"), they need to be one system with states, not two independent components. Rule 0 applies to UX patterns, not just code.

10. Checklist: Before Every Change

  1. Does a token exist for this value? If not, create it.
  2. Does a component already handle this pattern? If yes, extend it.
  3. Am I duplicating logic that exists elsewhere? If yes, parameterize.
  4. Does this fix work for the next case, not just this one?
  5. Is the version number updated in title and footer?
  6. Are CSS braces balanced? Are JS braces balanced?
  7. Are hidden elements removed from the tab order?
  8. Is all text ≥ 10px?