Capability Preservation Checklist (Phase 0, Step 7)

Rule: Every externally observable capability at e4f3bcc must survive the rewrite (or have intentional behavior change documented in Phase 7 step 24). This checklist groups by route namespace and background job, per plan step 7. Each row is a concrete input → expected observable output, not just cargo test passing.


Public Routes (no auth)

PathMethodCapabilityExpected Output
/healthGETLiveness200 {"status":"ok"} (or "OK" text at /health simple)
/health/detailedGETComponent status200 with per-component latency/status (public Detailed since 2026-06-02 fix)
/auth/registerPOSTUser registration201 + User + JWT pair; 409 on duplicate email
/auth/loginPOSTLogin200 + JWT pair; 401 on bad password (test login_wrong_password_unauthorized is #[ignore] pre-existing)
/auth/refreshPOSTToken refresh200 + new access token; 401 on expired
/auth/logoutPOSTLogout (if present)200 + revocation
/agentsGETList public agents (community + system)200 array of AgentSummary
/webhooks/*POSTWebhook ingress (document_upload, trigger_engine)200 + trigger dispatch; 401 if WEBHOOK_SECRET mismatch (env lock semantics preserved)
/events/*SSE/POSTEvents fan-outSSE stream if enabled
/oauth/*GET/POSTOAuth credential flows (if enabled)Redirect / token exchange

Verification: curl -s localhost:3000/health200; curl -s localhost:3000/api/auth/login -d '{"email":"...","password":"..."}' → JWT.


Protected Routes (JWT Authorization: Bearer)

PathMethodCapabilityExpected Output
/chatPOSTAgent chat (non-stream)200 ChatResponse with usage populated, tool-call trace
/chat/streamGET/POST (SSE)Streaming chat200 text/event-stream SSE chunks, usage footer
/researchPOSTDeep research (coordinator)200 ResearchResponse
/memoryGET/POSTConversation memoryCRUD for conversations, messages
/workflowsPOST/GETWorkflow execution (router/orchestrator)200 workflow output serialization
/user/agentsGET/POSTUser-owned agents (tenant_agents table)200 filtered by tenant_id
/loopsPOST/GETLoop-mode agent lifecycle (LoopRegistry)201 + lifecycle status
/conversationsGET/POST/PATCH/DELETEConversation CRUD200 ConversationSummary/Details
/skillsPOST/GETSkill execution (SkillEngine)200 skill step trace (tool_call/llm_call/condition)
/rag/*POST/GETRAG: ingest, search, delete_collection, list_collections200 + rag_crate:chunking_strategy preserved
/deployPOSTDeploy registry (DeployRegistry)202 + background script updates registry

Verification: curl -s localhost:3000/api/chat -H 'Authorization: Bearer <jwt>' -H 'Content-Type: application/json' -d '{"message":"hello"}' → valid ChatResponse with usage; curl -N localhost:3000/api/chat/stream → SSE.


Admin Routes (X-Admin-Secret)

Admin is the largest surface — 190 KB admin.rs at e4f3bcc. Split by domain in Phase 6, but same paths/auth must survive.

DomainRepresentative PathsCapability
TenantsPOST/GET /api/admin/tenants, GET /api/admin/tenants/:idTenant CRUD, TenantTier mapping
API KeysPOST /api/admin/api-keys, DELETE /api/admin/api-keys/:idTenant API key issuance, argon2 hash store
UsageGET /api/admin/usage, GET /api/admin/daily-usageAggregated run_history/agent_runs costs
QuotasGET/PUT /api/admin/quotasPer-tenant daily/monthly quotas (tenant_model_tiers)
Agents / Versions / RollbackGET/POST /api/admin/agents, GET /api/admin/agents/:id/versions, POST /api/admin/agents/:id/rollback, POST /api/admin/agents/emergency-stopAgent CRUD, version history (agent_versions), emergency stop (AtomicBool)
TemplatesGET/POST /api/admin/templates4 fleet templates (tenant_agents::seed_default_templates)
ModelsGET /api/admin/models97 models from live NVIDIA catalog (NvidiaCatalogCache)
AlertsGET /api/admin/alerts, POST /api/admin/alerts/:id/ackalerts table Budget alerts
Audit LogGET /api/admin/audit-logMutation audit trail
Agent Runs / Feedback / StatsGET /api/admin/agent-runs, GET /api/admin/agent-runs/:id/feedback, GET /api/admin/statsrun_history.rs 15 endpoints, 6 tables
Emergency StopPOST /api/admin/agents/emergency-stopGlobal 503 flag
Runtime Providers/ToolsGET/POST /api/admin/runtime/providers, GET/POST /api/admin/runtime/tools, POST /api/admin/runtime/tools/:id/test, GET /api/admin/runtime/tools/:id/versionsruntime_providers (021), runtime_tools (015) + hot-reload reload()
Fleet SecretsGET/PUT /api/admin/fleet-secretsEncrypted provider configs (FleetSecrets + FleetProviderSecretsStore)
ConnectorsGET/POST /api/admin/connectorsskills_and_connectors (019) — slack/google/linkedin/salesforce/hubspot prebuilt
MCP ServersGET/POST /api/admin/mcp/serversMcpRegistry clients (rmcp)
BillingGET /api/admin/billing/*Cost aggregation per tenant/period
OAuthGET/POST /api/admin/oauth/*oauth_credentials store
Schedules / Triggers / PipelinesGET/POST /api/admin/schedules, GET/POST /api/admin/triggers, GET/POST /api/admin/pipelinesCron schedules, event triggers, pipelines (020)
AllowlistsGET/PUT /api/admin/allowliststenant_allowlist
Token BudgetsGET/PUT /api/admin/token-budgetstoken_budgets
Model TiersGET/PUT /api/admin/model-tiersPer-tenant tier→model mapping (017)
Health MetricsGET /api/admin/health-metricsHourly aggregation (health_metrics_job)

Verification: Admin CRUD: create tenant → create API key → create runtime tool → create agent → trigger via /v1/chat with tenant key → verify isolation (tenant A cannot see tenant B's tools). See Phase 7 E2E (step 24) row 4.


v1 Routes (API key X-API-Key or Authorization: Bearer <api_key>)

PathMethodCapabilityExpected Output
/v1/chatPOSTTenant-scoped chat (v1 tenant agent runtime)200 ChatResponse via v1_tenant_agent_runtime_tests path
/v1/streamPOST (SSE)Tenant-scoped streaming200 SSE
/v1/agentsGETList tenant-available agents (resolver)200 filtered by Tier + allowlist + allowed_tools
/v1/* (extensions)POST/GETTenant product extensions via ares-server base_router extension pattern (client plugins call /v1/* APIs, no client code in ARES)200 per extension spec

Verification: curl -s localhost:3000/v1/chat -H 'X-API-Key: <tenant_key>' -d '{"message":"test"}' → tenant-resolved agent + ToolService::list scoped.


Background Jobs & Engines

JobTable / SourceTriggerObservable Proof
Scheduler (src/scheduler.rs 28.5 KB, 60s tick, catch-up pass)agent_schedules + missed_runsCron evaluation next_run_at, tokio::spawn every 60sInsert row with next_run_at in past → wait 70s → assert agent_runs row appears
Pipeline Engine (src/pipeline_engine.rs)agent_pipelinesConditional evaluation after upstream executionPipeline target agent_runs origin: scheduled preserved
Trigger Engine (src/trigger_engine.rs)webhook / document-upload / field-changePOST /webhooks/* or DB triggeragent_runs origin: trigger with trigger_id not pipeline_id
Skill Engine (src/skill_engine.rs 34 KB, depth limiting)skills + connectorsSequential ToolCall/LlmCall/SkillCall/ConditionReal tool calls + LLM calls in skill steps (R50-5 wired)
Workflow Engine (src/workflows/engine.rs, router/orchestrator)TOON workflows dirPOST /workflowsRouter vs orchestrator branch, fallback handling
Health Metrics Job (src/health_metrics_job.rs hourly)health_metricsHourly aggregationGET /api/admin/health-metrics shows hourly rows
Nvidia Catalog Refresh (crates/ares-config/src/nvidia_catalog.rs, catalog.start_background_refresh())build.nvidia.com/modelsPeriodic refresh (default interval)GET /api/admin/models returns ~97 models
Runtime Tool/ Provider Hot-Reload (runtime_registry.rs, provider_registry.rs)Postgres runtime_tools / runtime_providersWas 60s ArcSwap poll → Phase 3 epoch notifyMutate runtime_tools DB row → without restart ToolService::list reflects change (no 60s stale window)
Agent Version Snapshot (src/main.rs startup snapshot + hot-reload mpsc::unbounded_channel)config/agents/*.toonTOON file change + DynamicConfigManager::start_watchingModify config/agents/test.toonagent_versions row appears via hot_reload task

Cross-Cutting Invariants (must hold after redesign)

InvariantProof
Multi-tenant isolationTwo tenants with disjoint runtime_tools/runtime_providersToolService::list(tenant) shows only tenant's own; LlmService tier mapping isolated; ApiKeyAuth middleware enforces tenant_id
Hot-reload without restartFile + DB mutation → epoch-driven Fiber::refresh (not 60s poll); verify via config/entries.json reconciliation + ReflectService::notify(TypeId::of::<RuntimeToolService>()) BFS
StreamingGET /api/chat/stream + POST /v1/stream still produce SSE (text/event-stream); async-stream + tokio::sync::broadcast fan-out preserved
Fallback chainsProviderOverride.fallback_providers retry on retryable errors (R50-2) → coordinator retry observable via run_history.llm_calls + cost hooks
Cost/Usage/Token budgetsPOST /api/chat populates usage header → track_usage middleware → run_history + agent_runs + token_budgets enforcement
Per-agent tool assignmentAgentConfig.allowed_tools filter (R50-1) → TenantToolAllowed / TenantModelAllowed checks
MCP bridgeMcpRegistryToolRegistry bridge still exposes MCP tools as agent-callable; MCP server direct AgentExecutionService path is intentional improvement (latency) not regression
Axum route param syntax:param (matchit 0.7) stays :id not {id} until Axum 0.8 upgrade — grep src/api/routes.rs if upgraded
Config symlinkares.toml remains symlink to /opt/ares-config/ares.toml on VPS; Loader state in config/entries.json / config/cordis-entries.toon must not conflict
ARES stays genericZero client-specific routes/tables/logic — client needs are plugins in client's repo calling /v1/*

Intentional Behavior Changes (documented, not regressions)

ChangeJustification
MCP server calls AgentExecutionService directly instead of HTTP reqwest loopbackLatency improvement, eliminates loopback failure mode; observable: same ChatResponse but faster, no localhost:3000 hop in traces
60s poll → epoch notify (watch channel + Postgres NOTIFY/LISTEN)Eliminates stale window, reduces DB load; observable: runtime_tools change visible immediately, not up to 60s later
17-step run_server → 5–8 plugin callsSimplification, same services initialized; observable: startup logs show same component counts
sequential orchestratorJoinSet parallelThroughput improvement; sequential semantics preserved via Dispatch::Serial where order matters

Verification Matrix Hook (Phase 7, Step 22)

For each row above, Phase 7 (steps 22–24) runs:

  1. curl -s localhost:3000/health200
  2. curl -s localhost:3000/api/chat -H 'Authorization: Bearer <jwt>' -d '{"message":"hello"}' → valid ChatResponse with usage
  3. curl -N localhost:3000/api/chat/stream → SSE
  4. Admin CRUD isolation chain (create tenant → api key → runtime tool → agent → /v1/chat)
  5. Scheduler: insert past next_run_at → 70s → agent_runs row
  6. Hot-reload: modify TOON/DB → assert change without restart
  7. Multi-tenant isolation: disjoint tools → ToolService::list invisibility

Plus cargo check matrix (with and without postgres, with full) and rust-doctor --scope baseline --base main gate (score ≥ baseline projected, worst_tier no regress).