Files
support_backend/specs/004-product-knowledge/research.md
T
saqib mirandClaude Sonnet 5 e947ac44b8 docs: plan and design artifacts for product knowledge feature
/speckit-plan output for 004-product-knowledge: technical context and
constitution gate check (all PASS), Phase 0 research (6 decisions:
new-row-per-version instead of in-place overwrite to satisfy history
retention, conditional-update-then-insert concurrency reusing
003-ticketing's optimistic-locking pattern, structured (non-semantic)
filtered retrieval per doc 11 gap B1, known-issue lookup by error
code, creating the ai-support module group for the first time with
only its knowledge submodule populated, and admin auth consistent
with prior features), Phase 1 data model (KnowledgeEntry/ErrorCode/
KnownIssue/Runbook, refining doc 06's conceptual schema with an
explicit version-history mechanism), the admin CRUD + retrieval
contract, and a 6-scenario quickstart.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 15:30:46 +05:30

6.2 KiB

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: 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.