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
chore(framework): support both anonymous and named measurement #660
The framework has one recorded, owned answer to whether measurement is anonymous or attributable to a named person, and #297 is amended rather than quietly contradicted.
Scope
This is not an open question. #297 already decided it, as a decision of record: "Privacy: explicit opt-in, anonymised ids, never code or prompt content."
Milestone 16 reverses it. #656 reports per person by name; #652 names the conflict as a precondition. A decision of record that is contradicted by a later milestone without being amended is how a project ends up with two truths and no owner.
Two things make it heavier than a preference:
The journal enters git history. On a company repository it permanently records who worked on what, when, and for how long. Not a database anyone can purge — history.
The existing safeguard protects the wrong people. Telemetry is off by default on public repositories, which shields open-source contributors and nobody who is employed.
Decision, 2026-08-16 — support both, do not choose
Both modes are supported. The question stops being "anonymous or named" and becomes "how is one storage format made to serve both, and who decides which applies".
The shape that gives both from one format
Records always carry a pseudonym, never a name. A stable, per-person identifier with no meaning of its own.
The mapping from pseudonym to person is a separate artefact, held outside the repository, access-controlled, and optional.
Anonymous mode = the mapping does not exist.
Named mode = the mapping exists, and its holder is named.
One format, two modes. Switching means adding or removing a mapping rather than migrating data. #661 builds the mapping and now owns the guarantees around it.
The mode is a project decision, and the person is told
Set per project or per organisation, not per person: a mode a developer can flip individually makes any team figure meaningless. But the person is told which mode applies before anything is written about them, and can see their own records at any time. That is not a courtesy, it is what makes the measurement defensible.
Two traps, stated so nobody discovers them later
A stable pseudonym is not anonymity. Someone who is the only person committing to a given repository on a given day is identifiable by correlation, whatever the identifier says. Anonymous mode is honest protection against casual reading, not against a determined reader with access to the same repository. Claiming otherwise would be the kind of promise that ages badly — see #658 for what that costs.
The switch is asymmetric. Deleting the mapping anonymises what comes next and what is stored in the dashboard, but it cannot reach records already written into git history. Going the other way is impossible: a pseudonym never becomes a name retroactively. So the mode must be decided before the first record, and a change of mode has to state which direction it can and cannot reach.
What the journal actually adds beyond what git already reveals
Worth being precise, because it narrows the debate. With records materialised on commit (#620), the commit already names its author. The journal adds no new identity at that grain.
What it does add is how long the work took and what it cost. That is the genuinely new information about a person, and it is what this decision is really about — not who wrote the code, which git has always said.
Done When
The mode is a documented setting with a stated default, and the default is the more conservative of the two.
A person can see, before any record concerns them, which mode applies and what is kept.
A person can read their own records without asking anyone.
The documentation states plainly that a stable pseudonym resists casual reading, not correlation.
Changing mode states what it reaches and what it cannot, in both directions.
changed the title [-]chore(framework): decide whether measurement is anonymous or named[/-][+]chore(framework): support both anonymous and named measurement[/+]on Aug 16, 2026
The policy work — who sets the mode, what a person sees and when, the deletion path, the mapping itself — waits for the dashboard. Nobody needs it to produce a first figure.
Deferring is safe because two guards are in place from v1, and both are irreversible if missed:
Together they mean no name is written anywhere before this issue is opened, so both modes stay reachable at zero cost. The decision keeps its full range instead of being narrowed by a default nobody chose.
This issue says "Excludes: any implementation. This issue produces a decision and closes", and it blocks #656 and #661. That premise no longer holds: the mechanism shipped on the telemetry v1 branch (PR #706) before the decision was recorded.
What exists now: person_id and person_display_name as stored-record fields, a PersonIdentityAdapter, and two skill actions (04-identify, 05-forget).
What the shipped default does, so the gap is legible: nobody is named unless a person opts in on their own machine. A missing file, a damaged one, and one carrying no person_id all read the same way - nobody chose - and the file is read only from the OS user's own profile, with no environment override, so neither a repository nor a CI can supply an identity. That is compatible with #297's "anonymised ids".
So the gap is authorization, not behaviour. This can no longer close as "a decision, before anything is built". It needs rewording to a decision-of-record that amends #297 and states what already shipped.
This issue's scope was too narrow, and that is worth recording.#297 carries two decisions of record. The body above quotes one — Privacy — and every Done-When speaks only to privacy. As written, this issue could have closed with every box ticked while the Standard clause ("Sink is an OTel collector. No SaaS.") stayed standing and contradicted. That clause is the one a hosted product actually breaks.
Names who made it, and when — @blafourcade, 2026-08-31.
What an individual can see about their own data, and when relative to their manager — locally this holds by construction: the mapping and the identity are read from the person's own profile and nothing is uploaded. That stops being automatic the day a destination exists, which the record assigns to the destination's own work.
The deletion path, including what git history makes irreversible — not covered by the record. Still open.
Two of the five are unmet, so this stays open. Also stale in the body above: measurement is no longer only refusable per project — refuses per person and wins over a repository's committed switch, which answers the "the existing safeguard protects the wrong people" argument directly.
What an individual can see about their own data, and when relative to their manager
Clause 5 of the record. Structural while local — read from their own profile, no earlier reader because nothing is sent. Named as the destination's obligation once one exists, rather than promised on its behalf
The deletion path, including what git history makes irreversible
aidd telemetry forget. Shows every location and what is in each, removes only on confirmation, reports what went and what did not. History is reported at four honest strengths — committed / staged but never committed / not tracked and possibly held / no repository — because git ls-files reads the index, not history
Both bodies edited; neither still names it as an unresolved precondition
Two things this issue got wrong, recorded so the next one does not.
Its scope was half the problem: #297 carries two decisions of record and this body quoted one. As written it could have closed with every box ticked while the clause a hosted product actually breaks stayed contradicted.
And its factual premise was already stale. The journal names nobody — no line it writes carries any identity field. It does not enter git history — telemetry on git-ignores it. And "the safeguard protects the wrong people" is answered: AIDD_TELEMETRY=0 refuses per person and overrides a repository's committed switch.
Activity
blafourcade commentedon Aug 16, 2026
Decision, 2026-08-16 — support both, do not choose
Both modes are supported. The question stops being "anonymous or named" and becomes "how is one storage format made to serve both, and who decides which applies".
The shape that gives both from one format
Records always carry a pseudonym, never a name. A stable, per-person identifier with no meaning of its own.
The mapping from pseudonym to person is a separate artefact, held outside the repository, access-controlled, and optional.
One format, two modes. Switching means adding or removing a mapping rather than migrating data. #661 builds the mapping and now owns the guarantees around it.
The mode is a project decision, and the person is told
Set per project or per organisation, not per person: a mode a developer can flip individually makes any team figure meaningless. But the person is told which mode applies before anything is written about them, and can see their own records at any time. That is not a courtesy, it is what makes the measurement defensible.
Two traps, stated so nobody discovers them later
A stable pseudonym is not anonymity. Someone who is the only person committing to a given repository on a given day is identifiable by correlation, whatever the identifier says. Anonymous mode is honest protection against casual reading, not against a determined reader with access to the same repository. Claiming otherwise would be the kind of promise that ages badly — see #658 for what that costs.
The switch is asymmetric. Deleting the mapping anonymises what comes next and what is stored in the dashboard, but it cannot reach records already written into git history. Going the other way is impossible: a pseudonym never becomes a name retroactively. So the mode must be decided before the first record, and a change of mode has to state which direction it can and cannot reach.
What the journal actually adds beyond what git already reveals
Worth being precise, because it narrows the debate. With records materialised on commit (#620), the commit already names its author. The journal adds no new identity at that grain.
What it does add is how long the work took and what it cost. That is the genuinely new information about a person, and it is what this decision is really about — not who wrote the code, which git has always said.
Done When
[-]chore(framework): decide whether measurement is anonymous or named[/-][+]chore(framework): support both anonymous and named measurement[/+]blafourcade commentedon Aug 16, 2026
Deferred to milestone 16, 2026-08-16
The policy work — who sets the mode, what a person sees and when, the deletion path, the mapping itself — waits for the dashboard. Nobody needs it to produce a first figure.
Deferring is safe because two guards are in place from v1, and both are irreversible if missed:
user.emaillands on disk in the first week and the question is answered by accident.Together they mean no name is written anywhere before this issue is opened, so both modes stay reachable at zero cost. The decision keeps its full range instead of being narrowed by a default nobody chose.
blafourcade commentedon Aug 23, 2026
This issue says "Excludes: any implementation. This issue produces a decision and closes", and it blocks #656 and #661. That premise no longer holds: the mechanism shipped on the telemetry v1 branch (PR #706) before the decision was recorded.
What exists now:
person_idandperson_display_nameas stored-record fields, aPersonIdentityAdapter, and two skill actions (04-identify,05-forget).What the shipped default does, so the gap is legible: nobody is named unless a person opts in on their own machine. A missing file, a damaged one, and one carrying no
person_idall read the same way - nobody chose - and the file is read only from the OS user's own profile, with no environment override, so neither a repository nor a CI can supply an identity. That is compatible with #297's "anonymised ids".So the gap is authorization, not behaviour. This can no longer close as "a decision, before anything is built". It needs rewording to a decision-of-record that amends #297 and states what already shipped.
refactor(telemetry): a skill carries what it needs, and nothing reach…
docs(framework): amend #297 — a hosted destination is allowed, and ne…
blafourcade commentedon Aug 31, 2026
The decision this issue asks for is written, and it covers both clauses
aidd_docs/memory/internal/decisions/measurement-may-reach-a-hosted-destination.md— decided 2026-08-31 by @blafourcade, posted on #297.This issue's scope was too narrow, and that is worth recording. #297 carries two decisions of record. The body above quotes one — Privacy — and every Done-When speaks only to privacy. As written, this issue could have closed with every box ticked while the Standard clause ("Sink is an OTel collector. No SaaS.") stayed standing and contradicted. That clause is the one a hosted product actually breaks.
Against this issue's own conditions:
Two of the five are unmet, so this stays open. Also stale in the body above: measurement is no longer only refusable per project — refuses per person and wins over a repository's committed switch, which answers the "the existing safeguard protects the wrong people" argument directly.
feat(telemetry): resolve what a removal would touch, once
feat(telemetry): add the seventh command, aidd telemetry forget
feat(telemetry): let the skill answer a person who asks to be forgotten
docs(framework): the decision says what a person sees about themselve…
blafourcade commentedon Aug 31, 2026
All five conditions are met — closing
aidd telemetry forget. Shows every location and what is in each, removes only on confirmation, reports what went and what did not. History is reported at four honest strengths — committed / staged but never committed / not tracked and possibly held / no repository — becausegit ls-filesreads the index, not historyTwo things this issue got wrong, recorded so the next one does not.
Its scope was half the problem: #297 carries two decisions of record and this body quoted one. As written it could have closed with every box ticked while the clause a hosted product actually breaks stayed contradicted.
And its factual premise was already stale. The journal names nobody — no line it writes carries any identity field. It does not enter git history —
telemetry ongit-ignores it. And "the safeguard protects the wrong people" is answered:AIDD_TELEMETRY=0refuses per person and overrides a repository's committed switch.