Redaction is applied at the output boundary by semvertag/_redact.py, and the two Output
implementations apply it differently:
RichOutput redacts all three paths — progress, emit, error.
JsonOutput redacts only error. progress is a no-op, and emit writes
dataclasses.asdict(result) to stdout as raw JSON with no redaction pass.
So a token that reaches an error message never hits the terminal, but a token that reached a
RunResult field would be printed verbatim in --json mode — the mode CI consumes and logs.
Whether that is reachable today is the first thing to establish: every RunResult field is a tag
name, a commit sha, a bump level, or a fixed reason string, so the gap may be structural rather than
live.
The issue should reach one of two ends, and record which:
- It is not reachable, and that is a property of
RunResult's shape — in which case the invariant
("no RunResult field ever carries user-supplied text") belongs in a named test, so a future
field that does carry such text trips it.
- It is reachable, or could become so — in which case
emit gets the same redact pass as the
others, and the cost is one traversal of a small envelope.
Redacting unconditionally is not free of consequence: redact substitutes *** for any bare 32+
character hex run, and a commit sha is 40 hex characters, so a naive pass over the serialized JSON
would destroy the commit field. Any fix has to redact per-field, not over the rendered string.
Revisit trigger: none — actionable now; the analysis is cheap and the answer is worth writing
down either way.
Origin: recorded only in architecture/providers.md ("a token embedded in a result field would
appear unredacted in JSON output"), deleted by the planning/ migration.
Redaction is applied at the output boundary by
semvertag/_redact.py, and the twoOutputimplementations apply it differently:
RichOutputredacts all three paths —progress,emit,error.JsonOutputredacts onlyerror.progressis a no-op, andemitwritesdataclasses.asdict(result)to stdout as raw JSON with no redaction pass.So a token that reaches an error message never hits the terminal, but a token that reached a
RunResultfield would be printed verbatim in--jsonmode — the mode CI consumes and logs.Whether that is reachable today is the first thing to establish: every
RunResultfield is a tagname, a commit sha, a bump level, or a fixed reason string, so the gap may be structural rather than
live.
The issue should reach one of two ends, and record which:
RunResult's shape — in which case the invariant("no
RunResultfield ever carries user-supplied text") belongs in a named test, so a futurefield that does carry such text trips it.
emitgets the sameredactpass as theothers, and the cost is one traversal of a small envelope.
Redacting unconditionally is not free of consequence:
redactsubstitutes***for any bare 32+character hex run, and a commit sha is 40 hex characters, so a naive pass over the serialized JSON
would destroy the
commitfield. Any fix has to redact per-field, not over the rendered string.Revisit trigger: none — actionable now; the analysis is cheap and the answer is worth writing
down either way.
Origin: recorded only in
architecture/providers.md("a token embedded in a result field wouldappear unredacted in JSON output"), deleted by the
planning/migration.