ADR 0022: Telemetry sends a failure's output, scrubbed, and only as asked
Status: accepted · Date: 2026-10-16
Deciders: the user, planning sluice’s logs, 2026-10-16.
Sources: agentplot/blueprints:design/sluice/requirements.md clauses 16, 20, 26, 31, 32, 38, 53, 66; rules core.1, core.2, core.9, core.10, core.11, core.14, core.19; ADRs 0010, 0014.
Context and problem statement
Section titled “Context and problem statement”With telemetry on (ADR 0014), a backend shows that a candidate failed and which round read it, but not why. What a failed task printed is kept on the host that ran it, beside the branch’s records, for the last 20 failed candidates (20), and read by sluice status and the pull request’s comment. Everything else sluice writes as a log is either a record (log.jsonl, results.jsonl), a run’s raw output removed with its place, or a line on stderr nobody keeps. Should sluice send any of it as OTLP logs, and how, when a check’s output can be large and can hold a secret?
Considered options
Section titled “Considered options”- No logs: a reader goes to the host for the kept output.
- Tail every run’s output as it is written.
- Send only a pointer: which task failed and where its output is kept.
- Send a pointer always, and the output’s end, scrubbed, at a level a host chooses apart from
SLUICE_OTEL.
Decision outcome
Section titled “Decision outcome”Option 4.
Tailing (2) needs a process reading files for the life of a run: a service the core does not start (31, core.10, ADR 0010), or an exporter in the run’s supervisor, which holds the run’s lock and whose stderr goes nowhere (32, 66). Most of what it would send is the output of tasks that passed. A host that wants it can point its own log shipper at the run directories; sluice does not.
A pointer alone (3) names a path on one host that is pruned after 20 candidates. It is the default, since it sends nothing a check printed.
- What is sent. The core emits one
tracingevent per failed task, targetsluice::output, where it keeps that task’s output, with the output as its message andsluice.branch,sluice.candidate,sluice.submissions,sluice.task,sluice.kept,sluice.bytesandsluice.replayedas fields; a preview whose command failed emits one where it is released with its output kept. sluice’s own warnings and errors on stderr are emitted astracingwarnings and errors. The binary bridges those events, and no others, to OTLP log records: a metric’s event never becomes a log. The core names no backend (core.1, core.2). - Under the round. A record is emitted in the span current where it is read: a failure’s output under the
sluice.verdictspan of the round that read the verdict, the round that tells the culprit (20). The run was started in an earlier round, since a step does not wait for a run (core.14);sluice.candidatenames the revision on both, and on moon’s resource attributes assluice.revision.sluice.replayedsays when the output came from the task runner’s cache rather than an observation (16, 38; core.9). - The level.
SLUICE_OTEL_LOGSor--otel-logs:off,summary(the default) ortail, read only while telemetry is on. Atsummarya record’s body names the task and where its output is kept. Attailit is the output’s end, at mostSLUICE_OTEL_LOGS_LIMITbytes (8 KiB by default, never more than the 64 KiB kept), cut at a character’s start. - Scrubbed before it leaves. Every body sent, a warning’s included, has replaced with
[redacted]the value of any variable in sluice’s environment whose name holdsTOKEN,SECRET,KEY,PASS,AUTH,CREDENTIALorHEADERS, and what reads as a GitHub, AWS, Slack or Anthropic token, a JWT, a bearer token or a private key block. Scrubbing lowers the chance a secret is sent and does not rule it out; that is whytailis a host’s choice. - Telemetry still decides nothing. The kept copy on disk is the record, unchanged and unscrubbed, readable with no sluice process running (26, 53; core.11). A record the collector does not take is lost and never sent again. A preview’s record is read by nothing that decides (core.19).
Consequences
Section titled “Consequences”- sluice’s binary carries the OpenTelemetry logs SDK, its
tracingbridge andregex; the core still depends only ontracing. - A host sets
SLUICE_OTEL_LOGS=tailonly where whoever reads its backend may read the repository’s check output. - A backend receiving logs needs its logs pipeline enabled:
docs/telemetry.mdsays how for each receiver.