V1 Web PWA · Walk result

Dependency result: Library collection contract

A dated evidence record for V1 Web PWA: observed behavior, current source reconciliation, automation evidence, and explicit proof limits.

verdict: partial· date unknown
8sections4 minread1table

On this page
  • Opened: 2026-05-29 when collections were legacy rows with opaque item ids and no canonical Library namespace or persistence model.
  • Reconciled: 2026-07-18 against the current Library contract, registry, Prisma output, migration, creation adapter, and live-DB test. This was a source reconciliation, not a fresh production walk.
  • Verdict: partial — the original schema-design dependency is closed and creation now produces a canonical collection row. The legacy table remains read authority, unknown item ids are coerced into the note: namespace, paired writes are non-atomic, and no edit/share/version/collaboration path proves the richer contract after creation.
  • Primary spec: apps/oshun/bff/src/routes/domain-stubs-postgres.test.ts.

Result at a glance#

Evidence lane Current result Authority limit
Contract design Ordered typed items, visibility, collaborators, version tag, and state ship Encoded decisions; no public-sharing or collaboration policy validation
Persistence model Registry, Prisma model, migration, canonical indexes, and tombstones ship Repository evidence, not a production rollout receipt
Creation write collections.add() inserts legacy and canonical rows Sequential queries; no atomicity or reconciliation mechanism
Item mapping Known prefixes pass through; unknown ids become normalized note:<slug> Coercion can lose the source item kind and create normalized collisions
Automated evidence Live-DB test verifies three mapped ids plus private/active defaults No LibraryCollectionSchema.parse over the persisted row
Read/edit lifecycle Collection listing reads the legacy table No canonical edit, archive, share snapshot, collaborator, or read cutover

Evidence map#

The shipped lane creates a safe private canonical projection. The richer collection behavior remains beyond that first write.

flowchart LR A[Create collection] --> B[Legacy collection row] B --> C[Normalize item ids] C --> D[Canonical private active row] D -. reads still legacy .-> E[Collection list] D -. no connected flow .-> F[Share or version snapshot] D -. no connected flow .-> G[Collaborator changes] B -. no atomic rollback .-> H[Possible split write]

Proven observations#

The contract decisions now ship#

  • LibraryCollectionSchema preserves item order, requires kind-prefixed item identifiers, distinguishes private, unlisted, and public, models editor and viewer collaborators, carries an optional version tag, and enforces that itemCount matches the list without duplicate items or collaborators.
  • The persistence registry includes the Library domain. The generated model maps to v1_library_library_collection; migration 20260528022916_nyx_logged_observation_and_library_collection creates the canonical table and its tenant, identity, payload, live-scan, and tombstone indexes.
  • The old claims that the Library namespace and canonical persistence domain were missing are therefore historical, not present-tense gaps.

The initial projection is intentionally conservative#

  • collections.add() stores the original item ids in the legacy row. For the canonical row it preserves recognized prefixes and maps every unprefixed id to a normalized note: id. The canonical collection begins private, active, unversioned, and without collaborators or optional presentation fields.
  • The live-DB test creates a collection with two recognized prefixes and one raw id, then proves the canonical row contains the three expected mapped ids, the correct count, and the private/active defaults.
  • Listing remains owner-scoped on the legacy table, preserving current product behavior while the canonical read model is unfinished.

Boundaries and gaps#

  • A canonical write is not canonical read authority. The customer list is still assembled from goal3_stub_collection; canonical-only edits or repairs would be invisible.
  • A dual-write is not an atomic write. The two inserts are independent queries. The adapter has no transaction, outbox, or repair queue for a legacy success followed by canonical failure.
  • Prefixing is not type discovery. Treating an unknown id as note: keeps the row syntactically classifiable but does not establish what the underlying object is. Normalization can also collapse distinct raw strings to one slug.
  • A SQL row is not a Zod-validated collection. The generated ids and owner values can be non-UUID even though the contract requires UUIDs, and the focused test checks columns without reconstructing and parsing the object.
  • Private creation is not sharing semantics. No automated path changes visibility, creates an immutable share version, prevents a private-item leak, or exercises collaborator permissions.
  • Schema source is not production deployment proof. This audit did not inspect a production migration ledger or real customer collection data.

Re-run evidence#

Run the focused live-DB assertion with the repository Postgres test setup:

bash
pnpm exec vitest run \
  apps/oshun/bff/src/routes/domain-stubs-postgres.test.ts \
  -t "collections.add\(\) dual-writes"

A green result proves the current creation projection and its defaults. It does not prove atomic writes, canonical reads, item-kind correctness, or public and collaborative collection behavior.

Source trail#

Cross-references#

Open questions#

  • What authoritative lookup classifies each legacy item id before canonical cutover, and how are normalization collisions detected?
  • Which service owns collection reads and edits once the legacy table is no longer authoritative?
  • Will paired writes become transactional or outbox-backed, and how will historical mismatches be inventoried and repaired?
  • Which tests prove visibility inheritance, immutable shared versions, and editor/viewer permissions across mixed private and public items?
  • How will existing non-UUID collection and owner identifiers be represented in objects that must pass LibraryCollectionSchema?