Date: 2026-07-10
Scope: Lake Tahoe Accommodations listing descriptions, photo captions, Conduit guest messaging, Roam Free HostBuddy-era profiles, onboarding, and the shared property-data substrate.
Method: read-only repository audit. No production code, schema, credentials, or client data changed.
Executive verdict#
The proposed immutable raw history → vendor-neutral canonical operating model → derived intelligence and accelerators architecture is directionally correct, and this repository has already proved much of it in working code.
The important conclusion is not “reuse the current system.” It is:
Preserve the proven mechanics, replace the tenant-specific data contract.
The strongest existing assets are the immutable LTA Bronze archive, generic fact reconciliation and publish gates, deterministic claim dossiers, human approval ledgers, fail-closed write guards, readback verification, and content provenance. Rewriting those would waste working code and reintroduce solved failure modes.
The current data shape is not the wide base for the new company, however:
- LTA is a logical tenant inside one shared Supabase project, while the newly locked premium architecture requires a separate client data plane.
- Raw storage is a Tahoe-specific table with a hard-coded source enum and a property-code join, not a generic connector/object history.
- “Canonical” property information is split across
propertiescolumns,atomic_property_facts,properties.amenities,properties.metadata, child-entity facts, and legacy questionnaire/Notion projections. - Source authority is mostly encoded in Python precedence tuples and workflow documents, not in a client-visible, per-field authority registry with effective dates and conflict rules.
- Property identity is partly vendor-neutral (
properties.id) but many LTA workflows still key state and joins onunit_code,marketing_code, Streamlinehome_id, or Conduit entity IDs. - Derived content is governed well, but its version history is inconsistent: caption drafts and write receipts are durable; full LTA listing-copy versions still depend heavily on local JSON paths and Notion staging.
- The HostBuddy-era manual profile builder guesses missing operational facts and places
AI-DRAFTEDmaterial in a runtime knowledge page. That behavior conflicts with a first-principles safety model and should not carry forward. - The Roam Free Conduit renderer deliberately emits secrets such as Wi-Fi passwords in full to Conduit. That conflicts with the new promise that sensitive data can remain local and never reach frontier or external AI systems.
Overall shape match:
- Raw history: strong prototype, not generic
- Canonical model: partial and fragmented
- Derived intelligence/accelerators: strong, but content-first and tenant-specific
- Vendor portability: partial
- Separate premium client data planes: not matched
- Safe client/agent extensibility: not yet a platform contract
Current-state component and data-flow map#
flowchart TD
S[Streamline admin and public listing] --> B[tahoe_source_snapshots Bronze]
BW[Breezeway exports and guidebook] --> B
N[Notion property and working files] --> B
P[Gallery URLs and PM captions] --> B
O[Owner attestations] --> B
B --> F[parse, normalize, reconcile, certify]
F --> C[atomic_property_facts]
F --> K[knowledge_conflicts]
F --> E[atomic child entities and facts]
C --> G[listing gather brief]
E --> G
S --> G
P --> G
G --> D[property listing dossier]
D --> A[listing authoring and adversarial review]
A --> LR[lta_listing_runs and local copy JSON]
LR --> W[gated Streamline writers]
W --> V[readback verification and receipts]
W --> CS[Conduit PMS resync]
CS --> CV[Conduit sync verification receipt]
P --> CB[gallery baselines and frame observations]
D --> CD[per-photo caption dossier]
CB --> CD
CD --> CR[lta_caption_drafts and review ledger]
CR --> GW[gated gallery writer]
GW --> GR[per-photo push receipts and readback]
OR[OwnerRez and onboarding inputs] --> R[CanonicalRecord read model]
C --> R
R --> GE[GuestExport and TeamExport]
GE --> HN[HostBuddy-era Notion guest profile]
GE --> CO[Conduit overlay renderer]
LTA listing descriptions#
- Source captures land in
tahoe_source_snapshots, an append-only table protected against update, delete, and truncate. Evidence:supabase/migrations/20260620120000_tahoe_trustworthy_pipeline.sql. - Generic fact code parses source claims into
{property_code, key, value, source, quote, snapshot_id, value_type}, detects cross-source conflicts, chooses a winning source, and writes governed facts plus provenance. Evidence:lib/facts/model.py,lib/facts/reconcile.py,lib/facts/persist.py,lib/facts/publish.py. - Tahoe-specific policy binds the generic engine to a workspace, source list, join key, source precedence, aliases, and publish criteria. Evidence:
lib/facts/profiles/tahoe.py,lib/tahoe_facts/*.py. scripts/tahoe-listing-gather.pybuilds a writer brief from confirmed canonical facts, governed child facts, Streamline Bronze, the public scrape, PM captions, frame observations, proximity, and review signals. It hard-redacts credentials and team data.lib/property_listing_dossier.pyconverts that heterogeneous brief into a deterministic allowlist:allowed_claims,do_not_claim, review flags, missing inputs, readiness, source hash, and parameter hash. An adversary verdict can fail the dossier closed.- Human/AI authoring follows the LTA template and rubric in
skills/tahoe-listing-writeback/references/. The assembler refuses claims that cannot trace to the dossier and emits exact Streamline Desc and Airbnb payloads. lta_listing_runsrecords pipeline state and artifact paths. The Streamline writers are dry-run by default, require a reviewed approval manifest, take backups, write one property, and verify readback. Evidence:lib/lta_listing_runs.py,scripts/streamline-listing-writer.py,scripts/tahoe-listing-assemble.py.- Conduit receives the live description indirectly from its Streamline PMS connection. Because there is no API to trigger that refresh, a human triggers it and
scripts/tahoe-conduit-sync-verify.pyproves the expected description reached the LTA workspace before the run is considered complete.
LTA photo captions#
- Streamline gallery baselines pin
image_id, position, filename, PM title, current caption, and image URL. Photo bytes can be archived by immutable byte hash in Supabase Storage. Evidence:docs/tahoe-pipeline/CAPTION-PUSH-PHASE2-PLAN.md,supabase/migrations/20260706210000_gallery_photos_archive.sql. - A separate observer records per-frame space, co-visible spaces, seating, and visible features. The property dossier and frame facts are projected into a per-photo allowlist by
lib/lta_caption_dossier.py. - The authoring output is structurally narrow:
{kind, code, home_id, generation_baseline_id, writer_model, captions:[{image_id,draft_caption}]}. Titles cannot be drafted because the schema has no title field. Evidence:lib/lta_caption_author.py. lta_caption_draftsis the durable draft and approval ledger. Draft ingest cannot set approval fields; human approval cannot overwrite draft provenance.writer_modelis stored per caption.lta_caption_reviewssnapshots what the reviewer saw and their decisions.lta_caption_push_receiptsrecords per-photo before, after, approver, write time, verification, and approval token. Evidence: migrations20260705150000,20260707120000, and20260707140000.- Live writes are canary-first, per-property approved, fresh-baseline checked, one-shot, and readback verified.
Guest messaging and property profiles#
There are two generations of guest-knowledge handling.
HostBuddy-era manual model
skills/hostbuddy-profile/SKILL.core.mdbuilds a 28-section Notion page from listing/onboarding notes plus reusable blocks stored inhostbuddy_canonical_sections,hostbuddy_standard_blocks, andhostbuddy_appliances.- HostBuddy reads the Notion page body verbatim.
- Missing facts are guessed and marked
AI-DRAFTED; verified standard blocks are pasted verbatim. lib/onboarding_intake/views/guest_profile_md.pyandguest_profile_blocks.pylater made the rendering deterministic and reusable.lib/guest_profile_publisher.pypublishes the rendered page behind an explicit force flag.
Conduit-era model
- LTA currently relies on Conduit's Streamline PMS sync for listing descriptions and structured PMS knowledge. The repository verifies sync but does not directly overwrite those PMS-owned nodes.
- Roam Free has a separate custom-overlay path:
CanonicalRecord -> GuestExport -> conduit_renderer -> conduit-kb-push.py. conduit_kb_nodesmirrors Conduit nodes;conduit_pushed_nodesis an idempotency ledger for custom nodes the system owns.- The client is correctly workspace-token scoped and refuses to patch PMS-owned nodes. Evidence:
lib/conduit_client.py,lib/conduit_renderer.py,scripts/conduit-kb-pull.py,scripts/conduit-kb-push.py.
Concrete schema inventory#
Raw/source layer#
| Shape | Current use | Key fields | Audit verdict |
|---|---|---|---|
tahoe_source_snapshots |
Immutable LTA source payloads | workspace_id, property_code, source, source_url, fetched_at, content_hash, payload, metadata |
Strong immutability and replay; Tahoe-specific, source CHECK requires migrations, uniqueness omits workspace |
tahoe_reviews_snapshots |
Reva guest-review Bronze | source payload plus metadata | Correctly separated from property-fact rebuild; source-specific table proliferation |
tahoe_gallery_baselines / photos |
Gallery metadata snapshots | baseline ID/hash, image ID, position, title, caption, URL | Good point-in-time evidence; external table lineage is partly outside this migration chain |
gallery_photo_objects |
Canonical photo bytes | client/property code, image ID, SHA-256, storage path, source URL | Strong reorder-immune byte identity; client_code is not a tenant FK and bucket is public by design |
conduit_kb_nodes |
Current Conduit mirror | Conduit node ID, workspace/property IDs, title/body, parent, entity IDs, source, metadata | Useful connector mirror, but mutable current state rather than immutable raw history |
| Breezeway/Streamline landing tables | Source-specific caches | vendor IDs, property code, operator notes | Useful adapters, not the permanent model |
Canonical/governance layer#
| Shape | Grain | Important fields | Audit verdict |
|---|---|---|---|
properties |
one property | UUID, workspace, display/core fields, amenities JSONB, metadata JSONB, vendor columns | UUID is a good permanent identity seed; too many facts still live outside governed atoms |
atomic_property_facts |
one chosen value per property/key | property/workspace, key, string value/type, source, confidence, status, endpoint class, verifier, meta provenance |
Strong governed fact envelope; cannot retain multiple live candidates, source authority is not data-driven |
atomic_property_entities |
stable room/bed/bath/proximity entity | UUID, parent property/entity, type, key, status, source, confidence, meta | Correct solution for repeated grains; entity keys are parser-stable rather than vendor IDs |
atomic_property_entity_facts |
one chosen value per entity/key | value/type/source/status/endpoint/provenance | Strong extension of atomic governance |
knowledge_conflicts |
one detected disagreement | property/code/workspace, field, severity, source-values/evidence JSON, chosen value/source | Source-agnostic conflict payload is reusable; lifecycle/authority policy needs generalization |
onboarding_field_catalog plus Python registry |
field definitions and intake behavior | key, type, endpoint class, capture route, required/review rules | Valuable ontology seed; code and DB catalog need one authoritative contract |
CanonicalRecord |
in-memory property projection | core, fact rows, amenities, metadata |
Effective adapter for legacy split storage; the split itself should not be the target model |
Derived/output layer#
| Shape | Grain | Important fields | Audit verdict |
|---|---|---|---|
property_listing_dossiers |
versioned property/purpose/source hash | dossier JSON, allowed/denied claims, readiness, hashes, source artifacts | Excellent reproducible accelerator boundary; reusable with a generic tenant/purpose key |
lta_listing_runs |
one LTA unit | stage, gates, artifact paths, URLs, shipped flag | Good state-machine pattern; globally unique unit code and local artifact paths are not portable |
listing_versions |
property/channel/version | copy fields, status, generation/approval/publication | Useful earlier generic concept, but LTA workflow does not use it as its durable full-copy ledger |
lta_caption_runs |
one LTA unit | stage, baseline IDs, approval, artifacts | Strong workflow state, but tenant-specific and code-keyed |
lta_caption_drafts |
one photo/run | baseline/draft/approved caption, reviewer, writer model | Strong durable content/version/provenance pattern |
lta_caption_reviews |
one review emission | exact rows shown, decisions, approver | Reusable human-approval evidence pattern |
lta_caption_push_receipts |
one written photo | before/after, approval, write/verify details | Reusable publication receipt pattern |
lta_listing_edits |
one surgical edit invocation | reason, changes, editor, optional push receipt | Good audit pattern but not a substitute for full listing-version history |
conduit_pushed_nodes |
property/overlay key | Conduit node ID, content hash, status | Good idempotency ledger; needs tenant/source-connection keys |
| HostBuddy Notion profile | one page/property | 28 section body | Human-friendly presentation, not canonical storage |
Shape-match matrix against the proposed architecture#
| Proposed requirement | Current evidence | Match | Required change |
|---|---|---|---|
| Immutable raw source history | Append-only tahoe_source_snapshots with content hashes and mutation triggers |
High for LTA | Generalize to tenant/connection/object/version; keep source adapters out of core schema |
| Vendor-neutral permanent IDs | properties.id, child entity UUIDs; vendor IDs carried separately |
Medium | Make tenant-owned resource ID the only internal FK; move every vendor key into external-identity mappings |
| Explicit source authority/conflict rules | Tahoe precedence tuple, endpoint classes, conflict ledger, human attestations | Medium-low | Store authority by tenant + entity type + field + context + effective dates; no silent code-only precedence |
| Canonical operating model | Atomic facts and entities exist | Medium | Collapse the four-home logical model into governed facts/entities behind compatibility views |
| Derived intelligence and accelerators | Dossiers, listing/caption workflows, review signals, Conduit overlays | High | Re-key as generic content/intelligence artifacts and keep client-specific recipes as configuration |
| Separate premium client data plane | Shared Supabase plus workspace_id and RLS |
Low | Deploy the reusable schema/runtime per premium client; shared control plane may hold non-client operational metadata only |
| Data view/export/transform | RPCs and export views exist; many outputs are service-role-only | Medium-low | Add client-scoped export APIs, bulk snapshots, schemas, lineage, and change streams |
| Client/agent extensibility | Conduit API client and webhooks exist in places | Low | Publish stable read APIs, scoped webhooks, event contracts, service accounts, and policy-enforced agent tools |
| Safe local/private AI | Secret walls exist on some paths | Low | Add data classification and execution policy before model calls; support local-model routes and redact/tokenize external routes |
| Vendor migration/repointing | Canonical facts are partly independent; writers are vendor-specific adapters | Medium | Formalize connector contracts and external-ID mapping; build export/import reconciliation, not PMS-to-PMS transformations |
Reusable code inventory#
REUSE AS-IS#
These are pure or narrowly infrastructural components whose behavior already matches the target principles:
lib/facts/model.py: candidate fact and conflict envelopes.lib/facts/reconcile.py: deterministic, source-agnostic conflict detection.lib/facts/persist.py: offline write plans and explicit flush seam.lib/facts/publish.py: evidence-based fail-closed promotion.lib/property_listing_claim_contract.pyandlib/listing_claim_trace.py: claim licensing and trace patterns.lib/property_listing_dossier_adversary.pyandlib/lta_caption_dossier_adversary.py: independent refutation pattern.lib/streamline_write_guard.py: approval-manifest, drift, and scoped-write guards.lib/conduit_client.py: bounded API client, pagination, workspace-specific credentials, and PMS-node protection.- Caption approval separation in
lib/lta_caption_runs.py: draft ingest cannot set approval; approval cannot rewrite draft provenance. - Immutable content hashing and replay conventions in the dossier and Bronze code.
“Reuse as-is” means keep behavior and tests. It does not mean keep Tahoe table names forever.
EXTRACT / GENERALIZE#
lib/facts/profile.pyandlib/facts/profiles/tahoe.py: retain the profile mechanism, but move source authority from a single precedence tuple to policy records.lib/property_listing_dossier.py: maketenant_id,resource_id,artifact_type, andpolicy_versionfirst-class; retain hashes and allowlists.lib/lta_caption_dossier.py: retain per-media projection; remove LTA naming and store the dossier as a versioned content artifact, not inside a run JSON blob.lib/lta_listing_runs.pyandlib/lta_caption_runs.py: extract one generic workflow-run engine with typed stages, gates, artifacts, approvals, and receipts.lib/onboarding_intake/field_registry.py: use as the ontology seed, but generate Python types and UI catalogs from one versioned schema contract.lib/onboarding_intake/views/record.py: preserve as a compatibility adapter while migrating away from split canonical storage.lib/onboarding_intake/views/notion_exports.py: preserve classification and audience-wall logic, but turn the output into a vendor-neutral knowledge-pack projection.gallery_photo_objects: generalizeclient_code/property_codeto tenant/resource IDs while retaining hash-based immutable object identity.
WRAP WITH ADAPTER#
These components should remain vendor-specific at the edge:
- Streamline capture, description writer, Airbnb writer, gallery scraper/writer.
- OwnerRez sync, listing writer, and webhook code.
- Conduit pull, push, PMS-sync verification, and entity mapping.
- Notion property-profile and working-file publishers.
- Breezeway exports and guidebook parsers.
- Reva review ingestion.
- PriceLabs, RevA, Slack, and future connectors.
Each adapter should implement the same core contract: capture raw, map external identity, emit candidates/events, read current state, plan write, execute approved write, verify, and emit receipt.
RETIRE / DO NOT CARRY FORWARD#
- The HostBuddy manual rule “guess anything missing and mark it AI-DRAFTED” in
skills/hostbuddy-profile/SKILL.core.md. Unknown operational facts must remain unknown, block unsafe answers, or route to a human. - Any runtime knowledge page as a source of truth. Notion and Conduit are editable/presentation/runtime consumers, not canonical stores.
prompts/listing-description-airbnb-channel.mdfor current LTA generation; it is already marked legacy and conflicts with the master-description split.- The older generic Streamline prompt when it conflicts with the HCH0868 contract. There should be one versioned recipe per artifact, not several “canonical” prompt files.
- Direct reliance on local artifact paths as durable evidence. A path may be a cache pointer, never the only copy of approved content.
- Global uniqueness on
unit_codeorproperty_codewithout tenant/resource scope. - Direct external-AI delivery of unredacted secrets from
lib/conduit_renderer.py. Secrets need a policy-controlled retrieval tool or local execution path. - Vendor enum growth in the core raw table. Adding a connector should not require widening a CHECK constraint.
MISSING / NEW BUILD#
- Generic connector, raw object, raw event, and sync-run schema.
- Permanent external-identity mapping table.
- Field/source authority registry with conflict rules and effective dates.
- Candidate-value store that retains all current source claims outside raw blobs.
- Generic canonical-decision ledger linking chosen values to candidates/evidence and approval.
- Generic content artifact/version/review/publication model covering descriptions, captions, reports, social posts, websites, and agent knowledge packs.
- Data classification and execution-policy engine for model/tool access.
- Client export API, scoped service accounts, webhooks, and schema/version discovery.
- Per-client data-plane deployment and backup/restore automation.
- Migration/repointing toolkit with source/destination diff and reconciliation receipts.
Duplication, drift, and collision risks#
1. The same property fact exists in many shapes#
A fact such as occupancy, parking, Wi-Fi, pet policy, hot tub, or check-in instructions can appear in:
- Streamline structured fields
- Streamline listing prose
- Streamline photo captions
- Breezeway property notes or guidebook
propertiescolumnsatomic_property_factsproperties.amenitiesproperties.metadata.owner_sopsproperty_questionnaire- HostBuddy/Notion page body
- Conduit PMS nodes
- Conduit custom overlay nodes
- listing dossier JSON
- caption dossier JSON
- generated listing/caption copy
- local staging files
The current code often handles this defensively, but the duplication remains. The new platform should distinguish source claims, canonical decisions, and published projections so users never wonder which copy to edit.
2. “Canonical” is physically split#
CanonicalRecord proves the problem: it must merge core, facts, amenities, and metadata, and its value_for method imposes a facts-over-core-over-amenities lookup order. That is a compatibility rule, not field authority. Continue supporting the adapter, but stop adding new canonical homes.
3. Source precedence is too coarse#
lib/facts/profiles/tahoe.py ranks entire sources. Real authority is field- and context-specific. Streamline may own reservation status, while a human-attested guidebook owns parking instructions, PriceLabs owns a recommendation but not the accepted rate, and Conduit owns message events but not property facts. A single precedence tuple cannot express this.
4. The Notion round trip can create echoes#
The Guest Profile is rendered to Notion, then questionnaire tooling can parse the page back into property_questionnaire, and compatibility code can synthesize q_* facts. The code contains deduplication and dual-read fallbacks because echoes already exist. The new design should make projections explicitly non-ingestible unless a user edits a governed field through a defined command.
5. LTA run keys are not tenant-safe platform IDs#
Several LTA tables use globally unique unit_code; dossiers use client_code + property_code; Bronze uniqueness omits workspace. This is acceptable for a bounded engagement, not a wide platform. Every row should carry tenant ID plus permanent resource ID; external codes belong in a mapping table.
6. Logical tenancy conflicts with the locked premium architecture#
The shared Supabase workspace/RLS work is competent, but it is the future low-cost multi-tenant pattern, not the premium client data plane. LTA data currently shares infrastructure with Roam Free. The target should reuse schema and code, not the same database/storage/secrets boundary.
7. Guest-messaging secret handling is inconsistent#
Notion exports redact Wi-Fi passwords by default, while the Conduit renderer explicitly disables redaction and delegates disclosure controls to Conduit. That is incompatible with the local/private AI promise. A secret should remain in a classified vault and be retrieved only for an authorized reservation/person/purpose, preferably without entering an LLM prompt.
8. Listing version durability trails caption durability#
Caption history is reconstructable from Supabase. Full LTA description versions are not equivalently centralized: runs store paths, Notion holds staging pages, and edits hold deltas. Introduce a full content-version ledger before scaling the pattern to social, website, compliance, and bookkeeping outputs.
9. Current-source mirrors are mistaken for raw history in places#
conduit_kb_nodes is an upserted mirror, not immutable history. The same distinction will matter for Slack, PriceLabs, RevA, and Streamline operational objects. Keep “latest mirror” for convenience, but derive it from immutable captures/events.
Recommended canonical entities and relationships#
erDiagram
TENANT ||--o{ DATA_PLANE : owns
TENANT ||--o{ RESOURCE : owns
RESOURCE ||--o{ EXTERNAL_IDENTITY : maps_to
SOURCE_CONNECTION ||--o{ RAW_OBJECT_VERSION : captures
SOURCE_CONNECTION ||--o{ SYNC_RUN : executes
RESOURCE ||--o{ FACT_CANDIDATE : receives
FACT_DEFINITION ||--o{ FACT_CANDIDATE : types
RAW_OBJECT_VERSION ||--o{ FACT_CANDIDATE : evidences
RESOURCE ||--o{ CANONICAL_DECISION : has
FACT_DEFINITION ||--o{ CANONICAL_DECISION : defines
FACT_CANDIDATE ||--o{ CANONICAL_DECISION : supports
CANONICAL_DECISION ||--o{ CONFLICT : resolves
RESOURCE ||--o{ CONTENT_ARTIFACT : produces
CONTENT_ARTIFACT ||--o{ CONTENT_VERSION : versions
CONTENT_VERSION ||--o{ APPROVAL : reviewed_by
CONTENT_VERSION ||--o{ PUBLICATION_RECEIPT : delivered_as
RESOURCE ||--o{ MEDIA_ASSET : owns
MEDIA_ASSET ||--o{ MEDIA_PLACEMENT : appears_as
TENANT ||--o{ AGENT_POLICY : governs
Recommended definitions:
- Tenant: client legal/operating boundary.
- Data plane: the client's database, storage, secrets, queues, workers, backups, and optional local-model runtime.
- Resource: permanent client-owned object such as property, owner, guest, reservation, conversation, task, vendor, account, document, or media asset.
- External identity:
{resource_id, connection_id, external_type, external_id, valid_from, valid_to}. Vendor IDs never become internal identity. - Raw object version: immutable source response/event with tenant, connection, external object, capture time, source-effective time, schema version, hash, payload location, and classification.
- Fact definition: canonical key, entity type, data type, units, sensitivity, audience, freshness SLA, validation, and allowed uses.
- Fact candidate: one source's claim, linked to raw evidence, with observed/effective time and parser version.
- Canonical decision: chosen value plus explicit policy/approver, candidate links, validity range, and supersession. No last-write-wins.
- Conflict: competing candidates, severity, status, owner, resolution, and downstream impact.
- Content artifact/version: descriptions, captions, knowledge packs, reports, social posts, website pages, compliance packets, and bookkeeping workpapers, all versioned against a canonical snapshot and recipe/model version.
- Approval: who approved what exact hash, for which use and scope.
- Publication receipt: destination, external identity, before/after hashes, execution result, readback, and rollback pointer.
- Agent policy: data classes, permitted models/tools, local-only rules, human approval requirements, budgets, and kill switch.
Migration and wrapping plan without rewrites#
Phase 0: freeze contracts, do not move data#
- Treat current LTA and Roam Free workflows as acceptance fixtures.
- Preserve the existing tests around facts, dossiers, claims, write gates, caption approvals, and Conduit sync.
- Record current table/API contracts and mark every current source as canonical, mirror, projection, or scratch.
Phase 1: introduce generic interfaces around existing code#
- Define adapter protocols for capture, identity mapping, candidate extraction, write planning, execution, and verification.
- Make current Streamline, OwnerRez, Breezeway, Conduit, Notion, and Reva modules implement those protocols through thin wrappers.
- Do not change their live behavior.
Phase 2: add the permanent identity and authority layer#
- Seed permanent resource IDs from the existing
properties.idvalues where safe. - Populate external identities from Streamline home IDs, OwnerRez property IDs, LTA unit codes, Breezeway IDs, and Conduit entity IDs.
- Add a field authority registry and import the current Tahoe precedence as an explicit initial policy, then refine field by field.
Phase 3: shadow the generic raw/candidate/decision model#
- On each current capture, write the existing Bronze row and a new generic raw-object version.
- Parse candidates through the existing generic
lib/factsengine and compare candidate/conflict/canonical outputs with current tables. - No consumer repoint until diffs are explained and accepted.
Phase 4: unify derived content history#
- Persist every listing description and caption as generic content versions while continuing to feed current LTA tables.
- Import existing caption ledgers and listing dossiers by hash without rewriting the copy.
- Use current approval manifests and write receipts as the first implementation of generic approvals/publications.
Phase 5: repoint one consumer at a time#
Recommended order:
- Read-only dashboard and exports
- Listing dossier reads
- Caption dossier reads
- Conduit knowledge projection
- Streamline/OwnerRez approved writes
Every repoint runs in shadow first and compares hashes, claims, redactions, and receipts.
Phase 6: deploy the same core into separate client data planes#
- Roam Free remains the first real proving tenant for every capability.
- Deploy the same schema/runtime into an LTA-isolated data plane before treating LTA as the reference premium architecture.
- Migrate through immutable export/import plus reconciliation, not direct database copying without proof.
Phase 7: retire compatibility paths#
- Retire legacy prompts,
q_*echoes, local-only records, direct secret-to-Conduit rendering, and globally unique unit-code assumptions only after consumers have moved and replay tests pass.
This is a strangler migration: wrap, shadow, compare, repoint, then retire. There is no justification for rewriting the proven gates and adapters.
Architecture decisions for Taylor, ordered by dependency#
These should be asked one at a time.
- Canonical value model: approve
raw versions → fact candidates → explicit canonical decisions → derived artifacts, with no last-write-wins and no projection allowed to become canonical implicitly. - Source authority model: approve field- and context-specific authority records, rather than one global source precedence per client.
- Premium isolation boundary: confirm LTA will move to its own data plane before broad production use, while the shared Supabase remains the proving environment and possible future low-cost architecture.
- Permanent identity: approve internal tenant-owned UUIDs for every resource and a separate external-identity mapping for all vendor IDs/codes.
- Canonical storage rule: approve governed facts/entities as the only new canonical homes;
propertiescolumns, amenities JSON, metadata, Notion, and vendor mirrors become compatibility projections. - Content artifact model: approve one generic version/approval/publication ledger for listings, captions, knowledge packs, social posts, websites, compliance outputs, and later bookkeeping deliverables.
- Unknown-fact behavior: approve fail-closed handling for guest and operational facts; AI may propose a question or draft, but may not promote a guessed fact into runtime knowledge.
- Secret/model policy: approve local-only or tool-retrieval handling for access credentials and other sensitive values; no external AI prompt receives them by default.
- Client extensibility contract: approve read APIs, export snapshots, webhooks, scoped service accounts, and client-owned schemas as first-class product surfaces from the first production data plane.
- Migration method: approve wrap-and-shadow migration with hash/diff parity gates, not a new greenfield rewrite.
First-principles challenge to the current assumptions#
- Streamline should not be “the source of truth whenever it has a field.” It should be authoritative only for fields the client assigns to it. Otherwise the vendor remains the data model.
- Notion should not be the source of truth for Layer 2. It can be the preferred human editing surface, but edits should become governed commands/candidates in the client data plane.
- A canonical fact is not simply the highest-ranked current source value. It is a time-bounded decision with evidence, policy, and an owner.
- “Guest-facing” is not sufficient authorization for a secret. Wi-Fi and access credentials require reservation/person/purpose checks and should preferably be retrieved as tools, not embedded in reusable knowledge.
- A listing description and a guest-messaging knowledge pack should not independently restate the same facts as editable prose. Both should be projections from the same decisions, with separate recipes and audience policies.
- The data platform should not be optimized around property content alone. Property, reservation, owner, guest, conversation, task, vendor, account, document, and event identities must share the same raw/candidate/decision/derived pattern so the base can support CRM, social, bookkeeping, compliance, and websites.
Bottom line#
The repository has already solved many hard execution problems. The new platform should keep those solutions and place them behind a broader, client-owned data contract.
The single most valuable next decision is whether a canonical value is an overwritable row or an explicit decision over preserved source candidates. The recommendation is the latter. That choice makes source authority, conflict resolution, auditability, client exports, model safety, vendor migration, and future accelerators coherent.