# AI Triage v1 Design

## Chosen approach

The controller is a one-shot Python CLI launched by GitHub Actions. This keeps scheduling ownership in GHA, avoids a second long-running service, and makes each controller run visible and auditable. A repository variable gates scheduled execution, and the example task is disabled, so merging v1 cannot dispatch production tests by itself.

Two alternatives were rejected for v1: a persistent daemon would recreate the EC2 operational model and require state/health management; a pure shell workflow would make log parsing, ACP JSON-RPC, schema validation, and unit testing fragile.

## Components

- `config.py`: strict TOML parsing, environment references, task selection, and validation.
- `models.py`: immutable task, run, evidence, triage, and report data structures.
- `github.py`: REST dispatch, legacy run discovery, polling, job listing, and job-log download.
- `redaction.py`: deterministic secret/PII removal used at every egress boundary.
- `evidence.py`: GHA log normalization, failed-step gate, retry-aware HTTP evidence extraction, and evidence gaps.
- `acp.py`: minimal ACP v1 JSON-RPC stdio client with no filesystem/tool permissions.
- `triage.py`: prompt construction, AI result validation, safe fallback, deterministic gate, and owner routing.
- `feishu.py`: custom-bot webhook payload creation and delivery.
- `report.py`: stable redacted JSON and Markdown output.
- `orchestrator.py`: the state machine joining dispatch, polling, collection, triage, reporting, and notification.
- `cli.py`: `validate` and one-shot `run` commands.

## Data flow

1. GHA supplies the exact cron string or `manual` schedule key.
2. The CLI selects enabled tasks and resolves every selected task input before constructing clients or dispatching anything. A missing secret therefore produces zero partial dispatches.
3. The GitHub client dispatches the target workflow. A v2026 response supplies the run ID directly; a legacy 204 response enters bounded discovery.
4. The controller polls the run to completion and writes lifecycle notifications, including the terminal conclusion, when enabled.
5. On failure, it lists current-attempt jobs and processes every primary failed job.
6. Non-scenario failed steps are deterministically classified as infrastructure errors.
7. Scenario job logs are downloaded in memory with explicit byte limits. GitHub log redirects are followed only through a separate HTTPS request without the GitHub authorization header. Logs are redacted and parsed, and only the resulting evidence object reaches ACP.
8. The ACP agent returns strict JSON. The controller validates and normalizes it, applies the gate again, routes an owner, and builds a report.
9. Redacted JSON/Markdown reports are written before final notification, so a Feishu outage cannot lose the triage result.

## Failure behavior

GitHub API/authentication, dispatch ambiguity, and polling deadline errors fail the controller task with an explicit operational report. Missing logs or incomplete evidence do not invent fields; they are represented by `null` values and `evidence_gaps`. ACP failure falls back to infrastructure/low confidence while preserving parsed evidence. Feishu failure is recorded separately and does not change the downstream run conclusion or category.

## Safety decisions

Raw job logs never reach reports or agents. Credential keys are normalized across snake case, camel case, and prefixed environment names before redaction. All agent tool permission requests are rejected, filesystem capabilities are disabled, controller/downstream secrets are removed from the ACP environment, and timeout cleanup terminates the ACP process group. The prompt explicitly treats log content as untrusted data. Knowledge files are read-only. Secret values are provided only through GHA Secrets/environment variables.

## Known v1 limitations

Webhook notifications do not guarantee a reply inside an existing Feishu topic. Legacy GitHub dispatch correlation can be ambiguous under concurrent manual dispatches. The latest GitHub API avoids that ambiguity by returning a run ID. Owners remain unassigned until mappings are configured. V1 has no database, crash-resume state, or cross-run deduplication. Read retries can still consume part of the configured polling budget under prolonged GitHub rate limiting. Downstream workflows still expose sensitive workflow inputs in job logs; v1 contains that exposure at triage egress but does not repair the downstream repository.
