docs: spec for support organization feature (006)
Phase 6 of the roadmap: Team/Agent/AgentSkill/AgentAvailability models, dynamic HierarchyNode configuration, and a capability-eligibility read path for the future orchestration phase to call. Supersedes the pre-existing identity/agents scaffold stub (which queried a generic User/UserRole model unrelated to this system's real architecture); leaves identity/customers and identity/auth untouched as out of scope. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
82d02bcdcd
commit
e1983438b9
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user