Doctrine

ORA-2026-0116 — Stub-to-enriched world-model advancement

world-modeldata-enrichmentdormant-capabilityheartwoodstorieproimports

ORA-2026-0116 — Stub-to-enriched world-model advancement

Rule

Imported rows that exist in the world-model schema but do not yet make faithful project claims MUST advance on the enrichment ladder, or surface their dormancy. Importing a row creates a stub — a database object that occupies the namespace of a real-world thing without yet carrying source-backed evidence of it. Stubs that sit indefinitely are invisible permanent half-truths: they look like world-model rows to readers, queries, and downstream surfaces, but they cannot be sourced, cited, or rendered to a client without lying.

The ladder has four rungs. Every row in a world-model table MUST sit on exactly one rung, and every row that sits on a non-final rung MUST have a visible advancement path (a ticket, a backfill plan, or a tombstone candidate flag).

The ladder

stub  →  fact-backed  →  narrative-backed  →  surface-ready
                                            ↘  terminal-historical
RungDefinitionTest (mechanical)Example (Heartwood projects)
stubRow exists; primary identifier present (project name, person name, claim subject); no source-backed facts attachedSELECT * FROM projects WHERE address IS NULL OR job_type IS NULL OR contract_id IS NULL OR county IS NULL returns the row"Newton Farmhouse Addition" exists with name only; no address, no county, no contract, no job_type
fact-backedCore invariant facts (address, dates, parties, type, scope) present AND each carries source_kind + source_path proofAll required FK/NOT-NULL columns populated; provenance row exists in *_source_proof tableNewton has full address, county, contract id, job type, all sourced from BT contract scrape with stored URI
narrative-backedProject narrative atoms (design intent, notable feature, completion story, design context) exist as source-backed claims, NOT as marketing prose mixed with factsAt least N narrative claims in project_narrative_claims table with non-null source_kind + confidence ≥ 0.6Newton has a sourced design-intent claim (from architect's spec doc), a sourced notable-feature claim (from photo metadata), a sourced completion-story claim (from Zack's project notes)
surface-readyVisibility-eligible for public/portfolio surfaces. Has photo evidence with provenance, has consent/permission for any quoted client voice, has been reviewed within last 90 dayssurface_ready_at IS NOT NULL AND last_review_at > now() - interval '90 days' AND visibility_decision IN ('public', 'portfolio')Newton has imported StoriePro photos with hashes, has been reviewed by Zack, is approved for HCB public portfolio
terminal-historicalRow will not advance further. Source evidence does not exist (project too old, photos lost, narrative unrecoverable) and the row is preserved only for historical / financial / FK-integrity reasonsterminal_decision_at IS NOT NULL AND terminal_reason IS NOT NULL (e.g., terminal_reason = 'pre-2018 project, no surviving photos or narrative') — explicit operator decision required to enter this stateA 2015 small-job that exists in BT financial records but has zero photos, zero narrative source, no testimonial possibility. Preserved for cost-history; never appears on public surfaces.

Naming discipline: these are SHAPE names per ORA-2026-0044. Borrow from the discipline that already does this work — provenance/evidence (audit), enrichment (ETL), staging (build pipelines), narrative (history), preservation (archive). Avoid _complete, _done, *_final — they describe terminal state, not the shape of the row's claim.

Why terminal-historical is a peer rung not a child of stub: dormancy alarm rules treat stub as a row that should advance. Terminal-historical rows have an explicit will not advance decision attached, so they exit the dormancy alarm path. Without this rung, every un-enrichable historical row would either be deleted (losing financial / FK integrity) or trip dormancy alarms forever.

Materialization vs. computed rungs

The rung is computed, not materialized. Implementation options (in order of preference):

1. DB view or generated column that derives current_rung from underlying invariant tests at read time. Cannot drift from data because the data is the source. 2. Materialized column with a strict CHECK constraint that enforces the rung's invariants — e.g., CHECK (current_rung != 'surface-ready' OR (photo_count > 0 AND last_review_at > now() - interval '90 days')). Acceptable when read frequency justifies caching. 3. Materialized column without a CHECK constraint: forbidden. Drifts silently when underlying facts are deleted or downgraded; produces the exact "claims-without-evidence" failure mode this doctrine is designed to prevent.

The doctrine prefers option 1 unless query latency demands otherwise. Option 2 is a deliberate engineering trade-off, not a default.

When the doctrine fires

Any of the following triggers an enrichment-state obligation:

1. Bulk import lands rows into a world-model table (FLT-0082-style): the import PR MUST file the *_enrichment_state row alongside each imported row, marking initial rung as stub with a backfill ticket reference. No imports without enrichment-state rows. 2. A surface query joins to a world-model row that is below surface-ready: the query MUST either (a) filter out non-surface-ready rows, or (b) annotate them with a "stub — not surface-ready" marker. Silent rendering of stubs as if they were enriched is a violation. 3. A row sits at stub for >30 days with no advancement ticket: shepherd tick promotes a STALE_STUB ticket to the lane backlog as a candidate for tombstone or backfill prioritization. 4. A row jumps rungs without source proof: the advance MUST carry source_kind + source_path for each fact added. Advancing stub → fact-backed by typing in facts without source attribution is a violation — the row remains a stub-with-typing.

Why

FLT-0633 StoriePro enrichment scope, 2026-04-29. Heartwood DB had 40 projects, 22 of which were closed (completed) but missing core facts: no address, no county, no job type, no contract. photo_assets had 9 rows total. testimonials table — missing entirely. project_series table — missing entirely. project_narrative_claims columns on projects — zero. walkthrough_cards had 97 rows but all 97 were Woodbery (active project), not the 22 closed projects.

These 22 rows were stubs: they existed in the projects table, they had names, they were marked as closed — but they could not be cited as faithful world-model claims. A naive query joining projects to a marketing surface would render "Newton Farmhouse Addition" as if HCB had complete narrative for it. The row's existence implied a faithfulness it did not carry.

This is ORA-2026-0036's pattern (Dormant Capability) extended into the data row dimension. The library author's parallel — code that exists but has no consumer — is the import author's parallel: rows that exist but have no source-backed enrichment. Both look like signal until queried. Both compound silently.

Relationship to ORA-2026-0036 (parent pattern)

ORA-2026-0036 names "Dormant Capability" as the parent class — assets that exist in the codebase but propagate no signal about their existence or state. ORA-2026-0066 named one child (specs without prior art). FLT-0633 surfaces the second child:

Invisible Existing Work — three known children:
1. Dormant Capability (ORA-2026-0036): code exists, nobody knows to call it.
2. Specs Without Prior Art (ORA-2026-0066): spec is written, nobody knows the code already exists.
3. Stub-as-Enriched (this doctrine, ORA-2026-0116): row exists, nobody knows it has no source-backed claim.

All three are visibility failures. The countermeasure shape is the same: make dormancy visible. ORA-2026-0036 mandates the FOLLOW-ONS activation-ticket bullet for code. This doctrine mandates the *_enrichment_state row + ladder for data.

Relationship to ORA-2026-0062 (world-model fidelity)

ORA-2026-0062 is the tacit contract — does this make the world model more faithful to reality? A stub row sitting unmarked as a stub is a fidelity violation: the schema asserts presence of a project; the row carries no faithful claim about that project. The ladder is the operationalization of fidelity for imported rows. Coverage without fidelity is map-confabulation — importing 22 closed projects without enrichment is the data equivalent of confabulating coverage.

The 4th implicit Marquet pillar lens applies here: did this output serve world-model fidelity? — if not, was the gap named? Naming the gap = filing the enrichment-state row at stub with a backfill ticket. Not naming = silent confabulation.

How to apply

Schema obligation

  • Every world-model table that accepts imports MUST have a paired *_enrichment_state row per primary entity (one row per project, one per person, one per claim — not one per import batch). The state row carries: current_rung, last_advanced_at, advancement_ticket_id, missing_field_summary, last_reviewed_at.
  • Tables that hold derived narrative (testimonials, series, narrative claims) MUST carry per-row source_kind + source_path + confidence + correction_history columns.

Import obligation

  • Every import PR creates *_enrichment_state rows alongside imported rows. Initial rung = stub unless the import source carries source-backed proof (rare).
  • The PR's DONE post FOLLOW-ONS line names the backfill ticket per import batch, OR names a rung-advancement plan, OR names an explicit tombstone-after-N-days flag.

Surface obligation (the surface-read contract)

  • Default client-facing reads — any surface that can reach Zack, Aleah, Grace, or external clients (Heartwood site, Dollhouse, public portfolio, vendor-shared docs, marketing surfaces) — MUST filter WHERE current_rung = 'surface-ready'. No exceptions, no opt-out.
  • Internal/admin surfaces (operator console, fleet dashboards, debug tools, agent diagnostics) MAY render lower-rung rows but MUST visually distinguish them (e.g., greyed, prefix with [stub], badge with [fact-backed]). Silent rendering of stubs in any reader-facing surface is the violation this doctrine exists to prevent.
  • Code review gate: any new surface that joins to a world-model table is reviewed for surface-read contract compliance before merge. Reviewer asks "would this surface render a stub as if it were enriched?" — if yes, the join carries an explicit rung filter or annotation.

Advancement obligation

  • Advancing a row a rung requires source attribution. The advancement edit MUST carry source_kind + source_path for each fact added. Untraceable advances are reverted.
  • Skipping rungs is allowed only when the source proof covers all intermediate rungs simultaneously (rare — usually means the source is comprehensive, e.g., a single signed contract that establishes all core facts).

Dormancy alarm (decoupled from shepherd)

  • Filing happens at the import boundary, not at the shepherd boundary. When an import lands stubs, the import pipeline files the enrichment-backfill ticket(s) into the lane backlog as part of the same PR — one ticket per import batch, scoped to the rows it created. This decouples fleet operational infrastructure from business data schemas; the shepherd does not query heartwood-db or camber-db to discover dormant stubs.
  • Backlog aging is the alarm. Standard ORA-2026-0029 / Standing Directive §11 tombstone ladder applies to the enrichment-backfill ticket itself: 30 days dormant = STALE_SINCE, 60 days = _deprecated_<original>, 90 days = _archive/. The shepherd does its existing tick on backlog tickets; no schema-aware logic required.
  • Optional secondary signal (lane-internal, not fleet-wide): a per-lane periodic SQL probe (e.g., a Heartwood-internal cron that runs SELECT count(*) FROM project_enrichment_state WHERE current_rung = 'stub' AND last_advanced_at < now() - interval '30 days') MAY emit a lane-feed observation. This is lane infrastructure, not fleet shepherd infrastructure — different blast radius, different ownership.
  • Terminal-historical exemption: rows in terminal-historical are exempt from dormancy alarms regardless of age. They're not dormant; they're done.

When this is a DB/schema concern vs. backlog/proof concern

Concern typeSymptomOwner
DB/schemaTable cannot represent enrichment state at all (no *_enrichment_state table, no source-proof columns)CODEX-STRAT — schema migration
Backlog/proofSchema supports it, but rows aren't being advanced or stubs are accumulatingSTRAT shepherd — backlog promotion + tombstone ladder
Surface/renderQuery joins to a stub and renders it as enrichedCODEX-STRAT — query gate update
Doctrine/policyWhether to admit stubs at all, OR whether a particular enrichment requires Zack/operator review before advancementCLAUDE-STRAT — render decision (this doctrine)

The first move on encountering a stub-as-enriched bug is to identify which of the four concern types it belongs to. Only one of them is a doctrine question; the other three are owner-clear once classified.

Boot/bundle parity

This doctrine joins the additional-doctrines block in ~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, ~/.gemini/GEMINI.md per ORA-2026-0029 boot parity. Stamping handled by doctrine-parity-check --reconcile post-merge.

Operator note

Chad framing 2026-04-29 (FLT-0629 directive): "every byte HCB financial data from Gmail in heartwood-db by 6am ET; long-tail vision = predictive job costing/estimates/cost-to-completes/takeoffs/vendor RFIs." The long-tail vision is impossible without faithful rows — predictive cost-to-completes built on stub projects would be confabulation pretending to be foresight. The enrichment ladder is the discipline that makes the long-tail vision tractable.

See also

  • /Users/chadbarlow/gh/heartwood/docs/proofs/FLT-0633_storiepro_enrichment_scope_2026-04-29/README.md — the proximate case
  • ORA-2026-0036 — Dormant Capability (parent pattern)
  • ORA-2026-0062 — World Model is the Tacit Contract (anchor lens)
  • ORA-2026-0029 — Dying Well / Generative Ticket Transitions (tombstone ladder integration)
  • ORA-2026-0044 — Names Carry Contracts (ladder rung naming)
  • feedback_primary_source_required.md — adjacent doctrine: every receipt row MUST carry primary-source PDF (the same fidelity-over-coverage shape, applied to the receipts surface)