Telemetry
telemetry answers two questions about work already done: what did it cost, and whose was it.
It never causes the work and never installs anything on a project's behalf. Everything it reads
already exists on disk because a tool wrote it, so the whole context is a set of readers, a set
of attribution rules, and one sink.
Where the sink and the run journal live, what the report renders, and what each tool declares
about being measured are in aidd_docs/memory/telemetry.md. Read that for the facts; this page
says where new code goes and what it must not do.
What goes in
| Concept |
Location |
| A rendered answer's shape |
domain/cost-report.ts, domain/cost-report-envelope.ts |
| How a figure is tied to something |
domain/step-attribution.ts, domain/task-attribution.ts, domain/flow-attribution.ts |
| Reading one tool's own file format |
domain/formats/ (one module per tool's transcript or export) |
| A stored record and what happens to it over time |
domain/telemetry-sink-record.ts, domain/telemetry-sink-retention.ts |
| Who a session was for, and how strongly that is known |
domain/person-resolution.ts, domain/ports/person-identity-reader.ts, domain/ports/person-identity-store.ts |
| Something telemetry needs from outside itself |
domain/ports/ — declared here, satisfied at the composition root |
| The concrete reader behind one of those ports |
infrastructure/ |
One question the aidd telemetry command asks |
application/ |
How
- A tool declares, telemetry reads. What a route was measured to supply, and where a tool's
transcripts live, are declared in
kernel/measurement.ts and filled in per tool under
contexts/tools/domain/profiles/<tool>/. Every field there is required on purpose: a default
would be a capability nobody measured, quietly asserted for a tool nobody looked at. Never add
a branch on a tool id inside this context.
- What telemetry needs from another context, it declares as its own port.
installed-plugins-reader.ts
and ignore-entries.ts are the pattern: telemetry states the question, runtime/wiring/telemetry.ts
hands it an answer, and no context reaches into telemetry in return.
- A figure with no established denomination is not an amount. A zero whose denomination was never
established, a credit and a premium request are each their own thing; conflating them is how a
report lies without ever being wrong about a number.
- An interval derived from the run journal is this CLI's inference, not the tool's statement.
Keep the two distinguishable in whatever you add —
toolStatedStep exists for exactly that.
- Follow the use-case and port/adapter rules in
.claude/rules/00-architecture/.
Public surface
tests/architecture/context-boundary.arch.test.ts holds the list (PUBLIC_MODULES.telemetry):
the use cases the telemetry command drives, the shapes a rendered answer is made of, the
commit-trailer format the git adapter writes, and the two ports a caller wires a concrete
adapter into (domain/ports/telemetry-sink.ts, domain/ports/version-control.ts). Nothing
else, and no adapter. Rendering happens in presentation/display/, never here.
How it's tested
tests/contexts/telemetry/ mirrors src/contexts/telemetry/ — a format reader and an
attribution rule are unit-tier; an adapter against a real temp filesystem is integration-tier.
- A reader for a new tool's format needs a fixture captured from that tool's real output. A
format module tested only against a fixture this repository wrote proves the parser, not the
format. See the
test skill before touching a golden snapshot.
1---2name: telemetry3description: Owns what a session cost and who it was for, under src/contexts/telemetry/ — reading a tool's own local files, attributing a figure to a person, a task, a flow or a step, and the sink that keeps records per machine. Use when adding a reader for another tool's transcript format, changing how a figure is attributed or reported, touching the sink or the identity store, or wiring a telemetry port. Do NOT use for what a tool declares about being measured — that is `kernel/measurement.ts`, read via the `tools` skill. Do NOT use for installing or removing the hook that writes the run journal into a project — that record belongs to `framework`.4---56# Telemetry78`telemetry` answers two questions about work already done: what did it cost, and whose was it.9It never causes the work and never installs anything on a project's behalf. Everything it reads10already exists on disk because a tool wrote it, so the whole context is a set of readers, a set11of attribution rules, and one sink.1213Where the sink and the run journal live, what the report renders, and what each tool declares14about being measured are in `aidd_docs/memory/telemetry.md`. Read that for the facts; this page15says where new code goes and what it must not do.1617## What goes in1819| Concept | Location |20|---|---|21| A rendered answer's shape | `domain/cost-report.ts`, `domain/cost-report-envelope.ts` |22| How a figure is tied to something | `domain/step-attribution.ts`, `domain/task-attribution.ts`, `domain/flow-attribution.ts` |23| Reading one tool's own file format | `domain/formats/` (one module per tool's transcript or export) |24| A stored record and what happens to it over time | `domain/telemetry-sink-record.ts`, `domain/telemetry-sink-retention.ts` |25| Who a session was for, and how strongly that is known | `domain/person-resolution.ts`, `domain/ports/person-identity-reader.ts`, `domain/ports/person-identity-store.ts` |26| Something telemetry needs from outside itself | `domain/ports/` — declared here, satisfied at the composition root |27| The concrete reader behind one of those ports | `infrastructure/` |28| One question the `aidd telemetry` command asks | `application/` |2930## How3132- **A tool declares, telemetry reads.** What a route was measured to supply, and where a tool's33 transcripts live, are declared in `kernel/measurement.ts` and filled in per tool under34 `contexts/tools/domain/profiles/<tool>/`. Every field there is required on purpose: a default35 would be a capability nobody measured, quietly asserted for a tool nobody looked at. Never add36 a branch on a tool id inside this context.37- **What telemetry needs from another context, it declares as its own port.** `installed-plugins-reader.ts`38 and `ignore-entries.ts` are the pattern: telemetry states the question, `runtime/wiring/telemetry.ts`39 hands it an answer, and no context reaches into telemetry in return.40- A figure with no established denomination is not an amount. A zero whose denomination was never41 established, a credit and a premium request are each their own thing; conflating them is how a42 report lies without ever being wrong about a number.43- An interval derived from the run journal is this CLI's inference, not the tool's statement.44 Keep the two distinguishable in whatever you add — `toolStatedStep` exists for exactly that.45- Follow the use-case and port/adapter rules in `.claude/rules/00-architecture/`.4647## Public surface4849`tests/architecture/context-boundary.arch.test.ts` holds the list (`PUBLIC_MODULES.telemetry`):50the use cases the `telemetry` command drives, the shapes a rendered answer is made of, the51commit-trailer format the git adapter writes, and the two ports a caller wires a concrete52adapter into (`domain/ports/telemetry-sink.ts`, `domain/ports/version-control.ts`). Nothing53else, and no adapter. Rendering happens in `presentation/display/`, never here.5455## How it's tested5657- `tests/contexts/telemetry/` mirrors `src/contexts/telemetry/` — a format reader and an58 attribution rule are unit-tier; an adapter against a real temp filesystem is integration-tier.59- A reader for a new tool's format needs a fixture captured from that tool's real output. A60 format module tested only against a fixture this repository wrote proves the parser, not the61 format. See the `test` skill before touching a golden snapshot.