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:
saqib mir
2026-09-02 17:49:50 +05:30
co-authored by Claude Sonnet 5
parent 82d02bcdcd
commit e1983438b9
2 changed files with 333 additions and 0 deletions
@@ -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.
+283
View File
@@ -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.