Repository navigation
fix(telemetry): bind a branch only by the command's own declaration - #1045
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
A task set by hand without aiddDeclaredAt, or a declaration carried to another branch by git branch -c or -m, bound that branch in both the CLI and the hook. The command now stamps aiddDeclaredFor with the branch it declared on; both readers count a declaration only when aiddDeclaredAt is set and the stamp, when present, names the branch read. Unstamped declarations from earlier releases still read. A branch snapshot with no declaration time attributes nothing. forget removes the new key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
…every prompt The usage contract said the gate asks once per working branch; it holds every prompt until the branch is declared, as the spec that introduced it requires. The contract, the plugin README, the hook header and the shared fixture now state the branch declaration rule, the rename and legacy decisions, and the limits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
…s nothing Replaces a fallback to an empty list, whose only mutant was equivalent, and records the mutation and gate runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
git branch -C or -M onto an existing branch leaves each declaration key twice, and a plain git config set refuses such a key, so the command could no longer declare that branch. The store now replaces or unsets every value. Also from review round 1: the rename e2e checks a call made on the renamed branch, the architecture doc no longer says the gate asks once, the shared fixture keeps its formatting, and conditions made always true by the new rule are dropped. 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
…ndeclared git branch -C or -M onto a branch that already had declaration keys keeps both sets. Reading the last value of each mixed the copied declaration into the branch's own: a task or ticket the own declaration never set came from the copy, and its stamp passed. Both readers now treat any declaration key listed twice as no declaration; declaring again replaces every value. The shared fixture can now list keys in order, so both readers are pinned on it. 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
…edeclaration counts git branch -c, -m, -C and -M bring the source's reflog along, so the oldest entry is the source's creation. On a name used before, that made the earlier branch's declaration look newer than one made after the copy, and the report kept it while status and the gate showed the new one. The start is now the newest entry where the branch took its name, else its oldest entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Kills the one mutant left on a changed line, and records round 3's gates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
libgit2 writes "branch: renamed" in lower case. The reflog subject is now matched without regard to case, so such a rename starts the branch too. Also from review round 4: the limit for calls on a reused name before it is declared again, and stale comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
…back to the older Picks the generation over every snapshot before keeping only declared ones, so a reused name whose only snapshot was hand-set attributes nothing instead of the earlier branch's task. Also from review round 5: the rename of an unstamped declaration is stated as a limit, and the task contract lists every limit. 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎯 What & why
A task and a ticket are meant to come only from
aidd telemetry task. In practice a branch also got bound by a task key set by hand, or by a declaration thatgit branch -c/-mcarried over from another branch. This happened in both the CLI and the hook. The usage contract also said the prompt gate "asks once per working branch", which is not what it does: it holds every prompt until the branch is declared.🛠️ How it works
branch.<name>.aiddDeclaredFor = <name>, just beforeaiddDeclaredAt, which is still written last (git-branch-binding-store-adapter.ts:26). It replaces every value of a key, because-C/-Monto an existing branch leaves keys listed twice.aiddDeclaredAtis set, the stamp (if present and non-empty) names the branch being read, and no declaration key is listed twice. The CLI applies this in branch-binding.ts:69, the hook in git.cjs:88. Both are pinned by the shared fixturescripts/__tests__/fixtures/telemetry-bindings/, which can now list keys in order.git branch -m A Bleaves B undeclared, and the gate asks once on B. git moves the keys the same way for a copy and a rename, so the stamp cannot tell the two apart. Nothing is lost: B now starts when it took its name, read from the newestrenamed/copied … to refs/heads/Breflog entry (git or libgit2) in branch-binding.ts:122. Without that, a copy onto a reused name kept the old declaration in the report.declared_atattributes nothing. Its generation is still chosen over every snapshot (attribution.ts:113).🧪 How to verify
cd cli && npx vitest run --project=e2e tests/e2e/telemetry-report-declaration.e2e.test.tscovers copy, hand-set, rename,-Conto a declared branch, reused names and legacy.node --test scripts/__tests__/aidd-telemetry-hooks.test.jsaidd_docs/tasks/2026_10/2026_10_16_declaration-comes-only-from-the-command/evidence.md. Telemetry mutation score is 96.4; every mutant on changed lines is killed.pnpm testrun had 2 failures that were not captured. They did not recur in three later runs.review.md.🔗 Linked issue
None. Found by the R1 telemetry audit (B1, M8).
✅ I certify
🤖 Generated with Claude Code
https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp