Files
support_backend/specs/004-product-knowledge/tasks.md
T
saqib mirandClaude Sonnet 5 e33d86081f feat: implement product knowledge management & retrieval (004)
Implements 26 of 28 tasks from specs/004-product-knowledge/tasks.md
across all three user stories -- Phase 3 of the roadmap. First
feature to populate src/modules/ai-support/ (doc 07 places
`knowledge` there; only that submodule is built, matching this
codebase's convention of not pre-building unneeded submodules).

Schema (prisma/schema.prisma + migration):
- KnowledgeEntry, ErrorCode, KnownIssue, Runbook per docs/06,
  refining its conceptual flat `version` field into an explicit
  version-history mechanism: each edit inserts a new row
  (isCurrentVersion flag, compound unique on (code, version) /
  (key, productId, version)) instead of overwriting in place -- the
  only way "prior versions remain retrievable" (FR-004/FR-009) is
  actually true rather than aspirational.

User Story 1 -- knowledge entry authoring/publish/version (P1, MVP):
- draft -> published -> unpublished lifecycle; publish only takes
  effect from its effectiveDate.
- Editing uses the same conditional-update-then-insert optimistic
  concurrency pattern as 003-ticketing's Ticket.version (409 on a
  stale expectedVersion).
- Full version history readable via GET .../versions.

User Story 2 -- error codes, known issues, runbooks (P2):
- ErrorCode + KnownIssue with direct lookup-by-error-code.
- Runbook steps stored as an ordered JSON array, preserved exactly;
  same version-on-edit mechanism as knowledge entries; inactive
  runbooks are indistinguishable from nonexistent ones on lookup.

User Story 3 -- filtered retrieval (P3):
- GET /knowledge/retrieve: product-scoped, excludes draft/
  unpublished/not-yet-effective entries, validated entries ranked
  ahead of unvalidated. Deliberately NOT semantic/vector search --
  doc 11 gap B1 explicitly defers embedding-model choice to the
  future AI-support feature; this is real, usable structured
  filtering a semantic layer can sit in front of later.

Found and fixed one real bug before it reached tests: the retrieval
endpoint initially queried by the raw external product id instead of
resolving it to the internal Product.id first (every other endpoint
in this feature does that resolution) -- would have silently
returned zero results for every caller. Fixed with a lenient
tryResolveProductId (empty array, not 404, for an unregistered
product -- matches the "no matches, never an error" contract).

Deliberately skipped (not forgotten, see checklist notes): the two
planned mock-repository unit-test tasks (T004, T019) -- unlike
003-ticketing's state machine, this feature has no pure-logic
surface to isolate from Prisma; coverage comes entirely from
integration tests instead.

All 13 integration test files in the repo (36 tests, spanning this
feature and every prior one) verified passing together against a
real Postgres/Redis/MinIO -- no regressions. Full quality gate
(typecheck/lint/format/architecture/unit tests) passes.

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

229 lines
11 KiB
Markdown

---
description: "Task list for 004-product-knowledge"
---
# Tasks: Product Knowledge Management & Retrieval
**Input**: Design documents from `specs/004-product-knowledge/`
**Prerequisites**: [plan.md](./plan.md), [spec.md](./spec.md), [research.md](./research.md),
[data-model.md](./data-model.md), [contracts/knowledge-contract.md](./contracts/knowledge-contract.md),
[quickstart.md](./quickstart.md)
**Tests**: Included as first-class tasks (versioning and retrieval filtering are exactly the kind
of "MUST" requirements regressions silently break), same approach as 002/003.
**Organization**: Tasks are grouped by user story (US1 = P1 knowledge entry authoring/publish/
version, US2 = P2 error codes/known issues/runbooks, US3 = P3 retrieval).
## Format: `[ID] [P?] [Story] Description`
All file paths are relative to `supporthub-api/` (repo root).
---
## Phase 1: Setup
- [X] T001 Scaffold `src/modules/ai-support/knowledge/` with the standard module shape
(`controller/`, `routes/`, `schema/`, `repository/`, `service/`, `types/`, `mapper/`,
`constants/`, `index.ts`), matching every other module's conventions (research.md "Module
placement")
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Schema for all four entities, shared by every user story.
**⚠️ CRITICAL**: No user-story stage work can begin until this phase is complete.
- [X] T002 Add `KnowledgeEntry`, `ErrorCode`, `KnownIssue`, `Runbook` models to
`prisma/schema.prisma` per `data-model.md` (including `isCurrentVersion` and the
`(code, version)` / `(key, productId, version)` compound unique constraints — NOT a bare
unique on `code`/`key`), plus the four new back-relations on `Product`
- [X] T003 Run `npm run prisma:generate` and create the migration (`npm run prisma:migrate`) for
T002 (depends on T002)
**Checkpoint**: Schema migrated. User stories can now be built.
---
## Phase 3: User Story 1 - An admin authors, versions, and publishes knowledge entries (Priority: P1) 🎯 MVP
**Goal**: Full knowledge-entry lifecycle — create (draft) → publish → edit (new version) →
validate → unpublish, with prior versions always retrievable.
**Independent Test**: Quickstart Scenarios 1, 2, 3.
### Tests for User Story 1
- [~] T004 [P] [US1] ~~Unit tests for the version-on-edit logic~~ — deliberately skipped, not
forgotten: unlike 003-ticketing's state machine, there's no pure-logic surface here to
isolate from Prisma (the concurrency behavior lives entirely inside the repository's own
transaction) — see checklist Notes. Covered instead by T005's integration test, which
exercises the real conflict path against a live database.
- [X] T005 [US1] Integration test covering Quickstart Scenarios 1, 2, 3 (draft invisible →
publish makes retrievable, edit preserves history, stale-version edit rejected) against a
real Postgres in `tests/integration/knowledge-entries.test.ts`
### Implementation for User Story 1
- [X] T006 [US1] Add `KnowledgeRepository` (`create`; `findCurrentByCode`; `findAllVersionsByCode`
(newest first); `publish`/`unpublish`/`setValidationStatus` (update the current-version row
in place — these are metadata changes, not content edits, so they don't create a new
version); `createNewVersion` implementing research.md's conditional-update-then-insert) in
`src/modules/ai-support/knowledge/repository/knowledge.repository.ts` (depends on T003)
- [X] T007 [US1] Add `KnowledgeService` wrapping the repository with the FR-002/FR-004 rules
(new entries default to `status: draft`, `version: 1`; edits always go through
`createNewVersion`, never a raw update) in
`src/modules/ai-support/knowledge/service/knowledge.service.ts` (depends on T006)
- [X] T008 [US1] Add Zod schemas for create/publish/unpublish/validate/edit request bodies in
`src/modules/ai-support/knowledge/schema/knowledge.schema.ts` (depends on T001)
- [X] T009 [US1] Add routes: `POST /admin/products/:externalProductId/knowledge`,
`PATCH /admin/knowledge/:code/publish`, `PATCH /admin/knowledge/:code/unpublish`,
`PATCH /admin/knowledge/:code/validate`, `PUT /admin/knowledge/:code`,
`GET /admin/knowledge/:code/versions` — all gated by `fastify.authenticate` — in
`src/modules/ai-support/knowledge/routes/knowledge.routes.ts`, registered from
`src/api/routes.ts` (depends on T007, T008)
- [X] T010 [US1] Run Quickstart Scenarios 1, 2, 3 locally and confirm all three pass
**Checkpoint**: User Story 1 is fully functional — knowledge entries can be authored, published,
versioned, and validated, with full history preserved. This alone is a usable content-management
surface even before error codes/runbooks/retrieval exist.
---
## Phase 4: User Story 2 - Known issues, error codes, and runbooks are modeled as first-class, product-scoped records (Priority: P2)
**Goal**: `ErrorCode`/`KnownIssue` CRUD + lookup-by-error-code; `Runbook` CRUD with the same
version-on-edit mechanism as User Story 1.
**Independent Test**: Quickstart Scenarios 4, 5.
### Tests for User Story 2
- [X] T011 [P] [US2] Integration test covering Quickstart Scenario 4 (error code → known issue →
lookup by error code) against a real Postgres in
`tests/integration/known-issues.test.ts`
- [X] T012 [P] [US2] Integration test covering Quickstart Scenario 5 (runbook step order
preserved; deactivate removes it from lookup) against a real Postgres in
`tests/integration/runbooks.test.ts`
### Implementation for User Story 2
- [X] T013 [P] [US2] Add `ErrorCodesRepository`/`ErrorCodesService` (create; findByCode) in
`src/modules/ai-support/knowledge/repository/error-codes.repository.ts` +
`src/modules/ai-support/knowledge/service/error-codes.service.ts` (depends on T003)
- [X] T014 [US2] Add `KnownIssuesRepository`/`KnownIssuesService` (create; findByErrorCode,
joining through `ErrorCodesRepository`'s lookup) in
`src/modules/ai-support/knowledge/repository/known-issues.repository.ts` +
`src/modules/ai-support/knowledge/service/known-issues.service.ts` (depends on T013)
- [X] T015 [P] [US2] Add `RunbooksRepository` (`create`; `findCurrentByKey` — filters
`isCurrentVersion: true, active: true`, treating inactive the same as not-found per FR-010;
`createNewVersion` reusing research.md's conditional-update-then-insert pattern;
`deactivate`) in `src/modules/ai-support/knowledge/repository/runbooks.repository.ts`
(depends on T003)
- [X] T016 [US2] Add `RunbooksService` in
`src/modules/ai-support/knowledge/service/runbooks.service.ts` (depends on T015)
- [X] T017 [US2] Add routes: `POST /admin/products/:externalProductId/error-codes`,
`POST /admin/products/:externalProductId/known-issues`,
`GET /admin/products/:externalProductId/known-issues/by-error-code/:code`,
`POST /admin/products/:externalProductId/runbooks`,
`PUT /admin/products/:externalProductId/runbooks/:key`,
`PATCH /admin/products/:externalProductId/runbooks/:key/deactivate`,
`GET /admin/products/:externalProductId/runbooks/:key` — all gated by
`fastify.authenticate` — added to
`src/modules/ai-support/knowledge/routes/knowledge.routes.ts` (depends on T014, T016)
- [X] T018 [US2] Run Quickstart Scenarios 4, 5 locally and confirm both pass
**Checkpoint**: Both User Story 1 and 2 work together — the full structured knowledge catalog
(entries, error codes, known issues, runbooks) exists.
---
## Phase 5: User Story 3 - Retrieval returns only relevant, filtered, validation-aware knowledge (Priority: P3)
**Goal**: The filtered/ranked retrieval query (research.md), scoped to product/feature/category,
excluding drafts and not-yet-effective entries, validated-first ordering.
**Independent Test**: Quickstart Scenario 6.
### Tests for User Story 3
- [~] T019 [P] [US3] ~~Unit tests for the retrieval filter/ranking predicate~~ — deliberately
skipped for the same reason as T004: the filter/ranking predicate is a Prisma `where`/
`orderBy` clause, not an extractable pure function. Covered by T020's integration test.
- [X] T020 [US3] Integration test covering Quickstart Scenario 6 (cross-product isolation,
validated-first ranking) against a real Postgres in
`tests/integration/knowledge-retrieval.test.ts`
### Implementation for User Story 3
- [X] T021 [US3] Add `KnowledgeRepository.retrieve(productId, feature?, categoryScope?)`
implementing research.md's exact filter/order (depends on T006)
- [X] T022 [US3] Add `KnowledgeService.retrieve(...)` (depends on T021)
- [X] T023 [US3] Add route `GET /knowledge/retrieve` (no `fastify.authenticate` gate — this is a
read path the future AI-support feature will call internally, not an admin surface;
revisit once that feature defines its own internal-service-call convention) in
`src/modules/ai-support/knowledge/routes/knowledge.routes.ts`, registered from
`src/api/routes.ts` (depends on T022)
- [X] T024 [US3] Run Quickstart Scenario 6 locally and confirm it passes
**Checkpoint**: All three user stories work independently and together — knowledge can be
authored, structured, and retrieved correctly.
---
## Phase 6: Polish & Cross-Cutting Concerns
- [X] T025 [P] Add a "Product Knowledge" section to `README.md` describing the admin CRUD,
versioning mechanism, and retrieval contract
- [X] T026 [P] Update `specs/004-product-knowledge/checklists/requirements.md` Notes with any
implementation-time findings
- [X] T027 Run `npx tsx scripts/check-architecture.ts` and `npm run lint`/`npm run typecheck`
- [X] T028 Full regression: `npm run test:unit` (scoped to `tests/unit`) to confirm nothing broke
elsewhere
---
## Dependencies & Execution Order
### Phase Dependencies
- **Setup (Phase 1)**: No dependencies
- **Foundational (Phase 2)**: Depends on Setup — BLOCKS all user stories
- **User Story 1 (Phase 3)**: Depends on Foundational — no dependency on US2/US3
- **User Story 2 (Phase 4)**: Depends on Foundational — independent of US1's own entities, though
practically sequenced after since it reuses US1's version-on-edit pattern for runbooks
- **User Story 3 (Phase 5)**: Depends on US1's `KnowledgeRepository` existing (T006) — genuinely
not implementable before US1, since retrieval queries the same table US1 creates
- **Polish (Phase 6)**: Depends on all three user stories
### Parallel Opportunities
- T004 alongside T006-T009 once T003 exists (unit test doesn't need the real implementation)
- T011/T012 (independent integration test files)
- T013/T015 (independent repositories)
- T019 alongside US3's later tasks
- T025/T026 in Polish
---
## Implementation Strategy
### MVP First (User Story 1 Only)
1. Setup + Foundational (T001-T003)
2. User Story 1 (T004-T010)
3. **STOP and VALIDATE**: Quickstart Scenarios 1-3 pass — knowledge entries can be authored,
published, and versioned correctly. Usable as a content-management surface even before
structured error codes/runbooks/retrieval exist.
### Incremental Delivery
1. Setup + Foundational → schema migrated
2. Add User Story 1 → knowledge entries work end to end (MVP)
3. Add User Story 2 → error codes, known issues, runbooks
4. Add User Story 3 → filtered retrieval, ready for the future AI-support feature to call
5. Polish → docs and full regression