Files
support_backend/specs/007-orchestration-assignment/plan.md
T
saqib mirandClaude Sonnet 5 846b9e8dca docs: plan and design artifacts for orchestration and assignment feature
Maps the feature onto the three existing orchestration/{routing,
assignments,orchestration} scaffold stubs. Key decisions: Assignment
refined as a version-row-per-period model (paired with a separate
append-only AssignmentHistory event log), round-robin concurrency
safety via atomic Redis INCR (reusing existing infra, not a new one),
LEAST_LOADED/SKILL_BASED tie-breaks falling back to that same cursor,
currentLoad read but never mutated by this feature, and the escalation
trigger reusing 005's existing domain-event bus rather than a new
notification path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 11:30:23 +05:30

8.9 KiB

Implementation Plan: Orchestration and Assignment

Branch: 007-orchestration-assignment | Date: 2026-09-02 | Spec: spec.md

Input: Feature specification from specs/007-orchestration-assignment/spec.md

Summary

Populate the existing orchestration/routing, orchestration/assignments, and orchestration/orchestration stub directories (all placeholders today — e.g. the round-robin strategy stub just returns the first candidate) with the real engine: on a ticket's transition to HUMAN_ESCALATION (via the existing domain-event bus 005-ai-support first put to use), routing resolves the applicable hierarchy node and eligible-agent set by calling 006-support-organization's capability-eligibility lookup directly; assignments runs the resolved (or manually-overridden) pluggable strategy — ROUND_ROBIN (concurrency-safe via atomic Redis INCR), LEAST_LOADED, SKILL_BASED, MANUAL, DIRECT — and persists both the current Assignment and its append-only AssignmentHistory; orchestration ties the two together and moves the ticket to IN_PROGRESS through 003-ticketing's existing state machine.

Technical Context

Language/Version: TypeScript 5.4 / Node.js 20+.

Primary Dependencies: Prisma (new models), Zod, ioredis (already a dependency — atomic INCR for round-robin, reusing the existing shared client). No new runtime dependency.

Storage: PostgreSQL via Prisma (new Assignment, AssignmentHistory models). Redis for the round-robin cursor (research.md) — no new infrastructure, reusing src/infrastructure/cache.

Testing: Vitest — unit tests for each pluggable strategy's pure selection logic (given an eligible set + context, which agent) and for the tie-break composition; integration/concurrency tests for the full escalation→assignment flow and, specifically, ROUND_ROBIN under genuinely concurrent requests (Constitution's Testing gate explicitly requires an assignment concurrency test category — the first time this codebase has a feature that actually needs one, since 003-ticketing's own concurrency guarantee was single-writer-race on ticket status, not a multi-way selection race).

Target Platform: Same Fastify modular monolith. Populates existing module directories: src/modules/orchestration/{routing,assignments,orchestration}/.

Project Type: Backend service — single project.

Performance Goals: Round-robin's Redis INCR must stay a single round trip per assignment — no read-modify-write race window. Not otherwise performance-sensitive.

Constraints: MUST run automatically on human escalation (FR-001); MUST evaluate capability before availability (FR-003, inherited from 006); MUST be concurrency-safe for ROUND_ROBIN (FR-006); MUST never lose assignment history (FR-009); MUST reuse 003's ticket state machine, not invent a new status (FR-014).

Scale/Scope: Three populated modules, five assignment strategies, one manual-assignment admin endpoint, two read endpoints. Explicitly excludes: SLA policy execution, rule-driven escalation, agent-deactivation reaction, workload lifecycle mutation (see spec.md Assumptions).

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle / Section Check Result
I. SaaS Is the Sole Identity & Access Authority Assignment references Agent/Ticket — both SupportHub's own domain (Principle I's own list names "routing/assignment" as SupportHub's authority). No SaaS identity touched. PASS
II. Configuration Over Hardcoding The strategy actually used per ticket is read from the hierarchy node's own assignmentStrategy field (006) — never hardcoded to one strategy; five pluggable implementations selected by that config value. PASS
III. Layered Architecture With Enforced Module Boundaries Three modules follow the standard shape (assignments also keeps its pre-existing engine/strategies/rules/calculators extension per doc 05 §8); routingorchestration/hierarchy (006) and assignmentsidentity/agents are one-directional, no cycle — orchestration depends on both routing and assignments, neither of which depends back on it. PASS
IV. AI Recommends, Deterministic Policy Decides Not applicable in the AI sense — but structurally the same shape: the strategy is deterministic policy, never an LLM call; this feature has no AI involvement at all. PASS — N/A
V. Evidence-Based Verification Not applicable — no resolution/verification concept in this feature. PASS — N/A
VI. Durable Audit & History AssignmentHistory is the durable, append-only record doc 07 explicitly requires for "assignment/reassignment" (its own audit list). PASS
VII. Concurrency-Safe, Durable Job Handling This is the principle's own named example ("two tickets assigned simultaneously must never double-assign or corrupt round-robin state") — directly implemented via atomic Redis INCR, verified under real concurrent load (research.md, quickstart Scenario 2). PASS
VIII. Problem and Ticket Are Separate, Related Entities Assignment references Ticket, not Problem — doesn't touch the distinction. PASS — N/A
Technology & Platform Constraints Prisma + Zod + existing ioredis client only, no new dependency. PASS

No violations requiring Complexity Tracking justification.

Post-Design Constitution Re-check

All gates above remain PASS after Phase 1 design. Worth calling out against Principle VII explicitly: this is the first feature in this codebase since 003-ticketing's optimistic- concurrency ticket-status guarantee to have a genuine multi-writer race condition as a first-class requirement (not just a theoretical one) — ROUND_ROBIN's atomic-INCR design and its dedicated concurrency test (tasks.md) are what make this principle a verified guarantee here, not an aspiration.

Project Structure

Documentation (this feature)

specs/007-orchestration-assignment/
├── plan.md              # This file
├── research.md          # Phase 0 output
├── data-model.md         # Phase 1 output
├── quickstart.md          # Phase 1 output
├── contracts/              # Phase 1 output
└── tasks.md                # Phase 2 output (/speckit-tasks — not created here)

Source Code (repository root)

supporthub-api/
├── prisma/
│   └── schema.prisma                        # MODIFIED — add Assignment, AssignmentHistory
├── src/
│   ├── infrastructure/
│   │   └── cache/                            # reused as-is — round-robin's INCR goes through
│   │                                            the existing redisClient, no new file needed
│   │                                            beyond the strategy implementation itself
│   └── modules/
│       └── orchestration/
│           ├── hierarchy/                    # existing (006) — untouched
│           ├── routing/                      # REPLACED stub — resolves node + eligible agents
│           │   └── service/ types/ index.ts    (no controller/routes — internal only)
│           ├── assignments/                  # REPLACED stub — keeps engine/strategies/rules/
│           │   ├── controller/ routes/ schema/  calculators, adds the standard shape around it
│           │   │   repository/ service/ types/
│           │   │   mapper/ constants/ index.ts
│           │   ├── strategies/                 # round-robin, least-loaded, skill-based,
│           │   │                                 manual/direct (thin — just records the given
│           │   │                                 agentId)
│           │   ├── engine/                     # ties a resolved eligible set + chosen strategy
│           │   │                                 together into a persisted Assignment
│           │   └── calculators/                 # workload/skill-level comparison helpers
│           └── orchestration/                # REPLACED stub — event subscriber + the
│               └── service/ types/ index.ts     escalation→assignment→IN_PROGRESS workflow
└── tests/
    ├── unit/orchestration/                   # each strategy's pure selection logic
    ├── integration/                          # full escalation→assignment flow, manual
    │                                            (re)assignment, history
    └── concurrency/                          # ROUND_ROBIN under real concurrent requests

Structure Decision: Single project. routing and orchestration are internal-only submodules (no HTTP surface), matching 005's troubleshooting/escalation precedent for modules whose job is pure orchestration logic invoked by another module's service, not a caller-facing endpoint of their own.

Complexity Tracking

No constitution violations — table intentionally omitted.