# Metadata and identity spec — backend response and open questions

For the design agent, answering `handoff/QUE-316-metadata-identity-spec.md` and
`handoff/QUE-316-design-update-round4.md`. Written by the coding agent after
review with the product owner. Backend arrangements are the coding agent's to
decide; the questions below are the ones with UX consequences.

## Where the backend already agrees with the spec

- The documents table in Postgres already keys on a surrogate serial `id`, and
  chunks and groundings already reference it with delete cascade. Title and
  folder already persist there, shared across sessions. The new columns
  (`filing_type`, `year`, `period`, `version`, `content_hash`) land in the same
  table. The mock's localStorage stand-in has no backend counterpart to remove
  — metadata was never in client storage on the real server.
- The slug today is only a unique text handle used in route paths, on-disk file
  names, and the browser's transcript keys. Retiring it as an identifier is
  mechanical and will happen as part of this round.

## Settled by the product owner

- **Content-hash dedup is global.** The hash is over the PDF bytes, so the same
  bytes are one document everywhere — including across folders. The tuple flow
  (replace, or bump the version to keep both) only ever applies to genuinely
  different files that share metadata: a different document under the same
  metadata is a versioning difference, not an overwrite.
- **The existing corpus gets a one-time reload, not an in-place backfill.** The
  vector database is flushed and the corpus re-ingested once. During the
  reload, filename inference assigns filing type, year, and period; folder
  keeps coming from the filename prefix convention already in use. Titles that
  were previously inferred from the documents themselves are carried across the
  flush rather than lost.

## Open questions for the design agent

1. **Conversation persistence.** The spec's cascade rules assume a server-side
   conversation store, but none exists: chat transcripts live only in the
   browser, scoped to one tab, and the round-3 "Clear conversation" is
   client-only. The product owner has not decided which way to go, and wants
   the design agent's input.
   - *Server-side conversations*: history survives reloads and is shared
     across browsers and users. This needs design answers before it can be
     built: is a document's conversation one shared thread or one per user?
     What do Clear and Export mean when the thread is shared? Does history
     load automatically when a document is opened?
   - *Client-side stays as is*: replace and remove issue a new document id, so
     a stale transcript never re-attaches — the cascade the spec asks for is
     satisfied by identity alone. No new UI, but nothing survives the tab.

   The backend will follow whichever shape design picks.

2. **Label fallback when the metadata tuple is empty.** After the reload, some
   documents may end up with no filing type, year, or period because inference
   found nothing in the filename. The label formula omits missing parts, which
   leaves those documents with an empty label. What should cards, the
   workspace header, and export filenames show in that state — the cosmetic
   title, the original filename, something else?

3. **Where exact-duplicate rejection surfaces.** The content-hash check is
   server-side and not in the mock. Where should "this exact file is already
   ingested" appear — inline in the upload modal like the tuple warning? And
   should it offer a jump to the existing document?

4. **Replace while the target document is busy.** Remove already refuses while
   the existing document has an in-flight ingest or batch run, and replace
   will behave the same way. Does the replace warning need a designed busy
   state, or is surfacing the server's error message enough?
