|
1 | 1 | # Acceptance Coverage |
2 | 2 |
|
3 | | -TeaAgent acceptance tests live under `tests/acceptance/` and verify user-facing workflows rather than isolated primitives. |
4 | | - |
5 | | -## Covered |
6 | | - |
7 | | -- Daily CLI read-only workflow: `agent preflight`, `agent run`, `agent show`, audit persistence, and run-level audit summary. |
8 | | -- CLI prompt approval workflow: destructive call pauses, `agent resume` auto-approves the pending call id, writes the file, and reports audit summary. |
9 | | -- Daily TUI workflow: chat mode, memory injection, progress streaming, agent answer persistence in session history. |
10 | | -- TUI prompt approval workflow: approval prompt, destructive write, final run payload, and audit summary. |
11 | | -- MCP client compatibility flow: bearer auth, session lifecycle, `tools/list`, `tools/call`, and session close. |
12 | | -- A2A federation flow: remote discovery, partial endpoint failure, capability routing, delegation, context forwarding, and agent trace metadata. |
13 | | -- Managed runtime flow: tool metadata context construction, workspace/request context forwarding, persisted managed task audit events, and result trace metadata. |
14 | | -- Long-running worker flow: background worker start, list, show, log tail, and stop lifecycle. |
15 | | -- Workspace edit flow: hash-anchored read/edit, git status, test command execution, git diff inspection, and final diff summary. |
16 | | -- Live provider conformance flow: provider checks are skipped unless an explicit environment gate is set, preventing accidental live API calls in CI. |
17 | | -- Hosted-provider smoke flow: `model smoke --live-env-var` skips live adapter calls unless CI explicitly sets the gate to `1`. |
| 3 | +TeaAgent acceptance tests live under `tests/acceptance/` and verify user-facing |
| 4 | +workflows rather than isolated primitives. Integration tests live under |
| 5 | +`tests/integration/` and verify cross-component interactions. |
| 6 | + |
| 7 | +Run acceptance tests: |
| 8 | + |
| 9 | +```bash |
| 10 | +python3 -m pytest tests/acceptance |
| 11 | +``` |
| 12 | + |
| 13 | +Run integration tests: |
| 14 | + |
| 15 | +```bash |
| 16 | +python3 -m pytest tests/integration |
| 17 | +``` |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## Acceptance Flows (tests/acceptance/) |
| 22 | + |
| 23 | +### Original Flows |
| 24 | + |
| 25 | +| File | Story | Key assertions | |
| 26 | +|---|---|---| |
| 27 | +| `test_daily_cli.py` | Daily CLI read-only workflow | `agent preflight`, `agent run`, `agent show`, audit persistence, run-level audit summary | |
| 28 | +| `test_daily_tui.py` | Daily TUI workflow | chat mode, memory injection, progress streaming, agent answer persistence in session history | |
| 29 | +| `test_mcp_client_flow.py` | MCP client compatibility | bearer auth, session lifecycle, `tools/list`, `tools/call`, session close | |
| 30 | +| `test_a2a_federation_flow.py` | A2A federation | remote discovery, partial endpoint failure, capability routing, delegation, context forwarding, agent trace metadata | |
| 31 | +| `test_managed_runtime_flow.py` | Managed runtime | tool metadata context construction, workspace/request context forwarding, persisted managed task audit events, result trace metadata | |
| 32 | +| `test_ultrawork_flow.py` | Long-running worker | background worker start, list, show, log tail, and stop lifecycle | |
| 33 | +| `test_workspace_edit_flow.py` | Workspace edit | hash-anchored read/edit, git status, test command execution, git diff inspection, final diff summary | |
| 34 | +| `test_live_provider_conformance_flow.py` | Live provider conformance | provider checks skipped unless explicit env gate set, preventing accidental live API calls in CI | |
| 35 | +| `test_model_smoke_gating_flow.py` | Hosted-provider smoke | `model smoke --live-env-var` skips live adapter calls unless CI explicitly sets the gate | |
| 36 | + |
| 37 | +### New Flows — Added in this Sprint |
| 38 | + |
| 39 | +#### Cost & Token Tracking |
| 40 | + |
| 41 | +**File:** `test_cost_tracking_flow.py` |
| 42 | + |
| 43 | +**Story (AC-NEW-7):** *As a user, I want to see live token usage and estimated |
| 44 | +cost after a run so that I can track spending and tune my budget limits.* |
| 45 | + |
| 46 | +| Assertion | Details | |
| 47 | +|---|---| |
| 48 | +| `RunResult.cost_cents >= 0.0` | Present on all terminal statuses | |
| 49 | +| `RunResult.input_tokens` accumulated | Summed across all LLM calls in the run | |
| 50 | +| `RunResult.output_tokens` accumulated | Summed across all LLM calls in the run | |
| 51 | +| `run_completed` audit event carries cost fields | `cost_cents`, `input_tokens`, `output_tokens` | |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +#### Graceful Cancel |
| 56 | + |
| 57 | +**File:** `test_cancel_flow.py` |
| 58 | + |
| 59 | +**Story (AC-NEW-5):** *As a developer, I want to interrupt a running agent using |
| 60 | +a cancel token so that long-running tasks can be stopped cleanly without |
| 61 | +corrupting state.* |
| 62 | + |
| 63 | +| Assertion | Details | |
| 64 | +|---|---| |
| 65 | +| `cancel_token.set()` stops the run | Status is `failed:system` | |
| 66 | +| `run_started` still in audit log | No corruption on cancel | |
| 67 | +| Cancel token set from a different thread | Thread-safe via `threading.Event` | |
| 68 | +| Un-set cancel token runs normally | No performance penalty when not cancelled | |
| 69 | +| `ChatAgentConfig.cancel_token` wired end-to-end | Flows from config → runner | |
| 70 | + |
| 71 | +--- |
| 72 | + |
| 73 | +#### Remote MCP Tool Consumption |
| 74 | + |
| 75 | +**File:** `test_remote_mcp_consumption_flow.py` |
| 76 | + |
| 77 | +**Story (AC-NEW-9):** *As a platform engineer, I want to connect TeaAgent to a |
| 78 | +remote MCP server and have its tools appear in the tool registry, with approval |
| 79 | +rules applied uniformly.* |
| 80 | + |
| 81 | +| Assertion | Details | |
| 82 | +|---|---| |
| 83 | +| All remote tools registered | Names match MCP `tools/list` response | |
| 84 | +| Annotations inferred from MCP hints | `readOnlyHint`, `destructiveHint` | |
| 85 | +| `name_prefix` filter works | Only matching tools are registered | |
| 86 | +| `ToolRateLimit` applied uniformly | All remote tools share the same quota | |
| 87 | +| Remote tool callable via `ToolRegistry.execute` | Proxied through `MCPHTTPClient` | |
| 88 | + |
| 89 | +--- |
| 90 | + |
| 91 | +#### Skill Discovery and Injection |
| 92 | + |
| 93 | +**File:** `test_skill_install_flow.py` |
| 94 | + |
| 95 | +**Story (AC-NEW-10):** *As a platform engineer, I want to install skills into |
| 96 | +`.opencode/skill/<name>/SKILL.md` and have them automatically injected into the |
| 97 | +agent system prompt.* |
| 98 | + |
| 99 | +| Assertion | Details | |
| 100 | +|---|---| |
| 101 | +| Skill content in system prompt | After `run_chat_agent` with skill installed | |
| 102 | +| Multiple skills all present | All discovered skills injected | |
| 103 | +| No `Skills:` section when empty | Clean prompt when no skills installed | |
| 104 | +| Project skill overrides user skill | First-wins deduplication by name | |
| 105 | +| Skill reaches `ModelDecisionEngine` system prompt | End-to-end via stub adapter assertion | |
| 106 | + |
| 107 | +--- |
| 108 | + |
| 109 | +#### Policy-as-Code Deny Rules |
| 110 | + |
| 111 | +**File:** `test_policy_as_code_flow.py` |
| 112 | + |
| 113 | +**Story (AC-NEW-14):** *As a security lead, I want a `policy.yaml` deny-rule |
| 114 | +that blocks specific tool calls regardless of permission mode.* |
| 115 | + |
| 116 | +| Assertion | Details | |
| 117 | +|---|---| |
| 118 | +| `policy.yaml` loaded from workspace root | `load_file_policy(root)` finds it | |
| 119 | +| Matching rule blocks the tool call | Runner status is `failed:*` | |
| 120 | +| Non-matching tools not affected | Read-only tools pass through | |
| 121 | +| Rules fire in `danger-full-access` mode | File policy is independent of `PermissionMode` | |
| 122 | +| `argument_pattern` + `tool_pattern` combined | Glob matching on both axes | |
| 123 | + |
| 124 | +--- |
| 125 | + |
| 126 | +#### Webhook Audit Event Delivery |
| 127 | + |
| 128 | +**File:** `test_webhook_audit_flow.py` |
| 129 | + |
| 130 | +**Story (AC-NEW-12):** *As a fleet operator, I want to subscribe to audit events |
| 131 | +via webhook so my SIEM receives every important event in real time.* |
| 132 | + |
| 133 | +| Assertion | Details | |
| 134 | +|---|---| |
| 135 | +| `run_started` and `run_completed` delivered | During a full `run_chat_agent` call | |
| 136 | +| HMAC-SHA256 signature verifiable | `X-TeaAgent-Signature-256` header | |
| 137 | +| Event filter restricts delivery | Only whitelisted `event_type` values sent | |
| 138 | +| Webhook failure does not abort run | Unreachable endpoint silently ignored | |
| 139 | + |
| 140 | +--- |
| 141 | + |
| 142 | +#### Error Remediation Hints |
| 143 | + |
| 144 | +**File:** `test_error_remediation_flow.py` |
| 145 | + |
| 146 | +**Story (AC-NEW-2):** *As a user with a misconfigured environment, I want error |
| 147 | +messages to include actionable remediation hints.* |
| 148 | + |
| 149 | +| Assertion | Details | |
| 150 | +|---|---| |
| 151 | +| `BudgetExceededError` has hint about limits | Default hint mentions `max_iterations` | |
| 152 | +| `ToolPermissionError` has hint about mode | Default hint mentions `allow`/`permission` | |
| 153 | +| `ToolExecutionError` has workspace hint | Default hint mentions workspace/command | |
| 154 | +| `RunCancelledError` has resume hint | Default hint mentions `agent resume` | |
| 155 | +| Hints appear in `str()` output | `→` separator injected by `__str__` | |
| 156 | +| Custom hints override defaults | `hint=` kwarg takes precedence | |
| 157 | + |
| 158 | +--- |
| 159 | + |
| 160 | +#### Audit Log Integrity |
| 161 | + |
| 162 | +**File:** `test_audit_chain_integrity_flow.py` |
| 163 | + |
| 164 | +**Story (AC-NEW-13):** *As a security lead, I want the audit JSONL log to be |
| 165 | +verifiable so that tampering is detectable.* |
| 166 | + |
| 167 | +| Assertion | Details | |
| 168 | +|---|---| |
| 169 | +| Every JSONL line is valid JSON | Parseable individually | |
| 170 | +| Event IDs are unique within a run | No duplicates across events | |
| 171 | +| Sensitive values redacted in persisted log | `content` argument not in raw file | |
| 172 | +| Disk log matches in-memory events | Event types in same order | |
| 173 | +| Audit files have restricted permissions | Not world-readable (mode ≤ 0o600) | |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## Integration Tests (tests/integration/) |
| 178 | + |
| 179 | +| File | Coverage | |
| 180 | +|---|---| |
| 181 | +| `test_runner_cost_tracking.py` | `RunResult` cost fields + audit event fields (IT-1) | |
| 182 | +| `test_cancel_token.py` | Cancel token — pre-cancel, mid-run, thread-safe (IT-2) | |
| 183 | +| `test_tool_rate_limit.py` | Sliding-window quota, concurrency safety, expiry (IT-3) | |
| 184 | +| `test_mcp_tool_adapter.py` | `register_mcp_tools` discovery, annotations, filter (IT-4) | |
| 185 | +| `test_skill_loader.py` | `load_skills` discovery, dedup, cap, prompt injection (IT-5) | |
| 186 | +| `test_audit_sink_isolation.py` | Crashing sink does not propagate or block other sinks (IT-6) | |
| 187 | +| `test_file_policy.py` | `DenyRule` matching, `FilePolicy.assert_allowed`, runner wiring (IT-7) | |
| 188 | +| `test_webhook_sink.py` | HTTP delivery, HMAC, filter, failure suppression (IT-8) | |
| 189 | +| `test_error_hints.py` | All error classes have default hints, `str()` rendering (IT-9) | |
| 190 | +| `test_subagent_budget_inheritance.py` | Subagent depth limit, error dict, registry guard (IT-10) | |
| 191 | +| `test_run_resume_checkpoint.py` | Checkpoint save on tool completion, pending-approval, SQLite round-trip, obs replay (IT-11) | |
| 192 | +| `test_destructive_approval_lifecycle.py` | Pause → approve → resume; deny path; auto-approve handler; read-only block (IT-12) | |
| 193 | +| `test_streaming_tool_calls.py` | `on_chunk` callbacks, audit events, token accumulation (IT-13) | |
| 194 | +| `test_schema_migration_live.py` | Migration ordering, idempotency, data survival, version tracking (IT-14) | |
| 195 | +| `test_dpop_replay_concurrency.py` | Concurrent JTI consume: exactly one success; expiry reset (IT-15) | |
| 196 | + |
| 197 | +--- |
18 | 198 |
|
19 | 199 | ## Next User Stories |
20 | 200 |
|
21 | | -- Define the next product-facing acceptance story beyond safe provider gates, such as release packaging smoke or IDE command flow. |
| 201 | +The following stories are specified and prioritised but do not yet have |
| 202 | +acceptance tests. Each maps to a Review gap analysis recommendation. |
| 203 | + |
| 204 | +| ID | Story | Priority | |
| 205 | +|---|---|---| |
| 206 | +| AC-NEW-1 | `teaagent init` first-time wizard | P2 | |
| 207 | +| AC-NEW-3 | Workspace profile (`.teaagent/profile.toml`) | P2 | |
| 208 | +| AC-NEW-4 | Interactive diff preview + inline y/n approval | P0 | |
| 209 | +| AC-NEW-6 | `teaagent run undo <run_id>` workspace rollback | P2 | |
| 210 | +| AC-NEW-8 | Desktop/webhook notification on ultrawork completion | P2 | |
| 211 | +| AC-NEW-11 | A2A delegation carries `traceparent` header | P1 | |
| 212 | +| AC-NEW-15 | Configurable PII redaction categories | P2 | |
| 213 | +| AC-NEW-16 | HTTPS_PROXY / CA bundle / mTLS "just works" | P2 | |
| 214 | +| AC-NEW-17 | `teaagent upgrade` schema migration dry-run preview | P2 | |
| 215 | +| AC-NEW-18 | Graceful degradation when disk fills mid-run | P2 | |
| 216 | +| AC-NEW-19 | `teaagent run export / import` | P2 | |
| 217 | +| AC-NEW-20 | Local model (Ollama/vLLM) with full governance | P2 | |
| 218 | +| AC-NEW-21 | `teaagent eval run <suite>` with HTML report | P2 | |
| 219 | +| AC-NEW-22 | `teaagent benchmark` latency/cost regression tracking | P2 | |
0 commit comments