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>
11 KiB
description
| description |
|---|
| Task list for 004-product-knowledge |
Tasks: Product Knowledge Management & Retrieval
Input: Design documents from specs/004-product-knowledge/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/knowledge-contract.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
- 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.
- T002 Add
KnowledgeEntry,ErrorCode,KnownIssue,Runbookmodels toprisma/schema.prismaperdata-model.md(includingisCurrentVersionand the(code, version)/(key, productId, version)compound unique constraints — NOT a bare unique oncode/key), plus the four new back-relations onProduct - T003 Run
npm run prisma:generateand 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. - 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
- 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);createNewVersionimplementing research.md's conditional-update-then-insert) insrc/modules/ai-support/knowledge/repository/knowledge.repository.ts(depends on T003) - T007 [US1] Add
KnowledgeServicewrapping the repository with the FR-002/FR-004 rules (new entries default tostatus: draft,version: 1; edits always go throughcreateNewVersion, never a raw update) insrc/modules/ai-support/knowledge/service/knowledge.service.ts(depends on T006) - 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) - 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 byfastify.authenticate— insrc/modules/ai-support/knowledge/routes/knowledge.routes.ts, registered fromsrc/api/routes.ts(depends on T007, T008) - 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
- 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 - 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
- T013 [P] [US2] Add
ErrorCodesRepository/ErrorCodesService(create; findByCode) insrc/modules/ai-support/knowledge/repository/error-codes.repository.ts+src/modules/ai-support/knowledge/service/error-codes.service.ts(depends on T003) - T014 [US2] Add
KnownIssuesRepository/KnownIssuesService(create; findByErrorCode, joining throughErrorCodesRepository's lookup) insrc/modules/ai-support/knowledge/repository/known-issues.repository.ts+src/modules/ai-support/knowledge/service/known-issues.service.ts(depends on T013) - T015 [P] [US2] Add
RunbooksRepository(create;findCurrentByKey— filtersisCurrentVersion: true, active: true, treating inactive the same as not-found per FR-010;createNewVersionreusing research.md's conditional-update-then-insert pattern;deactivate) insrc/modules/ai-support/knowledge/repository/runbooks.repository.ts(depends on T003) - T016 [US2] Add
RunbooksServiceinsrc/modules/ai-support/knowledge/service/runbooks.service.ts(depends on T015) - 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 byfastify.authenticate— added tosrc/modules/ai-support/knowledge/routes/knowledge.routes.ts(depends on T014, T016) - 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 Prismawhere/orderByclause, not an extractable pure function. Covered by T020's integration test. - 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
- T021 [US3] Add
KnowledgeRepository.retrieve(productId, feature?, categoryScope?)implementing research.md's exact filter/order (depends on T006) - T022 [US3] Add
KnowledgeService.retrieve(...)(depends on T021) - T023 [US3] Add route
GET /knowledge/retrieve(nofastify.authenticategate — 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) insrc/modules/ai-support/knowledge/routes/knowledge.routes.ts, registered fromsrc/api/routes.ts(depends on T022) - 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
- T025 [P] Add a "Product Knowledge" section to
README.mddescribing the admin CRUD, versioning mechanism, and retrieval contract - T026 [P] Update
specs/004-product-knowledge/checklists/requirements.mdNotes with any implementation-time findings - T027 Run
npx tsx scripts/check-architecture.tsandnpm run lint/npm run typecheck - T028 Full regression:
npm run test:unit(scoped totests/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
KnowledgeRepositoryexisting (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)
- Setup + Foundational (T001-T003)
- User Story 1 (T004-T010)
- 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
- Setup + Foundational → schema migrated
- Add User Story 1 → knowledge entries work end to end (MVP)
- Add User Story 2 → error codes, known issues, runbooks
- Add User Story 3 → filtered retrieval, ready for the future AI-support feature to call
- Polish → docs and full regression