Skip to content

[Project history] Add authoritative lifecycle ingestion and idempotent reconciliation #284

Description

@seonghobae

Why this follows #280 / #285

#285 adds the evidence-gated Project history read model and Buyer timeline by projecting the existing normalized source_post, post_project_mention, post_summary_role, and post_lineage_edge authority. A production deployment still needs a source-owned writer boundary so an authorized external adapter can supply explicit lifecycle semantics without direct ad hoc SQL or title/body guessing.

This is a separate integration slice. It must not create a competing project-history store or expand #285's read/UI review surface.

Entry condition

Implementation starts only when one concrete, owned source adapter provides a documented and versioned source contract. Generic SAP, CRM/VOC, tender, or delivery code guesses are not an implementation contract.

The source contract must identify:

  • canonical project key and display name;
  • source_system_code plus source_record_key identity;
  • explicit lifecycle event code and mapping version;
  • offset-aware event start/end when present;
  • corporate-entity and visibility provenance;
  • the authorized source post or source-reference boundary that carries the evidence;
  • replacement and withdrawal semantics owned by that source channel.

Unknown codes fail closed and remain only in the original imported source representation. LLM output may propose a review candidate through contextual-orchestrator, but it cannot write an authoritative lifecycle fact until a deterministic policy or human review accepts it.

Minimal writer boundary

  • Reuse the existing PostgreSQL import/application-service boundary and feat: add Buyer Project history destination (v2.18.0) #285's normalized authority.
  • Validate lookup codes, source identity, evidence ownership, visibility, and project identity before mutation.
  • Replace only rows owned by the same source-system record/channel.
  • Persist the source post and its project, role, and lineage evidence atomically.
  • Re-running the same source record is idempotent.
  • Withdrawing a source record removes only that channel's projection; independent evidence remains.
  • Serialize concurrent writes on the existing source identity with the database's native transaction/constraint boundary.
  • If the concrete source exposes a lifecycle fact that the existing model cannot represent, record an ADR before adding a two-or-more-word snake_case, third-normal-form object. Do not pre-create speculative tables.

Security and audit

  • Administrative import permission remains separate from post_read.
  • Provider credentials, bearer tokens, and private records never enter repository fixtures, logs, screenshots, or ADR examples.
  • Existing source-post lifecycle eligibility remains the read-side publication gate.
  • Audit actor, mapping version, source identity, and before/after digest without duplicating source PII.

RED → GREEN acceptance

  1. The same source identity submitted twice produces one authoritative projection.
  2. Replacement changes only that source channel's fields and evidence links.
  3. An unknown event code writes no lifecycle fact.
  4. Missing, mismatched, hidden, or unauthorized evidence is rejected.
  5. Source withdrawal removes its projection while independently evidenced rows survive.
  6. Concurrent duplicate imports converge without deadlock or orphan evidence.
  7. Real-PostgreSQL tests cover clean install, upgrade, replacement, withdrawal, and rollback.
  8. Repository validation remains synthetic and non-identifying; private runtime validation returns aggregate evidence only.
  9. Changed production modules retain 100% statement/branch coverage and complete public docstrings.

Stack boundary

Implement after #285 is merged on its protected current head and a concrete source contract satisfies the entry condition. Keep this writer API out of the Buyer browser surface and out of MCP Global Ask, TEPP, fast-mlsirm, and Event Lineage score-persistence work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions