Skip to content

disk-hygiene: make guard decisions auditable after the fact instead of vanishing when no telemetry sink is set #3862

Description

@kyle-sexton

This was generated by AI during triage.

Parent

Refs #3347, finding F12.

What to build

Guard decisions are emitted only through a telemetry channel that is inert unless an environment variable points it somewhere. In the ordinary case, where no sink is configured, every guard decision is discarded as it is made. The component's own data root holds only run records, so nothing about what the guard allowed or denied survives the session.

The consequence is that the questions an operator actually asks afterwards are unanswerable. Why was this denied. Has it been denying this all along. Did the guard even run. The parent's sibling findings make this concrete: one is about a false denial that had to be reproduced by hand to be believed, and another is about the guard silently not running at all. Both would be trivially answerable from a decision record, and both currently require re-deriving the situation live.

Make guard decisions durable and inspectable by default.

Design points

  • Default on, local, and bounded. The failure here is that observability is opt-in, so re-inventing it as opt-in solves nothing. The record should exist without configuration, live beside the component's existing run data, and be bounded so it cannot grow without limit. Bounding is what makes default-on defensible.
  • Record the decision, not the payload. Enough to answer why an operation was allowed or denied, and to distinguish a guard that denied from a guard that never ran. Not the full inspected content. This matters for the same reason the sibling fail-open finding matters: the interesting states include the guard's own absence, and an absent guard writes nothing unless the record is designed to make absence visible.
  • The telemetry channel stays. This is not a replacement. A configured sink should keep receiving what it receives now; the local record is the floor beneath it, for the ordinary case where no sink exists.
  • Cost is a real constraint. This is a hook path with a latency budget, and the marketplace has a standing convention about always-on hook cost. Writing a decision record must be cheap enough not to breach it. Measure rather than assume, and state the measured cost in the PR.
  • Readable without tooling. An operator diagnosing a denial should be able to read the record directly. A format needing a purpose-built reader reintroduces the opacity this fixes.

Acceptance criteria

  • Guard decisions are recorded durably with no configuration, in the component's existing data location.
  • A record distinguishes allowed, denied, and did-not-run. The third is the one the sibling fail-open finding needs and is easy to omit.
  • A denial's record contains enough to explain why, sufficient to answer the sibling false-positive finding from the record alone rather than by reproducing it.
  • The record is bounded, with the bound documented and enforced rather than advisory.
  • The existing telemetry channel behaves as it does today when a sink is configured.
  • The added cost on the hook path is measured, stated in the PR, and within the standing hook budget convention.
  • The record is readable without special tooling.
  • scripts/affected-tests.sh --run selects and passes the suites mapped to the changed files.

Out of scope

  • Changing any guard decision. This item observes; it does not adjudicate.
  • Shipping a viewer or analysis tool.
  • Cross-machine aggregation.

Blocked by

None. Can start immediately, and it would make the two sibling guard decisions easier to reason about with evidence rather than by reproduction.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.priority: mediumReal value, no hard deadline; normal backlog flow.status: readyTriaged, unblocked, and fully specified; eligible to pick up.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions