Skip to content

Build the per-tool-call provenance record — it does not exist today #400

Description

@KenTaniguchi-R

Slice 1 of #399. Blocked by nothing; everything else in #399 is blocked by this.

The record does not exist

The sink check needs to know which sources a call touched. Verified against main @ 6dbf1c6:

  • tools.log gets every call, but only the tool name and verdict (guard.ts:340-342)
  • the resolved path reaches calls.log only when noteworthy is set — a denial or a flag (guard.ts:344-355)
const noteworthy = verdict.allow ? verdict.flag : verdict;
if (noteworthy) {
  write(deps.line.callsLog, { ..., rule: noteworthy.rule, detail: noteworthy.detail });
}

An allowed read of a permitted file leaves no path record at all. That is correct for the logs' current purposes — calls.log is deliberately sparse and owner-facing — and it is exactly the wrong shape for provenance.

What to build

A per-call provenance record, separate from both existing logs:

  1. One entry per tool call that resolves a source, allowed or denied, carrying the resolved path (or MCP server / skill name).
  2. Sequence numbers, so a gap is detectable rather than invisible.
  3. An explicit gap marker, written when the guard could not decide or could not log. See Observe mode fails open silently — it must write a gap marker #401 — without it, observe mode's failure path is a silent fail-open.
  4. Bounded, like the existing telemetry spool, and cleaned up per call.

The decision this carries

This record is a full inventory of everything the owner's agent read. That is more sensitive than anything the codebase writes today, and guard.ts's own comments note that logs get pasted into bug reports.

Decide before landing:

  • Where it lives, and whether it is inside the ~/.agentcall secret floor (it should be)
  • Whether it survives the call at all, or is consumed at the sink and deleted
  • Whether it is ever exportable — the existing tool telemetry deliberately strips paths ("arguments, results, paths, and error text never reach the spool or the exporter"), and that decision should not be quietly reversed here

Recommendation: per-call, ephemeral, consumed at the sink, deleted after. A durable read-inventory is a new standing asset with its own threat model; the sink check does not need one.

Cost note

guard-entry.ts is a fresh node process per tool call and its import graph is pinned by guard-entry import budget in test/guard-entry.test.ts. An append to an open file is cheap, but do not reach for a dependency here.

Related

#399 (epic) · #401 (the gap marker) · #372

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions