Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
c630931
feat(webhooks): agent schedule webhook triggers (WEBHOOK-001, #291)
vybe Apr 24, 2026
619f2f6
fix(security): patch 4 Dependabot alerts — happy-dom + vite (#486)
vybe Apr 24, 2026
b528c0c
docs(webhooks): add WEBHOOK-001 to requirements and architecture (fix…
vybe Apr 24, 2026
8fdf736
feat(webhooks): merge WEBHOOK-001 agent schedule webhook triggers (#291)
vybe Apr 24, 2026
ef6cfdb
feat(#488): add status-in-dev label + PR-merge automation (#489)
vybe Apr 24, 2026
5cccee4
feat(channels): file upload Phase 2 — workspace delivery hardening (#…
vybe Apr 25, 2026
81a78f4
fix(webhooks): import Request in schedules router (#495)
vybe Apr 25, 2026
1b68e41
fix(backlog): repair drain spawn — lazy-import target after #95 (#496…
vybe Apr 25, 2026
7bc481a
feat(announce): add Twitter/X support via API v2 + OAuth 1.0a
vybe Apr 25, 2026
dda730f
fix(chat): sync /task long-polls on backlog at capacity (#498) (#515)
vybe Apr 26, 2026
c94000e
docs(groom): document SDLC stages and add status-label/board reconcil…
vybe Apr 26, 2026
42f75be
fix(agent): classify signal-killed claude exits as 504, not fake auth…
vybe Apr 26, 2026
c221ba1
refactor(sprint): align skill with DEVELOPMENT_WORKFLOW.md (#519)
vybe Apr 26, 2026
0b3bb58
fix(agent): classify clean-exit empty-result as 502, not silent succe…
vybe Apr 26, 2026
c43748d
fix(agent): off-load synchronous terminate cleanup off the event loop…
vybe Apr 26, 2026
3f93db5
docs(planning): add Tier 2.6 hardening + actor-model destination roadmap
vybe Apr 26, 2026
e53fb4f
docs(planning): mark #291 (WEBHOOK-001) shipped, Sprint C now 5/5
vybe Apr 26, 2026
202e2d4
refactor(capacity): consolidate three queue/slot primitives into Capa…
vybe Apr 27, 2026
44c6802
fix(agent): drain pipe before close to preserve final result line (#5…
vybe Apr 27, 2026
e372006
fix(tests): update orphaned-recovery mocks to CapacityManager (#533) …
vybe Apr 27, 2026
d6f40a6
docs(skills): align validate-pr with DEVELOPMENT_WORKFLOW.md
vybe Apr 27, 2026
01d12bf
fix(validate-architecture): stale-citation filter + dedupe guard (#51…
AndriiPasternak31 Apr 27, 2026
e4e7a08
fix(config): clean stale Auth0 / AUDIT_URL, document SMTP/SendGrid/FR…
AndriiPasternak31 Apr 27, 2026
c79e149
fix(auth): require auth on /api/docs endpoints (#452) (#507)
AndriiPasternak31 Apr 27, 2026
648640b
fix(chat): wrap long unbroken strings in chat bubbles (#457) (#502)
AndriiPasternak31 Apr 27, 2026
67a5c82
fix(schedules): add missing Request import for webhook endpoints (#493)
pavshulin Apr 27, 2026
a4d3148
refactor(git): route orphan cleanup through db.delete_git_config (#45…
AndriiPasternak31 Apr 27, 2026
8c0f4b9
feat(subscription): auto-switch on first failure + auth-class trigger…
AndriiPasternak31 Apr 27, 2026
6882b57
docs(feature-flows): clean up #95 drift missed by #500 (#496) (#503)
AndriiPasternak31 Apr 27, 2026
32825ec
fix(deploy): align scripts, configs, and docs with production operati…
vybe Apr 27, 2026
0e600ad
fix(reliability): CAS guards on execution status writes + state machi…
vybe Apr 27, 2026
f199383
fix(public-chat): build context before storing user message to preven…
vybe Apr 27, 2026
415017f
fix(agent-runtime): guard content_block isinstance in process_stream_…
vybe Apr 28, 2026
b9757ca
docs(#411): Phase 1 canary harness design + catalog Phase 1 subset ad…
obasilakis Apr 28, 2026
38bae70
feat(settings): add Remove buttons for stored API keys + Slack (#459)…
dolho Apr 28, 2026
a8db324
fix(migrations): swallow duplicate-column race on cold start (#456) (…
dolho Apr 28, 2026
2509ad7
fix(git): UI Push no longer commits runtime state (#462)
vybe Apr 28, 2026
50fd3f4
feat(frontend): semantic status color tokens (#67) (#553)
oleksandr-korin Apr 28, 2026
0e97166
feat(files): FILES-001 outbound file sharing — MVP + Phase 1 hardenin…
pavshulin Apr 28, 2026
1ebe473
fix(channels): deliver images as vision content blocks via stream-jso…
vybe Apr 28, 2026
307d3cd
feat(frontend): add state, brand, accent token families (#555) (#561)
oleksandr-korin Apr 28, 2026
6fa665c
feat(scheduler): agent-owned pre-check hook (#454) (#455)
dolho Apr 28, 2026
8e3dd16
feat(chat): quick-action buttons in empty chat state (#363) (#551)
dolho Apr 28, 2026
1ca4b0b
ci: add dev deployment workflow
vybe Apr 28, 2026
5270d26
ci: serialize deploys with concurrency group
vybe Apr 28, 2026
cc19a0d
docs(readme): support dark/light mode for Trinity logo
vybe Apr 28, 2026
a36be38
docs(readme): use gh-dark-mode-only fragments for logo theming
vybe Apr 28, 2026
b19b03c
refactor(frontend): migrate operator queue to design-system tokens (#…
oleksandr-korin Apr 29, 2026
747d156
feat(frontend): Playwright e2e harness gated on `ui` label (#556) (#571)
oleksandr-korin Apr 29, 2026
7f09aa7
fix(files): remove redundant session_token gate from /api/files/{id} …
dolho Apr 29, 2026
2d4589e
feat(audit): daily retention prune for audit_log entries past 365d (#…
dolho Apr 29, 2026
85dfe14
feat(public-chat): chat history dropdown for logged-in users (#587)
vybe Apr 29, 2026
e23ade9
docs(architecture): correct health endpoint paths to /health (#565) (…
dolho Apr 29, 2026
f6226dd
feat(dashboard): command + response/error preview on timeline hover (…
dolho Apr 29, 2026
13612bb
fix(e2e): make Playwright auth setup actually authenticate (#556) (#579)
oleksandr-korin Apr 29, 2026
4142e15
sec(ws): single-use ticket auth on /ws — drop JWT-in-URL leak (#550) …
dolho Apr 29, 2026
f55f4b5
feat(skills): add /test-runner-ui-e2e for Playwright e2e suite (#556)…
oleksandr-korin Apr 29, 2026
7d8abe8
feat(voice): tool calls + canvas orb for voice chat (#581) (#594)
vybe Apr 29, 2026
5e2b786
feat(web-chat): file upload for web and public chat (#364)
vybe Apr 30, 2026
660640d
sec(files): close .mcp.json RCE-by-config bypass (AISEC-C2, #590)
pavshulin Apr 30, 2026
b474520
sec(mcp): structure-validate .mcp.json content (AISEC-C2 Layer 2, #598)
pavshulin Apr 30, 2026
c64dca2
feat(slack): UI + API to change DM-default agent per workspace (#584)…
pavshulin Apr 30, 2026
9a4655c
refactor(frontend): migrate Monitoring view to design-system tokens (…
oleksandr-korin Apr 30, 2026
a3011c4
refactor(frontend): migrate ApiKeys + tier e2e tests by @smoke tag (#…
oleksandr-korin Apr 30, 2026
dd97979
chore: pre-release cleanup — fix combined test suite, workflow, skills
vybe Apr 30, 2026
ede960e
fix(ci): revert Tailscale to authkey — OAuth secrets not yet provisioned
vybe Apr 30, 2026
baa475f
chore: merge main into dev before release v0.5.0 (resolve squash-merg…
vybe Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions .claude/agents/test-runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ The test suite covers:
- **File Upload** (unit/test_file_upload.py) - Telegram file extraction, download, message router validation, parse_message with files (#354 Phase 1); workspace delivery — filename sanitization (NFKC, traversal, length, collision dedup), chat injection format `[File uploaded by {uploader}]`, all-writes-failed signaling, partial failure handling (#487 Phase 2) [UNIT]
- **Telegram Voice** (unit/test_telegram_voice.py) - Voice transcription validation (duration/size limits), formatting, placeholder constants (#318) [UNIT]
- **Inter-Agent Timeout** (test_inter_agent_timeout_unit.py) - FanOutRequest Optional timeout, per-subtask None dispatch, conditional asyncio.timeout wrap (#418) [UNIT]
- **Subprocess PGroup** (unit/test_subprocess_pgroup.py) - Process-group lifecycle: terminate, drain_reader_threads (natural-drain ordering #531, buffered-data preservation), safe_close_pipes, signal_process_tree (#407, #531) [UNIT]
- **Empty Result Classification** (unit/test_empty_result_classification.py) - Clean exit with lost result line → 502, two-field null check, raw_messages num_turns fallback, populated-metadata pass-through (#520, #521, #531) [UNIT]

### Avatars & Image Generation
- **Avatars** (test_avatars.py) - Avatar serving, generation, regeneration, deletion, emotions, identity prompts, default generation (AVATAR-001/002/003) [SMOKE + Agent]
Expand Down Expand Up @@ -231,9 +233,9 @@ The test suite covers:

## Test Suite Statistics

**Total Tests**: ~2,257 tests across 124 test files
**Total Tests**: ~2,262 tests across 124 test files
**Smoke Tests**: ~578 tests (fast, no agent creation)
**Unit Tests**: ~165 tests (no backend needed, rate limit detection, watchdog logic, context formula, OTel trace logging, file upload, voice transcription, inter-agent timeout, scheduler sync loop, team-share gate, WhatsApp adapter (67))
**Unit Tests**: ~170 tests (no backend needed, rate limit detection, watchdog logic, context formula, OTel trace logging, file upload, voice transcription, inter-agent timeout, scheduler sync loop, team-share gate, WhatsApp adapter (67), subprocess pgroup (11), empty result classification (13))
**Core Tests (not slow)**: ~2,112 tests
**Slow Tests**: ~99 tests (chat execution, fleet ops, system agent ops, execution termination, WhatsApp live-backend integration)
**WebSocket Tests**: ~10 tests (web terminal, execution streaming)
Expand Down Expand Up @@ -263,6 +265,40 @@ Use these thresholds to assess test health (based on **executed** tests, not inc
- **Warning**: 75-90% pass rate, <5 failures
- **Critical**: <75% pass rate or >5 failures

## Recent Test Additions (2026-04-27)

| Test File | Description | Tests Added |
|-----------|-------------|-------------|
| `unit/test_subprocess_pgroup.py` | Regression test for #531 — `test_buffered_data_preserved_after_grandchild_kill` verifies that data written to stdout before a grandchild is killed (including the final result JSON line) is not dropped when `drain_reader_threads` is called | +1 test (file total 11) |
| `unit/test_empty_result_classification.py` | Four new tests for `_classify_empty_result` raw_messages fallback (#531): `test_raw_messages_fallback_derives_num_turns`, `test_raw_messages_fallback_not_used_when_num_turns_present`, `test_raw_messages_empty_falls_back_to_zero`, `test_raw_messages_none_falls_back_to_zero` | +4 tests (file total 13) |

**Pipe drain ordering fix (#531)** — `drain_reader_threads` in `subprocess_pgroup.py`:

Root cause of the "Execution completed without a result message" HTTP 502 on long agentic tasks. The old code called `safe_close_pipes()` immediately after `terminate_process_group()`, discarding the kernel pipe buffer (including the `{"type":"result"}` final JSON line) before the reader thread could drain it. Fixed: kill grandchildren first, then wait up to `post_kill_grace=30s` for natural drain; force-close only as a last resort for genuine wedges.

The new `test_buffered_data_preserved_after_grandchild_kill` test spawns a subprocess that writes a sentinel `RESULT_LINE` then forks a grandchild that holds stdout open; verifies the sentinel survives the grandchild kill. The `_classify_empty_result` tests verify that `num_turns` is derived from `raw_messages` when the result line was lost (since `metadata.num_turns` is only populated by that line), while `tool_count` continues to come from metadata (accumulated per-message during parsing, so accurate even without the result line).

---

## Recent Test Additions (2026-04-25)

| Test File | Description | Tests Added |
|-----------|-------------|-------------|
| `unit/test_github_init_gitignore.py` | Gitignore widen + migrate-on-push (#462) — full exclusion list coverage, idempotency, doc/constant sync, `git rm --cached` for newly-ignored files | 4 new tests (file total 6) |

**Gitignore Widen + Migrate (#462)** (`unit/test_github_init_gitignore.py`):

- `test_preserves_preexisting_gitignore` — existing `.gitignore` content is retained after init (pre-existing test)
- `test_fresh_agent_ignores_env_and_mcp_json` — fresh agent gets `.env` and `.mcp.json` exclusions (pre-existing test)
- `test_full_documented_exclusion_list_present` — all patterns in `_GITIGNORE_PATTERNS` constant are written to `.gitignore` (new)
- `test_idempotent_double_run` — running `initialize_git_in_container` twice does not duplicate patterns (new)
- `test_doc_and_constant_in_sync` — `_GITIGNORE_PATTERNS` constant matches the exclusion block in `TRINITY_COMPATIBLE_AGENT_GUIDE.md` (new)
- `test_rm_cached_for_newly_ignored_files` — `sync_to_github` calls `git rm --cached` for files that are tracked but now match a `.gitignore` pattern (new)

Pure unit tests over `git_service.py` stubs; no backend container required. Run via `pytest tests/unit/test_github_init_gitignore.py`.

---

## Recent Test Additions (2026-04-23)

| Test File | Description | Tests Added |
Expand Down
32 changes: 30 additions & 2 deletions .claude/skills/cso/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,35 @@ For each workflow file in `.github/workflows/`:

**Docker socket access**: Check if backend has Docker socket mounted and what permissions it has.

**Severity**: CRITICAL for prod DB URLs with credentials in committed config. HIGH for root containers in prod / Docker socket access without read-only. MEDIUM for missing USER directive / exposed ports.
**Redis authentication and network isolation**:
- Check `redis.conf` or Docker env for `requirepass` — unauthenticated Redis reachable from the agent container network is CRITICAL (agents get full read/write to all task queues and secrets including other tenants')
- Check if agent containers have direct TCP access to Redis (e.g., `redis:6379`) — they should reach the platform only via the backend HTTP API, not the Redis port directly
- Check Redis keyspace for per-user isolation — if all users share a Redis namespace without user-scoped key prefixes, cross-tenant enumeration is possible even with auth

**Docker `base_image` allowlist**:
- Search agent creation code for the `base_image` field — verify it's validated against an allowlist (regex or explicit list), not accepted as free text from the API request body
- An unvalidated `base_image` lets any authenticated user pull and execute arbitrary Docker images inside the agent network, giving access to Redis, the MCP server, and credential env vars

**Setup/onboarding endpoint exposure**:
- Check if the first-run setup endpoint (`/api/setup`, `/setup`, etc.) is network-accessible or bound to 127.0.0.1 only
- If accessible from the public internet, check whether it uses a single-use bootstrapping token to prevent a race-condition hijack granting full admin control to an external caller

**Severity**: CRITICAL for Redis without auth accessible from agent network. CRITICAL for unvalidated `base_image`. HIGH for setup endpoint reachable from public internet without a single-use token. HIGH for root containers in prod / Docker socket access without read-only. MEDIUM for missing USER directive / exposed ports.

**FP rules**: `docker-compose.yml` for local dev with localhost = not a finding.

### Phase 6: Webhook & Integration Audit

**Webhook routes**: Find files containing webhook/hook/callback route patterns. Check whether they also contain signature verification.

**Internal endpoint auth completeness**:
- For every route in the internal router (routes under `/api/internal/` or any "internal" prefix), verify each endpoint enforces the shared-secret header (`X-Internal-Secret`) — not just that the pattern exists in the file, but that every individual route uses it
- Probe: send request to each internal endpoint without the header and confirm 401/403, not 200

**Webhook trigger auth (not just signature)**:
- For webhook trigger endpoints, check for the presence of `Depends(get_current_user)` or an equivalent auth gate — not just HMAC signature verification
- Trigger routes have been found missing auth entirely while signature verification exists elsewhere in the same file; the two are independent checks

**TLS verification disabled**: Search for `verify=False`, `VERIFY_NONE`, etc.

**MCP server security**: Check `src/mcp-server/` for authentication, input validation, and tool permission boundaries.
Expand All @@ -156,8 +177,9 @@ Trinity-specific AI security concerns:
- **AI API keys in code**: Hardcoded API key assignments (not env vars)
- **Credential injection safety**: Are injected credentials exposed to agent code in unexpected ways?
- **Cost/resource attacks**: Can a user trigger unbounded LLM calls via agent chat?
- **Agent container network reach**: From inside an agent container, check what internal services are directly reachable (Redis, MCP server backend port, etc.). Agent containers should reach the platform only through the backend HTTP API — direct TCP access to Redis, internal service ports, or other agents' containers outside the API layer is a CRITICAL isolation failure. Check `docker-compose.yml` network definitions and confirm agent containers cannot reach `redis:6379` or `mcp-server:8080` directly.

**Severity**: CRITICAL for user input in system prompts / unsanitized LLM output rendered as HTML. HIGH for missing tool call validation / exposed AI API keys. MEDIUM for unbounded LLM calls.
**Severity**: CRITICAL for user input in system prompts / unsanitized LLM output rendered as HTML. CRITICAL for agent containers with direct Redis or internal service access bypassing the API. HIGH for missing tool call validation / exposed AI API keys. MEDIUM for unbounded LLM calls.

**FP rules**: User content in the user-message position of an AI conversation is NOT prompt injection.

Expand All @@ -178,10 +200,14 @@ For each OWASP category, perform targeted analysis. Scope file extensions to Pyt
- Missing auth on routes (check FastAPI dependency injection for auth)
- Direct object reference (can user A access user B's agents by changing IDs?)
- Horizontal/vertical privilege escalation
- **Internal router completeness**: Every endpoint in the internal router must use the shared-secret dependency — audit the full router, not a sample. No route should return 200 without the secret header.
- **Role-permission consistency across ALL creation routes**: For each resource-creation endpoint (`POST /api/agents`, `/api/processes`, `/deploy-local`, webhook-triggered deployments, etc.), verify the same role-check dependency is used. The lowest-privilege role must not bypass the creation gate via any alternate route.
- **File-write path policy completeness**: For any file-write API, verify the path-deny/allowlist is evaluated in full — not just basename matching. Specifically check: can `.mcp.json`, `.env`, `.credentials.enc`, or `.ssh/*` be reached via path traversal or a sibling endpoint (e.g., `/credentials/inject`) that enforces a shorter deny list?

#### A02: Cryptographic Failures
- Weak crypto (MD5, SHA1, DES, ECB) or hardcoded secrets
- JWT implementation (algorithm, expiration, secret management)
- **SSH key server-side generation**: Check if SSH private keys are generated server-side and returned in API responses. Flag as HIGH — clients should generate keypairs locally and submit only the public key. Server-generated private keys are exposed in transit and in API/access logs.

#### A03: Injection
- SQL injection: raw queries, string interpolation in SQL (check `database.py`)
Expand All @@ -192,6 +218,8 @@ For each OWASP category, perform targeted analysis. Scope file extensions to Pyt
- Rate limits on authentication endpoints?
- Account lockout after failed attempts?
- Business logic validated server-side?
- **Rate limiting quality**: Per-IP-only rate limits are bypassable via rotating proxies/Tor/IPv6 subnets. Check that rate limiting on auth endpoints (login, OTP, password reset) uses a per-account/per-email counter as the primary bucket. Also check that hard lockout on IP isn't itself a DoS primitive — progressive delay or CAPTCHA is safer than a hard block.
- **OTP brute-force window**: For email OTP flows, verify the code space (6 digits = 1M possibilities) is protected by an attempt counter that survives process restarts (stored in Redis/DB, not in-memory), and that codes expire within 5–10 minutes.

#### A05: Security Misconfiguration
- CORS configuration (wildcard origins?)
Expand Down
Loading
Loading