Skip to content

feat(telemetry): filter the report by task, ticket, person and more - #1033

Merged
blafourcade merged 10 commits into
nextfrom
feat/report-filters
Oct 16, 2026
Merged

blafourcade merged 10 commits into
nextfrom
feat/report-filters

Conversation

@blafourcade

Copy link
Copy Markdown
Contributor

🎯 What & why

aidd telemetry report could only split every call of a period along one axis. It could not answer "what did task auth cost, per day" or "what did this repository cost, per model". The report now takes filters: --task, --ticket, --person, --model, --agent, --session, --repository <id|prefix|.>, --absent <person|model|ticket>, --unattributed [reason].

🛠️ How it works

  • Every row a report prints can be selected, and the filtered report adds up to that row. A filter matches a call through the same axisValueOf that builds the rows (report-filter.ts:104), so a filter and a row cannot disagree. The flags mirror the three kinds of row in the envelope: a value, absent, or unattributed. A day row is selected by --from D --to D.
  • One selector per axis. Flags combine with AND, and each flag takes one value. --unattributed selects on both task and ticket, so combining it with --task, --ticket or --absent ticket is refused, as is a value plus --absent on the same axis. The use case refuses this itself, before anything is read (conflictsOf).
  • What the text shows can be typed back. The text report shows 8 characters of a session or repository id, so --session and --repository take a prefix. An exact value wins over a prefix: a 40-hex root commit can prefix a 64-hex id. An ambiguous prefix exits 1 and lists the full candidates (resolveId).
  • --repository . resolves the current directory with the same rule ingest uses. It runs after the AIDD_TELEMETRY=0 refusal, so a refused run never runs git.
  • Errors and empty results. A value outside a closed set is an error. A value that matches nothing gives an empty report with exit 0, and the text says so.
  • Envelope. It stays version: 1 and adds a filters field, always present. Each key is null or holds the value as resolved. coverage.records keeps its meaning (the calls of the period); totals covers what matched.
  • Docs. The 01-usage skill now turns "task X per day" into one filtered report. The usage contract and the CLI memory are updated. Contract: contract.md.

🧪 How to verify

  • cd cli && pnpm test: 6579 passed, 1 skipped.
  • Properties:
  • pnpm test:mutation:telemetry: 96.3 (floor 96). Survivors on changed lines are equivalent and explained in evidence.md.
  • pnpm smoke: 132/0/1. node scripts/check-claude-tokens-end-to-end.cjs (and --attended) now runs a filtered report too.
  • Tests were written first. Their red runs are recorded in evidence.md.
  • Independent review over 3 rounds, approved at 90%, with sandboxed runs of the built binary: review.md.

⚠️ Heads-up

  • Merge as a squash under this title. One commit on the branch is typed docs but changes how the skill frames questions; the squash title makes it a feat for release-please.
  • Known limits, written in the usage contract:
    • A task name belongs to no repository: add --repository . to scope it.
    • --repository . names the repository as it stands now. Calls stored under an earlier origin carry another id.
    • --person is the identity of the machine reporting, so locally it selects every call or none.
  • feat(telemetry): ask who is measured at opt-in, and name the default #1029 (identity at opt-in) touches the same command and display files. Whichever PR merges second resolves the conflicts.

🔗 Linked issue

Closes #1030

✅ I certify

  • I DO CERTIFY I READ EACH LINE OF THE PULL REQUEST BECAUSE I AM A SOFTWARE ENGINEER, NOT A AI PUPPY.

🤖 Generated with Claude Code

blafourcade and others added 10 commits October 15, 2026 21:01
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
A filter compares what axisValueOf gives a call with the value a flag
names, so it selects exactly the calls a row of that axis holds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
The use case refuses first, locates `.` before reading anything, then
resolves a session or repository typed by its start among the calls of
the period. The envelope names every filter, as resolved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Nine flags, one value each, trimmed and checked before anything is
read. The text names what was selected, and says when nothing matches.
The end-to-end check also runs one filtered report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
The usage contract, the telemetry memory and the usage skill name the
nine filters and the envelope's `filters`. One display assertion covers
the ambiguity of a repository start, which mutation found uncovered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
The use case refuses two flags that select on one axis, before the
environment's refusal, and takes `.` as its own field, with no cast.
A repeated bare flag is named, not shown as `null`; the selection line
reads the selectors the filter matches with. The end-to-end judge takes
a root-commit id, the usage skill says when a filter matched nothing,
and the contract's rule 2 names a value with `--absent` on its axis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
The command leaves conflicting filters to the use case, whose result
already exits 1. A request names a repository or `.`, never both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
@blafourcade
blafourcade marked this pull request as ready for review October 16, 2026 03:39
@blafourcade
blafourcade requested a review from a team as a code owner October 16, 2026 03:39
@blafourcade
blafourcade merged commit 7084a88 into next Oct 16, 2026
33 checks passed
blafourcade added a commit that referenced this pull request Oct 16, 2026
Brings in the report filters (#1033). Both sides kept: the `on`
identity question and options beside the report's filter options, both
public telemetry modules declared, both rows of the contributor map. The
absent person filter now reads `anonymous`, as the report rows do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
@aidd-bot aidd-bot Bot mentioned this pull request Oct 16, 2026
@blafourcade
blafourcade deleted the feat/report-filters branch October 17, 2026 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant