You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(framework): resolve one person across tools and machines #661
As a team lead reading a report I want one person to appear as one person, whatever tool they used So that a weekly figure is a fact rather than an artefact of how many tools someone happens to run
Acceptance
The same human working through two different tools appears once in a report, not twice.
The same human on two machines appears once.
A person absent from the mapping is reported as unresolved and counted separately, never silently merged into someone else or dropped.
The mapping is auditable: given a report line, one can see which raw identities produced it.
A person can see the identities mapped to them before anyone else reads a report about them.
Whether identities are stored as names or as pseudonyms follows the decision issue, and is not chosen here.
Why this is not trivial
Measured: the same human carries a different identifier per tool, and none of them is the git identity that appears in commits.
Copilot's is pseudonymous by construction, so it cannot be reconciled by matching an address — it needs a mapping someone maintains, or it stays unresolved. Nothing in the design produces an actor attribute of its own: the run record has no author field at all.
"Team" has the same problem one level up: it is named in #656's output and defined nowhere, with no source of truth.
Out of scope
The anonymity decision itself, which gates this.
Any hosted directory integration. The mapping's source is a separate choice; this issue owns the shape and the guarantees.
Both modes are supported, so this issue no longer only reconciles identities — it owns the mapping that switches between them.
Records always carry a pseudonym. This issue produces it, and guarantees it is stable for one person across tools and machines.
The pseudonym-to-person mapping is a separate artefact, outside the repository, access-controlled. Its presence is what makes a deployment named; its absence is what makes it anonymous.
Resolution across the four vendor identities stays as specified. Copilot's is pseudonymous at the source, so it cannot be matched by address and needs the mapping or stays unresolved.
Two properties to add, both consequences of serving two modes from one format:
Removing the mapping must anonymise every future reading, immediately, without touching stored records.
The mapping must be auditable: given a report line, one can see which raw identities produced it — and that audit must itself be gated, or it becomes a way around the anonymous mode.
A product decision taken 2026-08-28 makes this the pivot of the whole aggregation milestone.
The line between what the local layer answers and what a hosted product answers is not a pricing choice, it is a structural one: a machine holds one person's files. Everything about your own work — tokens, models, steps, tasks — is answerable locally and should stay that way. Nothing about a second person is, and no amount of local cleverness changes it.
That makes this issue the one thing that is simultaneously indispensable to any team view and impossible to fake locally. Per person, per team, per epic (#656), the task breakdown (#720), and the backlog join (#649) are all views; they need their key first, and this is the key.
Practical consequence: it moves ahead of #654, which is a lookup table that opens nothing and can land at any time.
One constraint worth carrying into the design, from the layer's existing shape: person_id is opted into per person on the local-read route and is deliberately not derived from any tool's own user attribute — telemetry-sink-record.ts excludes user.id on purpose, since an export carries it whenever the tool happens to set it, consent or not. Resolving one person across tools and machines must keep that property: an identity is something a person grants, never something a join infers.
Activity
blafourcade commentedon Aug 16, 2026
Scope grown by the decision of 2026-08-16
Both modes are supported, so this issue no longer only reconciles identities — it owns the mapping that switches between them.
Two properties to add, both consequences of serving two modes from one format:
refactor(telemetry): a skill carries what it needs, and nothing reach…
blafourcade commentedon Aug 28, 2026
Moved to the front
A product decision taken 2026-08-28 makes this the pivot of the whole aggregation milestone.
The line between what the local layer answers and what a hosted product answers is not a pricing choice, it is a structural one: a machine holds one person's files. Everything about your own work — tokens, models, steps, tasks — is answerable locally and should stay that way. Nothing about a second person is, and no amount of local cleverness changes it.
That makes this issue the one thing that is simultaneously indispensable to any team view and impossible to fake locally. Per person, per team, per epic (#656), the task breakdown (#720), and the backlog join (#649) are all views; they need their key first, and this is the key.
Practical consequence: it moves ahead of #654, which is a lookup table that opens nothing and can land at any time.
One constraint worth carrying into the design, from the layer's existing shape:
person_idis opted into per person on the local-read route and is deliberately not derived from any tool's own user attribute —telemetry-sink-record.tsexcludesuser.idon purpose, since an export carries it whenever the tool happens to set it, consent or not. Resolving one person across tools and machines must keep that property: an identity is something a person grants, never something a join infers.feat(cli): resolve a raw identity to the person who declared it
feat(cli): add the person breakdown to the cost report
feat(cli): let a person declare which identifiers are them
test(cli): prove the resolution guarantees end to end
docs(telemetry): mark resolve-one-person implemented
fix(cli): repair review findings for the person-mapping report path
docs(telemetry): fix stale per-person docs, document identity link/un…
fix(cli): repair findings from the re-check of the #661 repair
docs(framework): amend #297 — a hosted destination is allowed, and ne…
blafourcade commentedon Sep 2, 2026
Delivered by #706, squash-merged into
nextas 627408f.