docs: task breakdown for support organization feature

33 tasks across setup, foundational schema work, and four user stories
(teams/agents, skills/availability, the dynamic hierarchy tree, and a
capability-eligibility read path for the future orchestration phase).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
saqib mir
2026-09-02 17:53:30 +05:30
co-authored by Claude Sonnet 5
parent c18e203ad1
commit e703f826de
+271
View File
@@ -0,0 +1,271 @@
---
description: "Task list for 006-support-organization"
---
# Tasks: Support Organization
**Input**: Design documents from `specs/006-support-organization/`
**Prerequisites**: [plan.md](./plan.md), [spec.md](./spec.md), [research.md](./research.md),
[data-model.md](./data-model.md), [contracts/support-org-contract.md](./contracts/support-org-contract.md),
[quickstart.md](./quickstart.md)
**Tests**: Included as first-class tasks — this feature has two genuinely extractable pure
functions (cycle detection, capability-matching predicate) alongside the thin-Prisma-query CRUD
that dominated 004's test shape.
**Organization**: Tasks are grouped by user story (US1 = P1 teams/agents, US2 = P1 skills/
availability, US3 = P2 hierarchy tree, US4 = P3 capability-eligibility lookup).
## Format: `[ID] [P?] [Story] Description`
All file paths are relative to `supporthub-api/` (repo root).
---
## Phase 1: Setup
- [ ] T001 [P] Populate `src/modules/identity/teams/` with the standard module shape
(`controller/`, `routes/`, `schema/`, `repository/`, `service/`, `types/`, `mapper/`,
`constants/`, `index.ts`) — this directory currently has no files
- [ ] T002 [P] Replace `src/modules/identity/agents/`'s existing `User`-based stub content
(`repository/agents.repository.ts`, `service/agents.service.ts`,
`schema/agents.schema.ts`) — keep the module's file layout, replace what each file does
- [ ] T003 [P] Populate `src/modules/orchestration/hierarchy/` with the standard module shape —
this directory currently only has an empty `index.ts`
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Schema for all five entities, shared by every user story.
**⚠️ CRITICAL**: No user-story stage work can begin until this phase is complete.
- [ ] T004 Add `Team`, `Agent`, `AgentSkill`, `AgentAvailability`, `HierarchyNode` models to
`prisma/schema.prisma` per data-model.md (including `@@unique([agentId, skillTag])` on
`AgentSkill`, `agentId @unique` on `AgentAvailability`, the `HierarchyTree` self-relation
on `HierarchyNode`, and the `(teamId, active)`/`(parentId, order)`/`(active)` indexes)
(depends on T001-T003)
- [ ] T005 Run `npm run prisma:generate` and create the migration (`npm run prisma:migrate`) for
T004 (depends on T004)
**Checkpoint**: Schema migrated. User stories can now be built.
---
## Phase 3: User Story 1 - An admin builds out teams and agents (Priority: P1) 🎯 MVP
**Goal**: Team and agent CRUD with active/inactive state, never deletion; team deactivation
never cascades.
**Independent Test**: Quickstart Scenario 1.
### Tests for User Story 1
- [ ] T006 [US1] Integration test covering Quickstart Scenario 1 (create team/agent, agent
appears in roster, deactivate excludes from active listing without deleting, reactivate
restores it, team deactivation doesn't cascade) against a real Postgres in
`tests/integration/support-org-teams-agents.test.ts`
### Implementation for User Story 1
- [ ] T007 [P] [US1] Add `TeamsRepository`/`TeamsService` (create; update; findById with agent
roster; findAll) in `src/modules/identity/teams/repository/teams.repository.ts` +
`service/teams.service.ts` (depends on T005)
- [ ] T008 [US1] Add `AgentsRepository`/`AgentsService` (create; update; findById; findAll with
`active`/`teamId` filters) — replacing the existing `User`-based stub — in
`src/modules/identity/agents/repository/agents.repository.ts` +
`service/agents.service.ts` (depends on T005, T007 for the `teamId` FK)
- [ ] T009 [US1] Add Zod schemas + routes: `POST /admin/teams`, `PATCH /admin/teams/:teamId`,
`GET /admin/teams/:teamId`, `GET /admin/teams` (gated by `fastify.authenticate`) in
`identity/teams/schema/` + `routes/`, registered from `src/api/routes.ts` (depends on T007)
- [ ] T010 [US1] Add Zod schemas + routes: `POST /admin/teams/:teamId/agents`,
`PATCH /admin/agents/:agentId`, `GET /admin/agents/:agentId`, `GET /admin/agents` (gated by
`fastify.authenticate`) in `identity/agents/schema/` + `routes/`, registered from
`src/api/routes.ts` (depends on T008, T009)
- [ ] T011 [US1] Run Quickstart Scenario 1 locally and confirm all 5 steps pass
**Checkpoint**: Teams and agents can be created, updated, and deactivated/reactivated without
ever losing history. Usable as the foundation every later story needs.
---
## Phase 4: User Story 2 - An admin manages agent skills and availability (Priority: P1)
**Goal**: Skill tags (upsert-not-duplicate) and a single current availability record per agent.
**Independent Test**: Quickstart Scenario 2.
### Tests for User Story 2
- [ ] T012 [US2] Integration test covering Quickstart Scenario 2 (skill upsert doesn't
duplicate, availability upsert stays single-record, invalid status rejected) against a
real Postgres in `tests/integration/support-org-skills-availability.test.ts`
### Implementation for User Story 2
- [ ] T013 [P] [US2] Add `AgentSkillsRepository`/`AgentSkillsService` (`upsert(agentId,
skillTag, level)`; `findAllByAgent`) in
`src/modules/identity/agents/repository/agent-skills.repository.ts` +
`service/agent-skills.service.ts` (depends on T005)
- [ ] T014 [P] [US2] Add `AgentAvailabilityRepository`/`AgentAvailabilityService`
(`upsert(agentId, status, workingHours, currentLoad?)`; `findByAgent`) — `status` validated
against the fixed `available`/`busy`/`away`/`offline` set at the Zod schema layer (FR-008)
— in `src/modules/identity/agents/repository/agent-availability.repository.ts` +
`service/agent-availability.service.ts` (depends on T005)
- [ ] T015 [US2] Add Zod schemas + routes: `PUT /admin/agents/:agentId/skills/:skillTag`,
`GET /admin/agents/:agentId/skills`, `PUT /admin/agents/:agentId/availability`,
`GET /admin/agents/:agentId/availability` (gated by `fastify.authenticate`) in
`identity/agents/schema/` + `routes/` (depends on T013, T014)
- [ ] T016 [US2] Run Quickstart Scenario 2 locally and confirm all 5 steps pass
**Checkpoint**: Agents carry real, current skill and availability data — everything a future
capability/assignment lookup needs to read.
---
## Phase 5: User Story 3 - An admin configures the dynamic support hierarchy (Priority: P2)
**Goal**: A nestable, cycle-safe, orderable, audited hierarchy tree — every field stored as
opaque data for a future orchestration engine.
**Independent Test**: Quickstart Scenario 3.
### Tests for User Story 3
- [ ] T017 [P] [US3] Unit tests for the pure cycle-detection function — a direct self-reference
rejected, a transitive cycle (A's new parent is A's own descendant) rejected, a legitimate
reparent to an unrelated node accepted — in `tests/unit/support-org/cycle-check.test.ts`
- [ ] T018 [US3] Integration test covering Quickstart Scenario 3 (parent/child creation, order
preserved, nonexistent `parentId` rejected, self-reparent rejected, deactivation doesn't
cascade) against a real Postgres in `tests/integration/support-org-hierarchy.test.ts`
### Implementation for User Story 3
- [ ] T019 [P] [US3] Add the pure cycle-detection function `wouldCreateCycle(nodeId,
candidateParentId, ancestorChainOf: (id) => string[]) => boolean` in
`src/modules/orchestration/hierarchy/service/cycle-check.ts` (no dependencies — pure
function, takes the ancestor-lookup as an injected function so it's testable without a
database)
- [ ] T020 [US3] Add `HierarchyAuditLogRepository.writeHierarchyAuditEvent(...)` (research.md,
same shape as `catalog/products`' `writeIntegrationAuditEvent`) in
`src/modules/orchestration/hierarchy/repository/hierarchy-audit-log.repository.ts`
(depends on T005)
- [ ] T021 [US3] Add `HierarchyRepository` (create — validates `parentId` exists, `404` if not;
`updateById` — validates via T019 before applying a `parentId` change; `findById`;
`findChildren(parentId, ordered)`; `findAll(activeOnly?)`; `setActive`) +
`HierarchyService` (wraps the repository, calls T020 on every create/edit/activate/
deactivate — FR-017) in
`src/modules/orchestration/hierarchy/repository/hierarchy.repository.ts` +
`service/hierarchy.service.ts` (depends on T019, T020)
- [ ] T022 [US3] Add Zod schemas + routes: `POST /admin/hierarchy-nodes`,
`PUT /admin/hierarchy-nodes/:nodeId`, `PATCH .../activate`, `PATCH .../deactivate`,
`GET /admin/hierarchy-nodes/:nodeId`, `GET .../children`, `GET /admin/hierarchy-nodes`
(gated by `fastify.authenticate`) in `orchestration/hierarchy/schema/` + `routes/`,
registered from `src/api/routes.ts` (depends on T021)
- [ ] T023 [US3] Run Quickstart Scenario 3 locally and confirm all 6 steps pass
**Checkpoint**: The dynamic support hierarchy — this feature's core differentiator — is fully
configurable, cycle-safe, and audited.
---
## Phase 6: User Story 4 - A capability-eligibility lookup for a future caller to use (Priority: P3)
**Goal**: A read-only endpoint composing hierarchy scope with skill matching, deliberately
excluding availability.
**Independent Test**: Quickstart Scenario 4.
### Tests for User Story 4
- [ ] T024 [P] [US4] Unit tests for the pure capability-matching predicate (agent skill set is a
superset of the required set → eligible; missing one required skill → not eligible; empty
hierarchy scope matches any product/category/priority) in
`tests/unit/support-org/capability-match.test.ts`
- [ ] T025 [US4] Integration test covering Quickstart Scenario 4 (skill-only match; availability
never filters; inactive agent excluded; empty result on no match, never an error;
hierarchy-scope skill composition) against a real Postgres in
`tests/integration/support-org-capability-lookup.test.ts`
### Implementation for User Story 4
- [ ] T026 [P] [US4] Add the pure capability-matching predicate `isCapabilityEligible(agentSkills:
string[], requiredSkills: string[]) => boolean` in
`src/modules/orchestration/hierarchy/service/capability-match.ts` (no dependencies)
- [ ] T027 [US4] Add `CapabilityLookupService.findEligibleAgents(skills, productId?, categoryId?,
priorityId?)`: finds active `HierarchyNode`s whose scope arrays are empty-or-matching for
each given context value, unions their `skills` into the requested set, queries active
agents (active team) via T026's predicate applied to each candidate's `AgentSkill` tags —
in `src/modules/orchestration/hierarchy/service/capability-lookup.service.ts` (depends on
T026, and on `identity/agents`' repository being queryable — reuses its exported
repository rather than duplicating the query)
- [ ] T028 [US4] Add Zod schema + `GET /support-org/capability-eligibility` route (no
`fastify.authenticate` gate — research.md "Admin endpoint authentication") in
`orchestration/hierarchy/schema/` + `routes/`, registered from `src/api/routes.ts`
(depends on T027)
- [ ] T029 [US4] Run Quickstart Scenario 4 locally and confirm all 5 steps pass
**Checkpoint**: All four user stories work independently and together — a full support
organization (teams, agents, skills, availability, hierarchy) exists and is queryable by
capability, ready for Phase 7's orchestration engine to build on.
---
## Phase 7: Polish & Cross-Cutting Concerns
- [ ] T030 [P] Add a "Support Organization" section to `README.md` describing the admin CRUD,
the cycle-safety/audit guarantees on hierarchy nodes, and the capability-eligibility
contract (explicitly noting what it does *not* do — availability filtering, assignment)
- [ ] T031 [P] Update `specs/006-support-organization/checklists/requirements.md` Notes with any
implementation-time findings
- [ ] T032 Run `npx tsx scripts/check-architecture.ts` and `npm run lint`/`npm run typecheck`
- [ ] T033 Full regression: `npm run test:unit` (scoped to `tests/unit`) to confirm nothing broke
elsewhere, then the full integration suite against real Docker-provisioned infra
---
## 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-US4
- **User Story 2 (Phase 4)**: Depends on US1 (agents must exist to attach skills/availability to)
- **User Story 3 (Phase 5)**: Depends on Foundational and, loosely, on US1 (`teamId` FK) — no
dependency on US2
- **User Story 4 (Phase 6)**: Depends on US2 (skills to match on) and US3 (hierarchy nodes to
compose scope from) — genuinely the last story, unlike 004's US3 which only needed US1
- **Polish (Phase 7)**: Depends on all four user stories
### Parallel Opportunities
- T001/T002/T003 (independent scaffolding across three separate module directories)
- T007 (Teams) before T008 (Agents) strictly (FK), but both alongside T017/T019 (US3's pure
functions, independent of US1/US2 entirely) once T005 exists
- T013/T014 (independent repositories) in parallel
- T024/T026 (pure function + its test) independent of T017/T019
- T030/T031 in Polish
---
## Implementation Strategy
### MVP First (User Story 1 Only)
1. Setup + Foundational (T001-T005)
2. User Story 1 (T006-T011)
3. **STOP and VALIDATE**: Quickstart Scenario 1 passes — teams and agents exist with correct
active/inactive semantics. Usable as the foundation for every later phase even before skills,
availability, hierarchy, or capability lookup exist.
### Incremental Delivery
1. Setup + Foundational → schema migrated
2. Add User Story 1 → teams and agents exist (MVP)
3. Add User Story 2 → agents carry real skill/availability data
4. Add User Story 3 → the dynamic support hierarchy is fully configurable and audited
5. Add User Story 4 → a real capability-eligibility read path, ready for Phase 7
6. Polish → docs and full regression