diff --git a/specs/006-support-organization/checklists/requirements.md b/specs/006-support-organization/checklists/requirements.md new file mode 100644 index 0000000..15fb305 --- /dev/null +++ b/specs/006-support-organization/checklists/requirements.md @@ -0,0 +1,50 @@ +# Specification Quality Checklist: Support Organization + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-09-02 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- Scope is Phase 6 per `docs/10-implementation-roadmap.md`: `Team`/`Agent`/`AgentSkill`/ + `AgentAvailability` models, dynamic `HierarchyNode` configuration, and the admin API surface a + future hierarchy editor UI would call — explicitly not the orchestration engine, assignment + strategies, or SLA/escalation policy models themselves (Phase 7/8). +- **A real, pre-existing inconsistency was found and resolved at spec time, not discovered + later**: `src/modules/identity/agents` and `src/modules/identity/customers` already existed as + thin scaffold stubs querying a generic `User`/`UserRole` model left over from the very original + project scaffold — predating 002-saas-integration's `CustomerReference` (the actual customer- + identity mechanism this system uses) and doc 06's real `Agent` model. This feature supersedes + the `identity/agents` stub with a real implementation and explicitly leaves + `identity/customers` untouched (out of scope — customers are not this feature's concern, and + `CustomerReference` already answers that need in the real architecture). +- User Story 4 (capability-eligibility lookup) exists specifically as the read contract a future + Phase 7 (orchestration/assignment) will call, mirroring 004-product-knowledge's + `/knowledge/retrieve` being built for 005-ai-support to call rather than that later feature + reaching into 004's internals. +- All items pass; no revision iterations were needed. diff --git a/specs/006-support-organization/spec.md b/specs/006-support-organization/spec.md new file mode 100644 index 0000000..e47787b --- /dev/null +++ b/specs/006-support-organization/spec.md @@ -0,0 +1,283 @@ +# Feature Specification: Support Organization + +**Feature Branch**: `006-support-organization` + +**Created**: 2026-09-02 + +**Status**: Draft + +**Input**: User description: "Phase 6 of docs/10-implementation-roadmap.md: Team/Agent/ +AgentSkill/AgentAvailability models, dynamic HierarchyNode configuration, admin hierarchy +editor. Per docs/05-orchestration-sla-escalation.md §2-3 and docs/06-database-schema.md +'Domain: Support Hierarchy / Teams / Agents'." + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - An admin builds out teams and agents (Priority: P1) + +An administrator creates a support team, then creates agents and assigns each to a team. An +agent can be deactivated (e.g., they leave) without losing their history, and reactivated later. + +**Why this priority**: Every later capability in this feature — skills, availability, hierarchy +nodes that reference a team — and every later phase (orchestration, assignment) needs teams and +agents to already exist. There is nothing to configure or query without this. + +**Independent Test**: Create a team; create an agent assigned to it; confirm the agent appears +under that team; deactivate the agent; confirm it's excluded from an active-agents listing but +still resolvable by id. + +**Acceptance Scenarios**: + +1. **Given** an admin creates a team, **When** it's saved, **Then** it exists with `active: true` + and no agents yet. +2. **Given** an existing team, **When** an admin creates an agent assigned to it, **Then** the + agent is retrievable both directly and as part of that team's roster. +3. **Given** an active agent, **When** an admin deactivates them, **Then** they're excluded from + any "active agents" listing but their record (and history — assignments, skills) is not + deleted. +4. **Given** a deactivated agent, **When** an admin reactivates them, **Then** they reappear in + active listings. +5. **Given** an admin deactivates a team, **When** it's saved, **Then** the team's own active + agents are unaffected — team deactivation does not cascade into deactivating its agents. + +--- + +### User Story 2 - An admin manages agent skills and availability (Priority: P1) + +An administrator tags an agent with capability skills (each with a proficiency level), and an +agent's current availability — status, working hours, and current workload — is tracked as a +single, current, updatable record per agent. + +**Why this priority**: Skills and availability are what make an agent selectable at all — a +future orchestration/assignment phase (Phase 7) is fundamentally "which agents have this skill +and are currently available," so this data must be real and current before that phase, or any +capability-matching read path in this one (User Story 4), means anything. + +**Independent Test**: Tag an agent with a skill and a proficiency level; confirm it's listed on +the agent. Set the agent's availability status to `busy`; confirm a query for that agent reflects +it immediately. Update the same agent's availability again; confirm the prior value is replaced, +not duplicated. + +**Acceptance Scenarios**: + +1. **Given** an agent, **When** an admin adds a skill tag with a proficiency level, **Then** the + skill appears on the agent's record, and an agent can hold more than one skill tag at once. +2. **Given** an agent already has a skill tag, **When** an admin updates its proficiency level, + **Then** the level changes without creating a duplicate skill entry for the same tag. +3. **Given** an agent with no availability record yet, **When** an admin sets one, **Then** a + single current record exists with a status, working hours, and a current-load counter starting + at zero. +4. **Given** an agent's existing availability record, **When** its status is updated, **Then** + the change is immediately reflected — never a second, competing record for the same agent. +5. **Given** an availability status, **When** it's set, **Then** it MUST be one of a fixed, + validated set of values (`available`, `busy`, `away`, `offline`) — an arbitrary string is + rejected. + +--- + +### User Story 3 - An admin configures the dynamic support hierarchy (Priority: P2) + +An administrator builds an arbitrary tree of support hierarchy nodes for routing — each node +scoped to some combination of product/category/priority, tagged with the skills it covers, +pointing at a team, and carrying the assignment-strategy/SLA-policy/escalation-policy references +a future orchestration phase will read. Nodes can be nested under a parent, ordered relative to +their siblings, and deactivated without being deleted. + +**Why this priority**: This is the product's explicit differentiator (doc 01: "the customer never +needs to understand the internal support hierarchy... changing one must never require a code +change") — but it's meaningful only once teams exist (User Story 1), so it can't be P1 itself. + +**Independent Test**: Create a root hierarchy node and a child node under it; confirm the child +resolves its parent correctly and both are ordered as authored. Deactivate a node; confirm it's +excluded from an active-tree listing but still exists. Attempt to create a node whose `parentId` +doesn't exist; confirm it's rejected. + +**Acceptance Scenarios**: + +1. **Given** an admin creates a hierarchy node with no `parentId`, **When** it's saved, **Then** + it exists as a root node. +2. **Given** an existing node, **When** an admin creates another node with `parentId` set to it, + **Then** the new node is retrievable as a child of the first, and the tree can be read back in + full from any root. +3. **Given** an admin supplies a `parentId` that doesn't correspond to any existing node, **When** + the node is created, **Then** the request is rejected — a hierarchy can never reference a + parent that doesn't exist. +4. **Given** a node, **When** an admin sets its `productScope`/`categoryScope`/`priorityScope`/ + `skills`/`assignmentStrategy`/`slaPolicyId`/`escalationPolicyId`/`entryConditions`/ + `exitConditions`, **Then** every value is stored and returned exactly as given — none of it is + interpreted or enforced by this feature itself (that's the future orchestration engine's job). +5. **Given** an active node, **When** an admin deactivates it, **Then** it's excluded from a + query for the active tree but still exists and its children are unaffected (a deactivated + parent does not implicitly deactivate its children). +6. **Given** two sibling nodes under the same parent, **When** an admin sets their `order` values, + **Then** reading the parent's children back returns them in that exact order. + +--- + +### User Story 4 - A capability-eligibility lookup for a future caller to use (Priority: P3) + +Given a required skill (or set of skills) and optionally a product/category/priority context, a +lookup returns the active agents who are eligible on capability grounds alone — team membership, +skill match, active state. It deliberately does not consider availability, working hours, current +load, or perform any assignment decision. + +**Why this priority**: This is the read contract the future orchestration/assignment phase +(Phase 7) will call — analogous to how 004-product-knowledge built `/knowledge/retrieve` for the +future AI-support feature to call rather than that feature reaching into 004's internals. It +depends on User Stories 1-2 (agents/skills/teams must exist to be queried) and, for +product/category/priority-scoped lookups, on User Story 3's hierarchy nodes. + +**Independent Test**: Tag two agents with different skills; query eligibility for one skill; +confirm only the matching agent is returned. Deactivate that agent; confirm the query now returns +nothing. Query with a skill no agent has; confirm an empty result, never an error. + +**Acceptance Scenarios**: + +1. **Given** agents with different skill tags, **When** a capability lookup is run for a specific + skill, **Then** only agents holding that skill are returned. +2. **Given** doc 05 §3's explicit ordering ("capability is evaluated before availability"), + **When** the lookup runs, **Then** its result is never filtered or reordered by availability, + working hours, or current load — a `busy` or `offline` agent who has the skill is still + included; that filtering is explicitly out of scope for this lookup. +3. **Given** an inactive agent who otherwise matches, **When** the lookup runs, **Then** they are + excluded — capability matching never surfaces a deactivated agent. +4. **Given** a skill no active agent currently holds, **When** the lookup runs, **Then** it + returns an empty result, never an error. +5. **Given** a lookup scoped to a product/category/priority, **When** hierarchy nodes exist whose + scope matches, **Then** the matching node(s)' own `skills` are included as additional + eligibility criteria — the lookup composes hierarchy scoping with direct skill matching rather + than requiring the caller to pre-resolve which node applies. + +--- + +### Edge Cases + +- What happens if an admin tries to delete a team or agent that has assignment/ticket history + (once that history exists in a later phase)? Out of scope for strict enforcement here — this + feature does not implement hard deletion of teams, agents, or hierarchy nodes at all, only + active/inactive state, consistent with this system's broader convention of never silently + losing support-domain history. +- What happens if a hierarchy node's `parentId` is later changed to create a cycle (a node + becomes its own ancestor)? Rejected — a hierarchy MUST remain a tree, never a cycle. +- What happens when an agent is reassigned from one team to another? Supported as a normal update + to the agent's `teamId` — the agent's skills and availability record are unaffected by a team + change. +- What happens if two admins race to set the same agent's availability at the same moment? The + later write wins (last-write-wins) — availability is frequently-changing operational state, not + the kind of durable business record this system's optimistic-concurrency convention (ticket + status, knowledge versioning) is built for; see Assumptions. +- What happens when a capability lookup (User Story 4) is scoped to a product/category/priority + that no hierarchy node covers? It falls back to a plain skill-only match across all active + agents — an unconfigured scope is "match on skill alone," not "match nothing." + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The system MUST let an admin create a team, defaulting to `active: true`. +- **FR-002**: The system MUST let an admin create an agent assigned to a team, defaulting to + `active: true`. +- **FR-003**: Deactivating an agent MUST exclude them from any active-agent listing without + deleting their record or any of their skill/availability data. +- **FR-004**: Deactivating a team MUST NOT cascade into deactivating its agents. +- **FR-005**: The system MUST let an admin add a skill tag with a proficiency level to an agent, + and MUST support an agent holding multiple distinct skill tags at once. +- **FR-006**: Updating an existing skill tag's proficiency level for an agent MUST replace the + level, never create a duplicate entry for the same agent+tag pair. +- **FR-007**: The system MUST let an admin set an agent's availability (status, working hours, + current load), maintaining exactly one current record per agent — never more than one. +- **FR-008**: An availability `status` MUST be validated against a fixed set of values + (`available`, `busy`, `away`, `offline`); any other value MUST be rejected. +- **FR-009**: The system MUST let an admin create a hierarchy node, optionally under a parent + node; creating a node with a `parentId` that does not reference an existing node MUST be + rejected. +- **FR-010**: A hierarchy node MUST store `name`, `order`, an optional `teamId`, `skills`, + `productScope`, `categoryScope`, `priorityScope`, `assignmentStrategy`, optional + `slaPolicyId`/`escalationPolicyId`, and optional `entryConditions`/`exitConditions`, returning + every value exactly as given — this feature does not interpret, execute, or validate the + semantics of `assignmentStrategy`/`entryConditions`/`exitConditions` beyond storing them. +- **FR-011**: The system MUST reject a hierarchy node update that would introduce a cycle (a node + set as its own ancestor, directly or transitively). +- **FR-012**: Deactivating a hierarchy node MUST exclude it from an active-tree query without + deleting it or affecting its children's own active state. +- **FR-013**: Sibling hierarchy nodes under the same parent MUST be returned in their authored + `order` when the parent's children are read. +- **FR-014**: The system MUST provide a capability-eligibility lookup that, given a required + skill (or skills) and an optional product/category/priority context, returns active agents + whose team is active and who hold a matching skill — composing a matching hierarchy node's own + `skills` into the criteria when the context resolves to one. +- **FR-015**: The capability-eligibility lookup MUST NOT filter or reorder its result by + availability status, working hours, or current load (doc 05 §3's explicit capability-before- + availability ordering) — that consideration belongs to the future orchestration/assignment + phase, not this lookup. +- **FR-016**: The capability-eligibility lookup MUST exclude inactive agents and MUST return an + empty result, never an error, when nothing matches. +- **FR-017**: Every hierarchy node creation, edit, and active-state change MUST be written to the + durable audit log (Constitution Principle VI; doc 07 explicitly lists "hierarchy changes" as an + audited action). + +### Key Entities + +- **Team**: A named grouping of agents, with an active/inactive state independent of its + members' own active states. +- **Agent**: A support staff member belonging to a team, with an active/inactive state, a set of + skill tags, and at most one current availability record. +- **Agent Skill**: A capability tag held by an agent, with a proficiency level used by future + skill-based assignment logic — not enforced or interpreted by this feature. +- **Agent Availability**: An agent's current operational state — status, working hours, and + current workload — exactly one record per agent, always representing the latest known value. +- **Hierarchy Node**: A configurable, nestable support-routing unit — scoped to product/category/ + priority, tagged with skills, pointing at a team and at (not-yet-built) SLA/escalation policy + references, with entry/exit conditions and an assignment-strategy reference — all of it stored + as data for a future orchestration engine to interpret, never hardcoded logic in this feature. + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: 100% of deactivated agents are absent from every active-agent listing, while + remaining individually retrievable by id. +- **SC-002**: An agent's availability is always represented by exactly one record — never zero + after being set once, never more than one after any number of updates. +- **SC-003**: 100% of hierarchy node creation attempts with a nonexistent `parentId` are rejected, + never silently creating an orphaned or root node instead. +- **SC-004**: 100% of hierarchy trees remain acyclic — no update can produce a node that is its + own ancestor. +- **SC-005**: A capability-eligibility lookup for a skill no active agent holds returns an empty + result in 100% of cases, never an error. +- **SC-006**: Every hierarchy node change (create, edit, activate/deactivate) produces exactly one + corresponding audit log entry. + +## Assumptions + +- **Availability updates use last-write-wins, not optimistic concurrency** — unlike ticket status + or knowledge versioning (this system's durable-record convention), an agent's availability is + frequently-changing operational telemetry, not a business record whose history matters; adding + `expectedVersion` here would add friction without protecting anything meaningful. If a future + phase finds contention here a real problem (e.g., automated availability updates racing an + agent's own manual one), that's a decision for that phase to revisit, not this one. +- **This feature does not build the orchestration engine, assignment strategies, or SLA/ + escalation policy models** — `assignmentStrategy`, `slaPolicyId`, and `escalationPolicyId` on a + hierarchy node are stored exactly as given (free-text/reference values) with no validation + against a real policy table, because those tables don't exist until Phase 7/8. This mirrors how + 004-product-knowledge stored `errorCode` as free text pending 004's own later `ErrorCode` model + in the same feature — here, the referenced tables are a genuinely later phase, not later in the + same feature, so the reference fields stay untyped/unvalidated for now. +- **The capability-eligibility lookup (User Story 4) is a read contract for a future caller, not + the orchestration engine itself** — it answers "who is capability-eligible," never "who should + be assigned." Availability, workload, and strategy selection are explicitly out of scope, per + doc 05 §3's own ordering (capability before availability) and doc 05 §8 (orchestration needs + its own `engine/`/`rules/`/`strategies/` internal structure — not appropriate to build + speculatively inside this CRUD-shaped feature). +- **`identity/auth` remains a stub, unchanged by this feature** — every admin endpoint here is + gated by the same `fastify.authenticate` known-limitation stub as every prior feature's admin + surface; this feature does not attempt real agent authentication or a login flow (that's + Phase 10's Agent/Admin UI territory, or whenever `identity/auth` itself becomes its own + feature). +- **The pre-existing `identity/customers`/`identity/agents` scaffold stubs (querying the generic + `User`/`UserRole` model from the original project scaffold) are superseded, not extended** — a + real `Agent` is its own first-class entity per doc 06, unrelated to the generic `User` model + (which nothing in this system's real architecture, starting from 002-saas-integration's + `CustomerReference`, actually uses). `identity/customers` and its `User`-based stub are left + untouched (out of scope — this feature is about agents/teams/hierarchy, not customers); + `identity/agents`' stub is replaced with a real implementation as part of this feature.