Files
support_backend/specs/004-product-knowledge/research.md
T

87 lines
6.2 KiB
Markdown
Raw Normal View History

# Phase 0 Research: Product Knowledge Management & Retrieval
## Decision: Versioning mechanism — new row per version, not in-place overwrite
- **Decision**: `docs/06-database-schema.md`'s `KnowledgeEntry`/`Runbook` models are explicitly
"conceptual/pseudo-Prisma... refine field types... during Phase 1 modeling" — their flat
`version Int` field alone doesn't satisfy this feature's FR-004/FR-009 ("prior versions MUST
remain retrievable"), since a plain in-place `UPDATE` overwrites history. This feature refines
the schema: each edit inserts a **new row** sharing the same logical identifier (`code` for
`KnowledgeEntry`, `key`+`productId` for `Runbook`) with `version` incremented, and exactly one
row per logical identifier has `isCurrentVersion: true` at a time. The unique constraint moves
from a bare `code`/`key` to `(code, version)` / `(key, productId, version)`; a partial-unique-
style application check (see next decision) keeps only one current version.
- **Rationale**: This is the standard "immutable version history" pattern and directly satisfies
"prior versions remain retrievable by their own identity" (FR-004) without a separate audit
table — the versions themselves ARE the history, consistent with Constitution Principle VI.
- **Alternatives considered**: A separate `KnowledgeEntryVersion` history table with the main row
only ever holding "current" — rejected as more schema surface for the same guarantee; querying
"give me version 3 of KB-DQ-102" is equally simple either way, and one-table-per-entity-type
keeps retrieval queries (which only ever care about the current version) simpler.
## Decision: Concurrency on edit — conditional update + insert, same class as 003-ticketing
- **Decision**: Creating a new version is two steps inside one transaction: (1)
`UPDATE ... WHERE code = ? AND version = ? AND isCurrentVersion = true SET isCurrentVersion =
false` (the caller's `expectedVersion` must match the current row) — zero rows affected means a
concurrent edit already won, and this edit is rejected with `409 CONFLICT`; (2) only if step 1
affected exactly one row, insert the new current-version row.
- **Rationale**: Directly reuses the optimistic-concurrency pattern already established in
003-ticketing's `Ticket.version` handling — same shape of problem (two admins editing the same
entry), same solution, no new concurrency-control concept introduced into the codebase.
- **Alternatives considered**: Last-write-wins (no `expectedVersion` check) — rejected, would let
one admin's edit silently clobber another's without either of them knowing, which is exactly
what Constitution Principle VII's concurrency requirement exists to prevent.
## Decision: Retrieval — structured filtering, no vector/embedding search
- **Decision**: A retrieval query is `WHERE productId = ? AND isCurrentVersion = true AND status
= 'published' AND (effectiveDate IS NULL OR effectiveDate <= now()) AND (feature filter if
given) AND (categoryScope filter if given)`, ordered by `validationStatus = 'validated'` first,
then by `effectiveDate DESC` (most recently published first) as a simple, defensible tiebreak.
- **Rationale**: Per spec.md's Assumptions and doc 11 §B1, full semantic retrieval is explicitly
a future decision (embedding model, chunking, re-ranking) — this feature's job is a correct,
real, *filtered* retrieval contract that a semantic layer can be added in front of later
without changing what "correct" means (doc 11 §B1's "filters apply before the vector search"
requirement is satisfied by construction, since there's no vector search yet to apply them
before).
- **Alternatives considered**: Postgres full-text search (`tsvector`/`tsquery`) on
problem/symptoms text — considered as a nearer-term relevance improvement, but deferred: it
would still not be "the RAG layer" doc 03 describes, adds index/query complexity beyond what
this phase's requirements (FR-011 through FR-013) actually ask for, and can be added later as
a ranking refinement without a breaking contract change.
## Decision: Known issue lookup by error code
- **Decision**: `KnownIssue.errorCodeId` is a nullable FK to `ErrorCode`; lookup is a direct
`WHERE errorCodeId = ?` query (via the `ErrorCode`'s own id, resolved from its `code` string
first if the caller only has the string).
- **Rationale**: Matches doc 06's shape exactly (`KnownIssue.errorCodeId String?`) and FR-007's
"retrieve a known issue directly by its error code" — a simple indexed FK lookup, no special
design needed.
## Decision: Module placement — new `ai-support/knowledge` module group
- **Decision**: Create `src/modules/ai-support/knowledge/` now, following the standard module
shape (controller/routes/schema/repository/service/types/mapper/constants/index.ts) used by
every other module in this codebase. No other `ai-support` submodule
(agents/sessions/diagnosis/troubleshooting/tools/tool-execution/verification/escalation) is
created — those remain nonexistent until the Phase 4 feature that needs them, matching how
`identity/auth` and most of `orchestration`/`platform` remain untouched placeholder stubs from
the original scaffold rather than being pre-built speculatively.
- **Rationale**: Doc 07 explicitly places `knowledge` inside the `ai-support` group — this is
the documented, correct location, not an open design choice.
- **Alternatives considered**: Placing it under `catalog` (since it's product-scoped content,
similar to `catalog/products`/`catalog/categories`) — rejected; doc 07 already answers this
question, and following it keeps the module layout matching the architecture doc exactly.
## Decision: Admin endpoint authentication
- **Decision**: All admin CRUD endpoints (create/publish/unpublish/version-edit for knowledge
entries and runbooks; create for error codes/known issues) are gated by the existing
`fastify.authenticate` decorator — same known-limitation pattern as 002/003's admin routes (it
doesn't perform real JWT verification yet).
- **Rationale**: Consistency with every other admin surface built so far; introducing a different
auth mechanism just for this feature would be inconsistent without a reason to be.
- **Alternatives considered**: None — this follows established precedent directly.