Repository navigation
ci: promote next to main - #1038
Merged
Merged
Conversation
Keep generated project instructions concise and context-aware. Prefer high-signal bullets, remove test-only greeting and full-memory fallback, and batch work only when it saves time or context.
aidd-qa-v1.0.0 shipped; the pin would make every later release PR propose 1.0.0 again. Claude-Session: https://claude.ai/code/session_01WuxKN5bm96LFhU97rsgBUW AIDD-Session-Id: 6d6ccc35-f6c1-417d-84a6-9a0b70474e6b Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…933) Bumps the npm-dev-dependencies group with 2 updates: [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) and [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional). Updates `@commitlint/cli` from 21.2.2 to 21.2.3 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.3/@commitlint/cli) Updates `@commitlint/config-conventional` from 21.2.2 to 21.2.3 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.3/@commitlint/config-conventional) --- updated-dependencies: - dependency-name: "@commitlint/cli" dependency-version: 21.2.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: "@commitlint/config-conventional" dependency-version: 21.2.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the github-actions group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.38.1 to 4.38.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@1c5b675...2892aa5) Updates `github/codeql-action/autobuild` from 4.38.1 to 4.38.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@1c5b675...2892aa5) Updates `github/codeql-action/analyze` from 4.38.1 to 4.38.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@1c5b675...2892aa5) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.38.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action/autobuild dependency-version: 4.38.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.38.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.8.0 to 1.9.0. - [Release notes](https://github.com/squirrelchat/smol-toml/releases) - [Commits](squirrelchat/smol-toml@v1.8.0...v1.9.0) --- updated-dependencies: - dependency-name: smol-toml dependency-version: 1.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fix(aidd-dev): validate all CI failures before repushing
docs(aidd-context): shorten repeated file citations
Prioritize recommendations and surface only what the reader needs to understand or act. Keep instructions concise and offer compaction only after addressing frustration.
Bumps [simple-git](https://github.com/steveukx/git-js/tree/HEAD/simple-git) from 3.36.0 to 4.0.2. - [Release notes](https://github.com/steveukx/git-js/releases) - [Changelog](https://github.com/steveukx/git-js/blob/main/simple-git/CHANGELOG.md) - [Commits](https://github.com/steveukx/git-js/commits/simple-git@4.0.2/simple-git) --- updated-dependencies: - dependency-name: simple-git dependency-version: 4.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
* fix(cli): render setup constructor validation errors Keep unsupported user-scope combinations inside the command error boundary so intentional refusals print remedies without a stack trace. Document current registration and installation scope limits. Refs #891 AIDD-Session-Id: 01a111bd-4959-7be1-9ad4-093a46ef8295 * docs(cli): record independent setup validation review Record the approved review and outcome challenge for #891, mark the plan reviewed, and clarify that dependency construction assertions apply to the setup action. AIDD-Session-Id: 01a111bd-4959-7be1-9ad4-093a46ef8295 * docs(cli): keep the user-scope table to what setup refuses The table carried a second subject — plugin install file scope — and a closing paragraph that restated it, then recited what the change does not do. Both are out of what #891 asks, which is the refusal and the state already shipped. It also stated the support list by hand next to a --help list generated from supportsUserScopeActivation. Two sources for one fact, and only one of them can drift. The table now says the minimum and points at the generated one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(framework): a path the reader creates is not a dead reference `referenced-paths` anchors on whatever top-level entry is on disk, so `.aidd/` becomes an anchor the moment a contributor runs the CLI in this checkout. From then on the two pages documenting `.aidd/config.json` — the telemetry opt-in switch a repository commits to turn measurement on — were refused for naming a file this repository deliberately does not hold. Its absence is the state those pages describe. The checker now skips that path where it skips a command or a placeholder. The test states the case with the anchor present, so it is red on a clean clone too, where the old gate passed by accident. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * docs(framework): deleting a remote branch is the one push to skip the gates on `git push --delete` runs the pre-push gates over a push that carries no commit: 41 seconds of the cli suite to remove a branch. Three measurements say no fix is reachable from inside the hook — lefthook forwards git pre-push stdin to no command, the hook environment is byte-identical to a normal push, and a glob filter still matches. Owning the git hook file to read that stdin is a structural change for a rare operation, so the limit is written down instead, inside the paragraph that already rules on --no-verify rather than beside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…irectory (#968) The sandbox PATH drops every directory holding a drivable tool binary, which is right for the tool and wrong for node: npm installs `codex` into the bin directory of the node that ran npm, so the one directory the sandbox cannot do without is also one it would drop. On a machine with codex installed that way the sandbox reached no node at all, and the pre-push suite failed on the guard that exists to catch exactly that — a sandbox reaching neither node nor git would make the tool guard pass by breaking everything it exercises. The needed binaries are now linked into one directory placed first on that PATH, so the filter costs a tool its reachability and never costs the sandbox node, git or gh. POSIX only: linking node.exe needs a privilege a Windows runner may not have, and the npm layout behind this puts a .cmd shim beside node there. The new guard builds the crowded directory itself, since no runner can be relied on to have one. It is red on the old code for the reason it names. AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat(orchestrator): the sdlc flow pauses when asked It ran autonomously or not at all. A refactor kept the autonomy and dropped the rest, so a person who wanted to see the contract, the plan and the verdict before anything shipped had no way to ask. `interactive` beside the source now stops the run at each artifact that can still be corrected, and a refusal there is a finding routed back to the zone that owns it. `auto` stays the default and is unchanged. The pauses are the orchestrator's own behaviour, written once in its router: no called skill changes. The planning step already carried both branches and switches on what the router declares. Check gains one rule, because the flow could not reach a person at all when a strict checker never cleared: a finding that recurs after a pass acted on it stops being routed and becomes the user's to decide. The ship recipe committed and then asked for a request on a branch nothing had pushed. It passes the commit skill its push option now. Verified by eighty-five headless runs over sixty-four decision edges, then forty-four more measuring dispersion rather than presence. Twelve defects in the router's own paragraph were found and closed, five of them only by running it. What the runs could not settle is recorded beside the plan. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(orchestrator): each zone commits what it wrote, and the request gate becomes reachable Four defects the case battery surfaced in zones the interactive mode did not own. Each left the mode promising something the flow could not keep. Frame's spec and Check's review had no committer, so a request could open without the contract it was built from or the review that cleared it. Each zone now commits what it wrote before handing over. Whether an end to end journey was required rested on a file no zone reference points at, so a run that obeyed the reading rule skipped it. The project's testing memory decides, and nothing else does. `planning-ready` decided whether a spec is written, and therefore how often a person is consulted, with no test behind it. Three tests now hold it: the source names the artifact to change, states the observable outcome, and leaves open no decision that changes what is built. The request gate read `no actionable finding`, and a strict checker always had one, so two earlier runs looped four times each and never reached it. Once the review has cleared, only a finding that contradicts the contract reopens a zone; any other is carried onto the request as a note. A pass budget was rejected: the number would be arbitrary and indefensible. Eleven headless runs, one per claim and one per branch of each claim. The direct branch held on the same request that twenty earlier runs had settled there, so the third test is not stricter than the flow it describes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * refactor(orchestrator): the sdlc router says the same in fewer words The mode sentence names one condition instead of four, the stop rule moves next to the zone reading it, and the diagram carries each zone's pause so the flow and the prose cannot drift apart. The onboard tier note no longer names a skill from another plugin; it states the rule any autonomous skill follows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(orchestrator): the journey and the planning-ready test are read, not judged The planning-ready test becomes its own sentence, so a zone can answer it instead of inferring it from a clause. The journey runs when the plan calls for one and never otherwise, which moves the decision to the artifact that can state it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(orchestrator): a carried note never trips the recurrence stop The two sentences governing a recurring finding both claimed it: a cosmetic finding a pass had already acted on was a non-blocking note under one and a blocking stop under the other. Three independent readers answered that the text did not decide. The reopening rule now runs first, and the recurrence rule speaks of a finding that would be routed a second time, so a note never reaches it. Four readers re-traced both paths on the corrected text and none answered undecidable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(orchestrator): an approval carries its own corrections A Codex run of the interactive flow answered two of its three gates with "approved, with corrections". The text had no reading for that: a correction is a finding against the artifact, so the letter of it sent a one-line fix back through the zone and presented the artifact again. The run advanced instead, which is the useful behaviour and not what the text said. Three readers traced the three answers a pause can get — approved, approved with corrections, refused — and none answered undecidable. The run itself, including the three gates exercised with a person at them, is recorded beside the batch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * docs(orchestrator): record the codex runs of both modes The interactive gates had never been executed, only read: a headless run cannot answer them without inventing the approval the gate exists to collect. Two Codex runs close that, one per mode, on the same issue from the same commit. Both reached the same fix. The gates added no quality on it, and the person at the first one made it worse, so the comparison is recorded as measured rather than as the result the design expected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * docs(orchestrator): the user-facing pages name both modes again #910 asks that documentation explain both modes and their default, and the review had stripped every mention. The description, the plugin table and the README row say autonomous or supervised, and the catalogs follow from the description. The word a request carries stays `interactive`, which the argument hint already names. The README row names it too, so the word a reader meets is the word they can type. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * refactor(orchestrator): the mode rules live in their own reference `## Behavior` had grown from 84 words to 313, almost all of it the mode: nine sentences, each one a run that failed without it. Compressing them would have traded a proven case for a shorter page — an earlier pass already lost the plural in "every wait a step takes" that way and let brainstorm skip its own pause. So they move whole into `references/mode.md`, a section per mode, and the router keeps the dispatch alone at 77 words. One redundancy went with the structure: inside a section named Interactive, "dispatch in interactive" says nothing. Three readers starting from SKILL.md alone found the reference and applied it: the five mode cases, the three pauses in order, the zero pauses of auto with its three exceptions, and where a refused outcome routes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * refactor(orchestrator): the mode reference states one rule per line Four paragraphs of run-earned sentences read as prose when every one of them is a constraint to check. They become bulleted rules under Behavior, Auto, Interactive and Refusal, so a reader meets one rule at a time. Two rules went in the process. "Dispatch only the steps … and run the rest as `## Auto` says" pointed at a section of its own file, which reads as nothing: it now says that everything else runs without pausing. And the opening pair, naming the three pauses then saying to present and wait, was one rule written twice. Five readers confirm the format did not turn rules into a procedure: the five mode cases, the three pauses in order, no bullet contradicting another, an approval carrying a correction, and — step by step — that clarifying and formalizing are the contract pause rather than two more, while implementing and repairing pause for nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(orchestrator): the contract pauses once, on the finished artifact A supervised run whose source is not planning-ready waited four times, not three. Nothing said the contract pauses once: "only the steps that produce those three pause, clarifying and formalizing included" counts per step, "that artifact's pause" counts per artifact, and three readers walking that path answered four. Adding the spec sentence back only moved one of three — it was never what held the count. Two rules replace four. The count is now an invariant: three pauses, one per artifact, the producing steps sharing it. And the pause is on the finished artifact, so clarification questions are part of producing the contract rather than pauses of their own, and the formalized spec is what gets presented. The exceptions move above both modes, where they always belonged: as a rule of `auto` alone they contradicted "everything else runs without pausing" the moment Check withheld a twice-routed finding in a supervised run. Thirteen readers across the two paths: three on the clarify-then-spec walk, three on the contradiction, three confirming it closed, and the last three answering that nothing is left undecided — the first of the series to say so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * refactor(orchestrator): the check zone states one rule per line Eight sentences in one paragraph, three of them added by this branch, and two routings living in two files with nothing saying they were different things: a checker finding routes by what it is about, a refusal the user gives at a pause routes by artifact. A reader had to infer that from the word "finding" meaning two things. The zone now reads as rules under Behavior, Findings and Handoff, and Findings opens by saying which of the two it governs and where the other one lives. Three readers crossed the two on four cases — an implementation finding before the review clears, a contract refusal at the outcome pause, a contract contradiction after it cleared, and a plan refusal. All four landed in the same zone for all three readers, each through the right file, and none found the two routings in conflict. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * docs(orchestrator): record the supervised flow run with someone answering it Every earlier run was headless, so the three gates were only read: a headless run cannot answer a pause without inventing the approval the pause exists to collect. This one stopped at each pause, reported what it presented, and continued from a real answer — approve, approve with a correction, refuse, refuse again. The source was deliberately not planning-ready, so the clarify then formalize path ran. That path had never been counted: every headless interactive run used a planning-ready source and went through Frame directly, which is why the pause counting defect survived 38 of them and surfaced only when asked about. Five pauses on three artifacts, two step waits correctly held not to be pauses, ten commits, seventeen tests green, no pull request. The run also volunteered two faults of its own conduct — probe files outside the sandbox, and a mutation measured against a reverted tree — neither of which the reviewer had caught. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…lsewhere (#969) * fix(aidd-pm): give spec gaps one home under open questions The TBD marker had a form but no place, so two runs on the same feature could put the same gap in different sections. Gaps are now listed once under a required Open Questions section, and a residual question makes the spec invalid. The new criterion weighs nothing, so the pass threshold is unchanged. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * docs(aidd-pm): record the plan behind the spec open questions fix The Deliver zone wrote this plan and its phase, so it is committed with the work it describes. It carries the backlog link to #626 and the five decisions the implementation was not free to re-make, including the two the ticket got wrong: SKILL.md needs no change, and the validator gains no weighted criterion. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(aidd-pm): scope the spec marker rule to every other section and let refine clear entries Review of a4d185a found two gaps. First, the rule and both test rows said "no required section carries a marker". Context and Stakeholders are optional, so a gap listed under Open Questions and copied into Context passed every check while reproducing the second run measured in #626. The rule now reads "every other section", in tbd-marker.md and in both test rows. Second, refine only listed what was still unanswered. With the marker no longer inside its field, rewriting a field no longer deleted it, so one stale entry could invalidate a spec whose content was complete. Step 4 now removes answered entries first and writes None when none remain, with a test row. Also drops the placeholder bullet from the spec template and the criterion's self-description from the validator. Weights and threshold are unchanged. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * docs(aidd-pm): name the gap once in the spec open questions plan A checker found the plan carrying a false reason: check-doc-duplication.js scans only docs, the two memory banks and the READMEs, so it could never have failed a sentence shared inside a skill. The decision stands on one fact, one home; the reason did not. The same round found one noun written four ways across the skill, the broadest of which now blocks validation. The plan fixes the unit: a gap is an unresolved decision a required section depends on, and what no required section depends on is not a gap. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(aidd-pm): define a spec gap once and make the validator criterion spec-wide Finding 1: the spec skill named one unit four ways (unresolved decision, gap, missing required field, field still unanswered). tbd-marker.md now defines a gap once, as an unresolved decision a required section depends on, excluding what no required section depends on and implementation detail. 01-build, 02-refine, the template and the validator point at it. Finding 2: the open_questions criterion now states that no TBD marker appears outside Open Questions anywhere in the spec, Context included, so an evaluator reading the yml alone has one reading. Weights and pass_threshold are unchanged. Also: 02-refine removes an entry only once its decision is fully answered, and rewrites it to the remaining question otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * docs(aidd-pm): record the spec open questions journey and review The validator now refuses both cases phase-1 named and never ran: a residual entry, and a None section with a marker under Context. Placement and the measured case hold. The thin-request runs failed their criterion. Two of three still ask which fields the CSV must carry, which no required section depends on. The unit bounds where a gap is written, not which questions are raised. Also repairs a sentence in tbd-marker.md that read "nowhere else, anywhere in the spec". Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(aidd-pm): cut the spec gap rules to the words that change the draft The heading was hardcoded in five files. The template owns it now, and nothing else names it. Cutting the text also closed the over-ask the previous evidence caught: on the same thin request, drafts went from 5, 4 and 6 entries with CSV field lists and refusal wording to 2, 4 and 2 with none. The rule did not change, only its length. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(aidd-pm): say the refine gap step once It said answered twice and rewrite twice. Three clauses, three behaviours, no word spent on a second pass. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * refactor(aidd-pm): cut the spec actions to the words that change the draft The gap rules were lean and the text around them was not, so the skill still read as mostly prose. 01-build loses a quarter of its words, 02-refine and the template guidance the rest: the restated sub-bullet, the parenthetical listing the optional sections the template already marks, the three example verbs, and the two paragraphs explaining the backlog declaration. Every rule is kept. A fresh context given the shortened step 6 still writes backlog-link.json for a named ticket, writes none for no ticket, and leaves an existing file untouched for a different one. A fourth draft of the thin request listed three gaps, none of them presentation detail. This goes past the issue, on the user asking for the existing text to be cut too. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(aidd-pm): put back the three clauses the cut should have kept A checker measured what the last cut cost, and two of the three were real. Refine lost "None when none remains", so resolving the last gap left an empty section 3 times out of 3, and the validator refuses that. The validator lost the definition of a gap, so an evaluator reading the yml alone supplied its own and refused a spec whose content was complete. Refine never linked the template, so on a spec carrying no such section it invented the heading in lower case 3 times out of 3. All three now pass 3 of 3. 01-build gets back what written_by holds, which its own test row requires. The journey no longer claims the cut caused the shorter drafts: that cut changed several clauses at once, so length was never isolated. It reports what is measured instead, including 0 markers outside the section across 22 drafts and None on a fully answered request 3 times out of 3. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * docs(aidd-pm): record what the installed tree does, and what it refuses Eight sessions reading SKILL.md from a translated sandbox and following it. Placement holds: the section exists 8 of 8, no marker outside it 8 of 8. Zero does not: a request answering every required field still lists entries 3 of 3, and the new hard threshold then refuses that spec. The pasted-text runs were optimistic. They measured None 3 of 3 where the router measures 0 of 3, because the router reads more files and asks more. Also records why the first installed-skill attempt measured nothing: the Skill tool resolves to the user-scope install whatever a project declares, and a project marketplace does not register in a headless session. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(aidd-pm): a gap written elsewhere refuses the spec, a gap still open does not Measured through the installed tree: a request answering every required field still lists 3, 2 and 2 entries, always the column set and when the seven days start. Neither is a decision a required section depends on. So the threshold added here refused specs where nothing was missing, and before this branch an extra question cost nothing. open_questions_unresolved becomes open_questions_outside_their_section. A listed gap is valid, a gap written in any other section is not, which is the defect the issue measured. Driving to zero moves to the template as the intention it is, and to #625 as the only thing that will enforce it. Also closes four record findings: the plan claimed one home for a definition the validator must repeat, phase-1 still framed the rule on required sections, 01-build had lost what a backlog item is and the one-support rule, and two files named two authorities for one check. Measured, three runs each: a listed gap valid 3/3, a gap under Context invalid 3/3, None valid 3/3. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * refactor(aidd-pm): the spec skill keeps only the words that change a draft The backlog declaration was a contract inside a step, so it moves to its own reference and the step becomes one line. Four duplications go with it: the template already gives headers, bullets and a non-goals section, so the transversal rule saying to was redundant; the drop-implementation-detail clause is already transversal; the validator repeated the plan boundary its own hard threshold carries; and two files named two authorities for one section check. The assets lose their padding. The template keeps one placeholder bullet per list instead of two and states each rule once. The validator reads as a checklist again, every criterion one line, arithmetic untouched: required 90, total 100, pass_threshold 90, five hard thresholds. 1379 words to 1050, with a section and a reference added since. Measured on the rebuilt tree: every required section present 4 of 4, the section exists 4 of 4, no marker outside it 4 of 4, and the three validator cases unchanged at 2 of 2 each. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * refactor(aidd-pm): the backlog declaration shape becomes an asset The JSON was a scaffold inside a reference, so it moves to assets/backlog-link-template.json beside the spec template, and the reference keeps only the rules. The guard that pins the taught field names follows it. Tested through the tree, which the declaration never had been and refine never had at all: a named ticket writes the file with all three fields, no ticket writes none, an existing file for another item is left untouched, and refine drops a resolved entry then reads None on the last. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * fix(aidd-pm): a section says it is optional in its guidance, not in its heading The template shipped `## Stakeholders (optional)`, and a drafted spec carried the parenthesis into its own heading. Optionality belongs to the template that teaches it, not to the artefact. Tested through the tree: no drafted spec carries it, 6 runs out of 6. Two paths nothing had exercised, both green. From a PRD, 3 runs out of 3 carry every required section and lift the PRD own open question as the one gap. On a second model, 3 runs out of 3 place every gap in the section, which settles that the over-asking is not one model habit: 6, 7 and 7 entries against 6 to 8. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * test(cli): the taught backlog example can be an asset, not only a fenced block CI caught what no local gate could: the spec skill ships its backlog-link shape as an asset now, and this guard extracted the first fenced json block out of the action, which no longer has one. It read null and failed, exactly as a guard should. The loader accepts both forms, the asset bytes or the fenced block, so each skill still feeds its own example through the real adapter. Worth noting: the pre-push gate globs cli/**, so a plugins-only change runs no cli test locally, while cli-ci.yml runs them. That is the gap this slipped through. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c * test(cli): drop the comments the code already says The loader docblock restated its own body, and the header docblock explained which skill ships which form, which the two paths say. One line stays: why the real adapter and not a stand-in parser, the only thing the code cannot show. Refs #626 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…974) ## 🎯 What & why A cli test reads plugin markdown that no gate watches, so a change confined to those plugin files could land it red on `next`. The structural assertions move to the guard whose glob already covers `plugins/**`. ## 🛠️ How it works [`a-backlog-link-the-reader-can-read.test.js`](scripts/__tests__/a-backlog-link-the-reader-can-read.test.js) checked that the three field names appeared as substrings. It parses the taught example now, and requires each field as a non-empty string — the same predicate the reader applies at [`task-backlog-adapter.ts:12`](cli/src/contexts/telemetry/infrastructure/task-backlog-adapter.ts#L12). A taught value that is not a plain object is refused by the predicate [`json-file.ts:5`](cli/src/kernel/reading/json-file.ts#L5) uses, so nothing the reader accepts is rejected here. **The fence rule took three attempts, and the history is worth one line.** Requiring exactly one json fence failed a case nobody has produced. Selecting the fence by content then left this guard green on a decoy above the example while [its `cli/` sibling](cli/tests/contexts/telemetry/infrastructure/task-backlog-skill-shape.integration.test.ts) went red — reopening the very harm this issue names, since no gate runs that sibling on `plugins/**`. What ships copies the sibling's own pattern, `g` added for `matchAll`, so the two cannot disagree about which fence they read. **One case stays green on both, knowingly.** A valid decoy carrying `backlog` at line start, above a broken taught example, is read as the example by both guards. They agree, so nothing lands red on `next`, and closing it here alone would make this guard red where the sibling is green. Recorded in the plan's Decisions. ## 🧪 How to verify - Empty `written_by` in `plugins/aidd-dev/skills/01-plan/actions/04-plan.md`, stage that file alone, and run `pnpm exec lefthook run pre-commit`: `scripts-tests` exits 1. Do not pass `--all-files --force`, which bypasses the globs and so proves nothing about the local gate. - Mutations, each verified against both guards: a line-start fence above the example fails on both; a fence opened after text on the same line is ignored by both; the fence markers stripped, `null`, `[1]`, and an emptied field each fail naming the file, the shape or the field. - `node scripts/check-tests-leave-git-alone.js -- node --test 'scripts/__tests__/**/*.test.js'` → 550 pass. ##⚠️ Heads-up - The deferred gap is #973: the sibling pins the placeholder value and demands key-set equality, both stronger than the reader, and a plugins-only edit to either still lands it red on `next`. - [`review.md`](aidd_docs/tasks/2026_10/2026_10_07_backlog-shape-guard/review.md) in the task folder is round 1's record and reads `changes-requested` against a head three rounds old. Rounds 2 to 4 each found one item, each fixed here; the last returned no contract violation. - Four commit bodies on this branch assert rules the tree no longer has — the one-fence rule, content selection, a renamed-asset mutation that proved another test, and "no number exists to declare yet". This description is the squash message; do not let GitHub concatenate them. ## 🔗 Linked issue Closes #972 ## ✅ 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](https://claude.com/claude-code) https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z
* fix(opencode): load hooks and telemetry in both plugin runtimes OpenCode V2 requires a default definition and emits a different event lifecycle. Keep the V1 server entrypoint while adapting measured V2 tool and execution events. Closes #953 AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): record completed OpenCode compatibility validation AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * fix(opencode): share runtime adapter across plugin delivery paths AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): record shared runtime delivery and final validation AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): record independent OpenCode review and challenge AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * fix(cli): verify Kilo hooks and repair mutation asset loading AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * test(cli): cover Kilo parser and session lifecycle mutations AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * test(cli): assert final Kilo hook counts after shutdown AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): record independent Kilo verification review AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * test(cli): refresh Kilo bridge golden after lifecycle support AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): verify corrected Kilo golden baseline AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * fix(cli): keep embedded configuration bytes stable on Windows AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): record Windows checkout verification AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * test(cli): remove code interpolation from Kilo probes AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * refactor(cli): remove obsolete Kilo parser and stale hook comments AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): shorten documentation and add concise Claude rules Preserve operational contracts, verification commands and Kanban metadata while removing repetition and transient snapshots. Refs #953. AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): keep architecture focused on boundaries Link implementation protocols to canonical sources instead of duplicating them in the framework overview. Refs #953. AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): clarify architecture and telemetry for all readers Explain core concepts visually and keep operational safeguards visible while linking detailed implementation contracts. Refs #953. AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): organize documentation by section purpose Keep responsibilities, usage, interpretation and safeguards in their own sections while preserving linked anchors and contracts. Refs #953. AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): consolidate concise documentation rules Keep the same documentation safeguards in fewer instructions. Refs #953. AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): explain architecture from marketplace to components Give each plugin component a clear role, keep adapter details with the CLI, and correct conflicting public documentation. Refs #953. AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * docs(framework): prefer architecture diagrams and tables Keep the architecture focused on stable relationships and responsibilities, with concise prose and preserved safeguards. Refs #953. AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3 * fix(cli): repair runtime files for current plugins Refs #953. AIDD-Session-Id: 01a11696-08a4-78b1-b05c-c456b9fbceb3
docs(aidd-context): reduce cognitive load in agents instructions
One phase of a 7-phase plan, one diff, three models: `100% (6/6)` on 7 blocks, `100% (6/6)` on 1 block, `53% (16/34)` where 16/34 is 47%, and boxes copied from the previous run. The skill never said what a phase outside the diff becomes, how the percent is computed, or where a past round lives. A review now appends one section per round and is never rewritten. A round carries its date, its author from `git config user.name`, its diff, the axes that ran, a verdict and a score; its `Criteria` list holds the plan's criteria and its `Findings` list the defects no criterion covers. The score counts over the plan's whole criteria total, in three numbers that sum to it, so a plan covered at a third can no longer read as finished. The skill follows `skill-generate`'s authoring contract: the router holds the flow, the five actions and two transversal rules, and nothing else. `01-prepare` resolves the diff once for the three axes and opens the round; `05-finalize` judges it and checks its shape. `references/report-contract.md` holds where the report lives, what diff is reviewed and how a round joins it. An action says what to decide; the template owns the names and the form, the validator the field set. The skill reads 1583 words, against 1785 on `next`. Severity reads `critical`, `major`, `minor`: one scale, where `warning` put a log level between two gravities. Three files outside this skill still say `warning`. A critical is the harm a merge would do to production, its users, their data or what depends on it, never how strictly a rule is worded. Before, a broken declared rule read critical in 7 of 12 runs and a crash in production blocked in 3 of 5; now 0 of 10 and 5 of 5, over 50 runs on ten defect fixtures with every verdict unanimous. Measured, each with the mutation that proves it: 31 assertions, 39 mutations, each reddening one assertion alone. The assertions spell a path the way the documents spell it, so Windows and macOS read the same tree. 71 real runs on the final text: 50 of 50 functional runs over 10 fixtures scored right, including 10 phases and 34 criteria reading `3/34 met, 5 unmet, 26 out of the diff`; 3 of 3 runs without a plan read `not scored`; 33 of 33 planted defects found across the three axes; no run patched a file. Against `next` on the same fixtures, counting checked boxes against the truth: 6 wrong runs of 18. Four rules come from those campaigns, each turning a split between runs into unanimity: a context line of the diff is evidence, a criterion whose subject is absent is a gap rather than `not-applicable`, a phase projecting no file is traced rather than out of the diff, and a label echoing the criterion's words is not evidence. Refs #858 Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z AIDD-Session-Id: bf713ca4-3c1b-491d-ac0a-4d4533330b5c Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#995) On `next`, a criterion only a run could show was checked from the code alone: "submitting the form displays a success banner" came out `[x]` citing `src/signup.js:3` in 9 runs of 9, including one where a recorded run had seen that banner fail. A review stays static. A criterion whose subject is in the diff but whose outcome only a run could show, what a screen displays, a timing, an outside system's answer, is now unverified at runtime: named in the round with what a run would show, never checked from the code, and never blocking the verdict. A run recorded beside the plan on the reviewed head decides it instead: passing checks it, citing the record; failing leaves it unchecked; a record from another head leaves it unverified. The score gains a fourth number, so met, unmet, unverified and out of the diff sum to the plan's whole criteria total. This replaces #977's "a not-applicable criterion counts as met": such a criterion is now unverified, and `not-applicable` is gone from the skill. Measured with `claude -p` against the plugin from this tree, 103 runs: - 85 functional runs: every fixture scored as expected, including six traps: a record naming a branch, a working-tree edit after the run, a stale record covering a criterion the code proves, a blocked run, a mixed criterion, and a backend criterion that must stay checked. - 18 all-axes runs: verdicts unchanged (approve, blocked, then changes-requested), no unverified line on criteria the code proves. A round whose every criterion is unverified approves with `0/N met`; its score says so. Refs #976 Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…i/next/simple-git-4.0.2 chore(deps): bump simple-git from 3.36.0 to 4.0.2 in /cli
…er (#912) chore(deps-dev): bump the npm-dev-dependencies group across 1 directory with 11 updates Bumps the npm-dev-dependencies group with 11 updates in the /cli directory: | Package | From | To | | --- | --- | --- | | [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `2.5.11` | `2.5.15` | | [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `21.2.2` | `21.2.3` | | [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) | `21.2.2` | `21.2.3` | | [@stryker-mutator/core](https://github.com/stryker-mutator/stryker-js/tree/HEAD/packages/core) | `9.6.1` | `10.0.0` | | [@stryker-mutator/vitest-runner](https://github.com/stryker-mutator/stryker-js/tree/HEAD/packages/vitest-runner) | `9.6.1` | `10.0.0` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.0` | `26.6.4` | | [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `3.2.6` | `5.0.3` | | [fast-check](https://github.com/dubzzz/fast-check/tree/HEAD/packages/fast-check) | `4.9.0` | `4.10.2` | | [jscpd](https://github.com/kucherenko/jscpd/tree/HEAD/rust/jscpd) | `5.0.16` | `5.4.0` | | [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) | `6.33.0` | `6.39.0` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `3.2.6` | `5.0.3` | Updates `@biomejs/biome` from 2.5.11 to 2.5.15 - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.15/packages/@biomejs/biome) Updates `@commitlint/cli` from 21.2.2 to 21.2.3 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.3/@commitlint/cli) Updates `@commitlint/config-conventional` from 21.2.2 to 21.2.3 - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.3/@commitlint/config-conventional) Updates `@stryker-mutator/core` from 9.6.1 to 10.0.0 - [Release notes](https://github.com/stryker-mutator/stryker-js/releases) - [Changelog](https://github.com/stryker-mutator/stryker-js/blob/master/packages/core/CHANGELOG.md) - [Commits](https://github.com/stryker-mutator/stryker-js/commits/v10.0.0/packages/core) Updates `@stryker-mutator/vitest-runner` from 9.6.1 to 10.0.0 - [Release notes](https://github.com/stryker-mutator/stryker-js/releases) - [Changelog](https://github.com/stryker-mutator/stryker-js/blob/master/packages/vitest-runner/CHANGELOG.md) - [Commits](https://github.com/stryker-mutator/stryker-js/commits/v10.0.0/packages/vitest-runner) Updates `@types/node` from 26.4.0 to 26.6.4 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@vitest/coverage-v8` from 3.2.6 to 5.0.3 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.3/packages/coverage-v8) Updates `fast-check` from 4.9.0 to 4.10.2 - [Release notes](https://github.com/dubzzz/fast-check/releases) - [Changelog](https://github.com/dubzzz/fast-check/blob/main/packages/fast-check/CHANGELOG.md) - [Commits](https://github.com/dubzzz/fast-check/commits/v4.10.2/packages/fast-check) Updates `jscpd` from 5.0.16 to 5.4.0 - [Release notes](https://github.com/kucherenko/jscpd/releases) - [Commits](https://github.com/kucherenko/jscpd/commits/v5.4.0/rust/jscpd) Updates `knip` from 6.33.0 to 6.39.0 - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.39.0/packages/knip) Updates `vitest` from 3.2.6 to 5.0.3 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.3/packages/vitest) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.5.14 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: "@commitlint/cli" dependency-version: 21.2.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: "@commitlint/config-conventional" dependency-version: 21.2.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: "@stryker-mutator/core" dependency-version: 10.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: npm-dev-dependencies - dependency-name: "@stryker-mutator/vitest-runner" dependency-version: 10.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: npm-dev-dependencies - dependency-name: "@types/node" dependency-version: 26.6.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-dev-dependencies - dependency-name: "@vitest/coverage-v8" dependency-version: 5.0.1 dependency-type: direct:development update-type: version-update:semver-major dependency-group: npm-dev-dependencies - dependency-name: fast-check dependency-version: 4.10.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-dev-dependencies - dependency-name: jscpd dependency-version: 5.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-dev-dependencies - dependency-name: knip dependency-version: 6.37.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-dev-dependencies - dependency-name: vitest dependency-version: 5.0.1 dependency-type: direct:development update-type: version-update:semver-major dependency-group: npm-dev-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Baptiste LAFOURCADE <baptiste.lafourcade@gmail.com>
…1000) * test(cli): run the Biome guards on Windows `biome-guards-bite.arch.test.ts` spawns `node_modules/.bin/biome` directly. Windows reaches Biome through a `.cmd` shim, so the launch reportedly fails with ENOENT (#881), and the Windows job never runs the architecture suite, so nothing here has shown it. The Windows job now runs that one file. Refs #881 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * fix(cli): launch Biome through node in the architecture guards On the Windows runner the Biome guards failed with `expected [] to include 'src/kernel/exit.ts:plugin'` and two more like it: spawning `node_modules/.bin/biome` directly found no executable there, and the empty output read as a tree with no findings. The guards now run the package's own launcher, `@biomejs/biome/bin/biome`, through `process.execPath`, which works on every platform. A run that is neither clean (exit 0) nor diagnosed (exit 1 with a finding) throws instead of returning nothing, so a guard asserting that no finding exists can no longer pass when Biome never ran. A test with a missing launcher was red before this. Closes #881 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * fix(cli): read Biome's Windows paths in the architecture guards With Biome launched, the Windows guards still failed with `expected [ …(5) ] to include 'src/kernel/exit.ts:plugin'`. Biome found the five planted findings it finds elsewhere, but names a file with backslashes there, so no key matched. The parser now turns the separators into forward slashes, under a test fed a Windows line. Refs #881 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * chore(ci): name the Windows hook steps by what they do A CI step name, an error message and a test comment carried the number of the issue that led to them. Each now says what it checks, and the issue numbers stay in the commits and pull requests that fixed them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * docs(framework): keep tracker IDs out of every file, not only docs The documentation rule already dropped ticket and run IDs, but only for the Markdown its paths name, so code, comments, test names and CI steps carried them. A rule with no paths now covers every file: tracker IDs live in commits, pull requests and issues, and the thing is named by what it does. The documentation rule no longer repeats the clause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * chore(ci): run the whole architecture suite on Windows The Windows job ran one file of the architecture suite, the Biome guards. It now runs `pnpm test:arch`, the same suite the Linux architecture job runs, so every architecture guard is exercised on Windows, the Biome guards among them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * docs(framework): reduce the tracker-ID rule to one bullet The rule now reads as a single bullet, and the documentation rule keeps its own clause unchanged: its paths only reach the documentation, so the two do not overlap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * chore(framework): check out text files with LF on every platform Run on Windows, the architecture suite failed in three files that read text by its lines: `codebase-map.md has no fenced block`, `expected [] to include 'knip'`, and a SyntaxError loading `mutation-covers-source`. A Windows checkout turns LF into CRLF, which the repository's scripts and tests do not expect. `.gitattributes` now asks for LF on every platform, `text=auto` leaving binary files alone. No committed file changes: the only CRLF files are archived evidence whose own `.gitattributes` already mark them `-text`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * fix(cli): resolve both sides of a Biome finding's path in the guards After the merge with #912, the Windows guards still failed with `expected [ …(5) ] to include 'src/kernel/exit.ts:plugin'`: Biome found the five planted findings, and none of their paths matched. Only the planted tree went through realpathSync, so a file Biome named through another spelling of the same directory could not be made relative to it. Each file now resolves through realpathSync too, and a failed match prints the findings it saw. Refs #881 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * fix(cli): decode the file the Biome GitHub reporter escapes With the findings now printed, the Windows guards failed on `ENOENT ... lstat '...\aidd-biome-bite-BHHWDT\C%3A'`. On Windows Biome reports an absolute file, and the GitHub reporter escapes its drive colon as `%3A`, so the path resolved under the planted tree instead of to the file. The guard decodes the reporter's `%3A`, `%2C` and `%25` before resolving the path, under a test that was red first. Refs #881 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Normalize the memory directory prefix before building README links on Windows. Add regression coverage and isolate hook tests from inherited CLAUDE_PROJECT_DIR. Closes #986
* docs(aidd-context): refresh cook recipes Keep recipe steps actionable, enforce valid heading nesting, and preserve verified token and MCP guidance. * docs(aidd-context): streamline token optimization recipe Separate durable agent instructions from scoped rules and keep each optimization technique directly actionable. * feat(aidd-context): validate technical recipes Keep bundled guidance concise, reproducible, and mechanically verifiable. * chore(cli): migrate Biome configuration Keep lint settings compatible with the installed Biome schema.
* fix(cli): preserve exact mutation filtering with vitest 5 * test(cli): assert recipe and plugin removal contracts * test(cli): preserve tracked files and unconfirmed native refs
…1005) A run of table rows with no separator under the Actions table was read as the table resumed after a blank line, so a glossary there could donate a citation and hide a deleted row. A blank line ends a table, as every renderer reads it: such a run now draws one refusal at its first line, and cites nothing. Orthogonality keeps reading through fences while router coherence does not, on purpose and now said in the code: a fenced command is still one the model runs. A test pins it. R8 says that two actions sharing a slug keep their number, the form the guard already cites. The split-table unit test and the property test's split shapes now expect that single refusal. Every router in the tree still passes. Closes #884 Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs(aidd-context): let rule generation cover OpenCode V1 The rule contract excluded OpenCode V1, yet the contribution it writes for OpenCode is the root `AGENTS.md`, and OpenCode V1's rules documentation reads that file: "Place an AGENTS.md in your project root for project-specific rules", with config `instructions` only combined with it. The OpenCode row now covers V1 and V2, and the V1 documentation joins the checked contracts. This rests on the documentation: no OpenCode 1.x session has been run against a generated rule. Closes #913 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * docs(aidd-context): name OpenCode once, with no version and no date Both OpenCode versions read the root `AGENTS.md`, so the row and the sentence under the table name OpenCode alone, and the checked-contracts line loses its date. The tracker-ID rule now also keeps dates out, and covers docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z * docs(aidd-context): state the OpenCode target as one instruction The sentence listing what OpenCode does not read named three formats a generator could pick back up. It now says only where to write and the one wrong target the contract used to name, `.opencode/rules`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE16AxC1V9wAMGmGgefe4z --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
framework, tools and kernel run in 4 legs, telemetry in 3, tools-codex and translate in 2, cut deterministically by file weight. A scoring job merges the legs' reports and applies the same score and floor as one run, and fails on a missing, drifted or empty leg. CLI CI: 27.5 to about 14 min warm, 45.3 to 27.7 cold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Remove the twelve high-severity advisory paths with compatible patch floors while keeping excluded dependencies and quality thresholds unchanged. Refs #1008
Validate the qs and esbuild parent-range exceptions without migrating their consumers. Account for the patched compiler emitting TypeScript fields correctly in the measured bundle budget. Refs #469
…1019) Bumps the npm-dev-dependencies group with 2 updates: [js-yaml](https://github.com/nodeca/js-yaml) and [lefthook](https://github.com/evilmartians/lefthook). Updates `js-yaml` from 5.4.2 to 5.4.3 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](nodeca/js-yaml@5.4.2...5.4.3) Updates `lefthook` from 2.1.14 to 2.2.1 - [Release notes](https://github.com/evilmartians/lefthook/releases) - [Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md) - [Commits](evilmartians/lefthook@v2.1.14...v2.2.1) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 5.4.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: lefthook dependency-version: 2.2.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-dev-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…984) Make parallel task execution discoverable and retain blocked or failed agents in the final recap.
…3 updates (#1024) chore(deps-dev): bump the npm-dev-dependencies group Bumps the npm-dev-dependencies group in /kanban with 3 updates: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `@biomejs/biome` from 2.5.14 to 2.5.15 - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.15/packages/@biomejs/biome) Updates `@types/node` from 26.6.2 to 26.6.5 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `vitest` from 5.0.2 to 5.0.3 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.3/packages/vitest) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.5.15 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: "@types/node" dependency-version: 26.6.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: vitest dependency-version: 5.0.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…pdates (#1020) chore(deps-dev): bump the npm-dev-dependencies group Bumps the npm-dev-dependencies group in /cli with 3 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [jscpd](https://github.com/kucherenko/jscpd/tree/HEAD/rust/jscpd) and [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip). Updates `@types/node` from 26.6.4 to 26.6.5 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `jscpd` from 5.4.0 to 5.4.1 - [Release notes](https://github.com/kucherenko/jscpd/releases) - [Commits](https://github.com/kucherenko/jscpd/commits/v5.4.1/rust/jscpd) Updates `knip` from 6.39.0 to 6.41.0 - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.41.0/packages/knip) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.6.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: jscpd dependency-version: 5.4.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-dev-dependencies - dependency-name: knip dependency-version: 6.41.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-dev-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the github-actions group with 4 updates: [actions/setup-node](https://github.com/actions/setup-node), [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/autobuild](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action). Updates `actions/setup-node` from 7.0.0 to 7.1.0 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@8207627...949feb2) Updates `github/codeql-action/init` from 4.38.2 to 4.38.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@2892aa5...24c5418) Updates `github/codeql-action/autobuild` from 4.38.2 to 4.38.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@2892aa5...24c5418) Updates `github/codeql-action/analyze` from 4.38.2 to 4.38.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@2892aa5...24c5418) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: github/codeql-action/init dependency-version: 4.38.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action/autobuild dependency-version: 4.38.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.38.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@inquirer/prompts](https://github.com/SBoudrias/Inquirer.js) from 8.7.2 to 8.7.3. - [Release notes](https://github.com/SBoudrias/Inquirer.js/releases) - [Commits](https://github.com/SBoudrias/Inquirer.js/compare/@inquirer/prompts@8.7.2...@inquirer/prompts@8.7.3) --- updated-dependencies: - dependency-name: "@inquirer/prompts" dependency-version: 8.7.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.9.0 to 1.9.1. - [Release notes](https://github.com/squirrelchat/smol-toml/releases) - [Commits](squirrelchat/smol-toml@v1.9.0...v1.9.1) --- updated-dependencies: - dependency-name: smol-toml dependency-version: 1.9.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… 1 (#993) * docs(telemetry): frame deterministic token measurement, release 1 The contract for replacing the previous telemetry: every billed Claude Code call counted once, broken down by person, session, model, period, repository, task and ticket, the task coming only from a declaration. Carries the measurements, the common usage record and the prototype readers with their mutation-proved fixtures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp AIDD-Session-Id: 2c21d903-3a7e-47ac-83f8-d8b7ae3aa579 * docs(telemetry): plan deterministic token measurement in nine phases Removal of the previous telemetry first, then count, store, declare, ask, attribute and report, opt-in and cleanup, and the end-to-end proof. Pins the consent key, the binding snapshots, the hook guards and a mutation per rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp AIDD-Session-Id: 2c21d903-3a7e-47ac-83f8-d8b7ae3aa579 * refactor(telemetry)!: remove the previous telemetry The previous measurement is replaced by a deterministic, declaration-attributed one in the following commits of this branch. This commit removes the old one so nothing half-describes the new one. cli: delete the telemetry context, its wiring, command, displays and tests, and everything that existed only to serve it: the measurement vocabulary, the per-tool telemetry declarations and transcript locations, the git adapter that installed the commit-trailer hook, the identity and sink resolvers, the FileWriter chmodExecutable method and the run-journal gitignore entry. The OpenCode hooks bridge no longer names the deleted plugin module in its generated comments; the framework build golden is re-baselined for that one line. Architecture guards, biome overrides, mutation scope, smoke steps and the help golden describe the CLI that remains. plugins: delete the aidd-telemetry hooks and skills (README reduced to a stub, manifest description updated). Remove the backlog-link.json step, reference and template from aidd-pm:04-spec and aidd-dev:01-plan, the backlog-link paragraph from the SDLC frame, and every aidd:step-end echo with the SDLC, async-dev and backlog "Say when this orchestration is over" sections. scripts and CI: delete the telemetry script tests, their fixtures, the two telemetry scripts, the generated prompts documentation and its lefthook job, the prepare-commit-msg trailer job, the journal replay, telemetry check and telemetry e2e exclusion in cli-ci.yml, and the identifier-join probe. Tests that used the telemetry plugin as a byte fixture read aidd-context instead. docs: delete the cost-report and metrics contracts and aidd_docs/runs/, rewrite every memory, README and docs line that claimed the previous telemetry, and mark the pending telemetry task folders of 2026_09 as superseded. The previous version's on-disk locations are recorded in aidd_docs/tasks/2026_10/2026_10_09_deterministic-tokens/context/previous-version-leftovers.md for the opt-in and forget phases. BREAKING CHANGE: the aidd telemetry commands, the aidd-telemetry hooks and skills, backlog-link.json and the AIDD-Session-Id commit trailer are removed; measurement returns, for Claude Code first, later on this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): count each billed Claude Code call once Read Claude Code transcripts into one usage record per billed model call. - key message.id:requestId, or message.id:sessionId:timestamp without a request id - drop the synthetic model; isSidechain or agentId makes the call a subagent - cache_write and cache_write_1h are summed over the message iterations; the one-hour part is unknown when any iteration lacks its split - each advisor iteration is its own record, keyed <key>#advisor<n> - an unrecognised shape is reported and never guessed; an unknown counter is null, never 0 - global fold across every file: largest total, then earliest time, then lowest session id, independent of input order - source: recursive walk of the projects root including subagents and *.jsonl.superseded-* set-asides, byte-offset reads, an unterminated last line is left unconsumed, a shrunk or replaced file is re-read whole Re-adds the telemetry context to the architecture guards (public modules, biome layer overrides, codebase map, mutation scope, tool-name baseline). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): store usage in a local ledger, one record per call Add `aidd telemetry ingest`: read what every Claude Code transcript gained and store each billed call of the projects that opted in. - ledger: <telemetry dir>/ledger/<YYYY-MM>.jsonl by the record's own UTC time; a changed partition is rewritten by temp file and rename, so a better snapshot of a call replaces the held one even across months and no reader meets two live records or a torn line - idempotent: held records fold first, so an equal record changes nothing; the phase 3 fold decides every replacement, in any order - lock: ledger/.lock holds pid and creation time, created exclusively; a dead pid or a lock older than ten minutes is cleared, a live recent one is waited for and released only by its owner - repository: git rev-parse per working directory, id = sha256 of the normalised host/owner/repo of origin (https, ssh, scp-like, credentials, port and .git dropped), else the lowest root commit; a linked worktree resolves to its main repository - consent: .aidd/config.json telemetry.enabled with version 2; the previous bare enabled is not consent; an unparseable file is counted; the main working tree answers for a worktree without its own file; AIDD_TELEMETRY=0 refuses before anything is read or written - roots.json remembers each directory seen alive, so lines from a deleted one still resolve; one never seen alive is counted, not stored - bindings/branches.jsonl snapshots branch.<name>.aiddTask|Ticket|DeclaredAt with the branch's creation time from its reflog, only when it changed - write order under the lock: ledger, roots, snapshots, offsets last - owner-only storage: 0700/0600 on POSIX, icacls on Windows - coverage counters in the ingest result, by reason not stored Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): declare the task a branch or session works on aidd telemetry task <name> [--ticket <ref>] | --none binds the running Claude session from now on and, on a working branch, the branch itself through its git config, then snapshots the declaration at once. The default branch and a detached head are left untouched. A project that has not opted in to version 2, or AIDD_TELEMETRY=0, is refused. With no argument the command shows what the work is bound to and where that comes from. The format the CLI writes and the plugin hooks read is pinned by one shared fixture under scripts/__tests__/fixtures/telemetry-bindings/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(aidd-telemetry): ask for the task once per branch, before any token Three Claude-only hooks, inert unless the project opted in: a prompt gate that answers a typed declaration without the model and asks once on an unbound working branch when a person is present, a session-start that keeps the task across /clear and /branch, and an async catch-up that runs the ingest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): report usage on eight axes, attributed by declaration aidd telemetry report reads new transcripts, then splits what the stored calls consumed along total, person, session, model, day (UTC), repository, task or ticket, four counters apart, every axis summing to the same totals. An unknown counter is left out of its sum and counted, never added as zero. Attribution is one pure function: the session's declaration at the call's time, else its carry, else the branch's declaration from the snapshots (from the branch's creation, so declaring late moves earlier work), else unattributed with the reason. The first declaration in a carried session applies to the whole session. Sub-agent and advisor calls follow their session. aidd telemetry identity <id> writes {person_id} to identity.json in the telemetry directory, owner-only; --off removes it. Nothing is carried over from the previous version's file. Ingest also reports when the oldest transcript still on disk was last written, for the coverage line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): keep a reused branch name's earlier work on its task Attribution read only the latest snapshot of a repository and branch name, so a branch deleted and created again under the same name took the older generation's calls to the new task, or to none. Snapshots are now read as generations told apart by branch_created_at: a call belongs to the youngest generation created strictly before it, and within that generation to the latest snapshot, so declaring late still moves its earlier work. A snapshot with no creation time speaks only when no dated generation matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): opt in, opt out and forget, leaving no trace of the old `on` writes consent at version 2, removes the previous version's commit hook line with its script, run journal and ignore entry, and resets the ledger offsets and remembered consent so earlier history is stored once. `off` switches off and keeps the version. `forget` previews, and with --yes erases both versions' files and declaration keys under the lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): add init and usage skills and the usage contract Two thin skills over the CLI: 00-init opts in after consent, sets or removes the identity and forgets with a preview; 01-usage maps a question to report flags and reads the JSON without computing a figure. A test checks every command they name against the help golden. The usage contract, the plugin README and the CLI memory describe the record, the version 1 envelope, the stores and the ask and carry rules. Stale mentions of the replaced measurement go, the smoke harness covers the telemetry commands, and the bundle budget is set to the measured build plus 2 %. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): drive the whole journey on the built binary Opt in, get asked on an unbound branch, declare by typing it, clear and keep the task, run a headless session, then check that every report axis adds up to the total and lands on the declared task and ticket. The real hooks run through an aidd shim, a .cmd on Windows and a node script elsewhere, with no AI tool binary on PATH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): record the real-history oracle, equal on every day The built CLI and the prototype's independent reader counted the same projected history: 2,746 files, 262,328 records, 68 days, no day differing. The usage record's branch comment now says what attribution does with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): resolve the release 1 review findings C1: partitioning is linear, an ingest rewrites only the months that changed, declarations and snapshots take a bindings lock and never wait on the ledger lock. C2: on lifts a remembered refusal for every directory of the repository. W1: ordinary prompts that look like a declaration pass the gate. W2: a branch redeclaration applies from its own time. W3: consent is the clone's git config aidd.telemetry, never committed. W4: cmd.exe /s command line, and the hook home matches the CLI's. W5: mutation proofs recorded. Minor: stale context docs, superseded old plan, report wording, dead attribution reasons, V1 config block removal, forget wording, next step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): record the mutation proofs of every phase Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): consent is read live from the clone, never lifted by id Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): say so when a typed declaration is not understood Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): ingest repairs a month that holds a torn line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): read the branch config inside the bindings lock Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): each lock names itself when it times out Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): show the total axis row once Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): word consent as the clone's git config everywhere Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): drop the run journal words and record fix round 2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): pin the clone consent rules against surviving mutants Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): off withdraws what the clone's deleted directories were remembered as Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): re-run the round 2 mutations at the final head Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): store a call only if its clone consented at that time A call is stored only if its directory was seen alive in clone X, X is the clone answering now (or the remembered X once it is gone), and X's consent covered the call's time. A clone is its git common dir's dev, ino, birth time and path; consent is a history of intervals kept beside the ledger, opened by on, closed by off or by an ingest that finds the key changed or the clone gone. roots.json becomes format 2: one entry per directory and clone, no consented flag. forget names only gone clones that had consented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): say how to send a mistyped declaration as a prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): record fix round 3 and reword a stale target line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): source the per-OS identity notes and correct round 3 The identity comment now cites the Node Stats documentation and the libuv sources it was read from, and drops a claim about FAT and network shares that no source gave. The proofs file states what was observed: the damaged-month mutation at its stated scope and without the new assertion, and row 12 at the old code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(aidd-telemetry): say that forget also ends measurement Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(aidd-telemetry): ask only where an interval is open for the clone A clone consents for the hooks only when its key is 2:<token>, the consent log holds an open interval with that token, and that interval was recorded for the clone's real path. A key set by hand, a cp -R copy, a clone made again or a moved one asks nothing and blocks nothing. The hooks also close: when a hook passes the Claude-only guard in a clone whose key no longer names an open interval of its path, it appends {token, close} to consents.jsonl, whoever is present. The shared fixture pins the log format and both decisions for the CLI and the hooks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(cli): store a call only if its clone consented at that time Measurement starts at on and reads nothing back. Consent is a key 2:<token> in the clone's git config and an interval in ledger/consents.jsonl ({token, clone, open} and {token, close}). A call is stored when its directory was seen alive in a clone that had an open interval at the call's time. on mints the token and opens the interval, off and an ingest that sees the key stop naming it close it, and a damaged consent log refuses every clone. on no longer resets offsets. task and show apply the same test as the hooks. The bundle budget goes to 667 KB (653.3 KB measured). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): the consent rule, and fix round 4 The usage contract states the three-clause rule first: measurement starts at on, a call is stored only inside an open interval of its clone, and nothing else decides. plan.md keeps the previous decision, marked superseded, and adds the user's. mutation-proofs.md records the scenario table before and after, and the mutations with their red counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): record the round 4 mutation score and survivors Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(cli): a damaged log, a copy, a clone out of reach stay safe On and off refuse over a damaged consent log, and the advice is forget then on. The hook compares the full clone identity. An unstattable clone is unreadable, a clone out of reach keeps its interval, a broken git config is unreadable, and a closed consent is told apart from a never-opted clone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(cli): let the journey prove a call right after on is stored Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): record the fix round 5 mutations Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): mark the plan implemented, the presence probe open Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(cli): smoke off before forget, which now unsets the key Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(cli): word no-consent as no consent at that time, fix the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): a torn last line of the consent log is not damage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): bound the bindings lock wait for the hook's declaration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): date the oracle method, record the fix round 6 mutations Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): pass windows, codeql and the runtime mutation floor The first CI run of this branch found three gates red. - Windows: the posix cases of the shared fixture were joined with the host's separator, and one legacy-locations case compared a literal path with a joined one. Both now say which separator they mean. - CodeQL: four stat-then-open races. The hook's appender opens the file first and measures the descriptor; the test helpers read entry types from readdir instead of stat-ing each path. - Mutation (runtime) was 88.2 against a floor of 90: removing the git adapter took its killed mutants with it, and the Windows path of the private storage adapter had no coverage on a posix runner. The adapter takes its host (platform, environment, runner) so that path runs everywhere, and the case-insensitive file system check is pure. Local score 91.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): tell a birth from the change time to the nanosecond On Linux a birth time equal to the change time was taken as no birth, since libuv copies the change time when statx is refused. Compared in milliseconds, a real birth fell in the change time's millisecond for 1 git common dir in 80 freshly cloned or copied on ext4 (probed in a node:22 container); none was equal to the nanosecond. With the birth dropped, a copy moved onto a deleted clone's path was known only from ingest's first sighting, and a call made before that went to the old clone's open interval: the CI's "copy at the path" row stored it. The CLI and the hook now compare birthtimeNs with ctimeNs. The shared fixture carries both, plus a case of a birth in the change time's millisecond, which the millisecond rule fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): run the integration tier on windows - The sandbox starts from the native real path, so a call's directory and the clone git reports are spelt alike (no 8.3 short name). - Tests that lock a directory with chmod or stand in a shell-script git are posix-only; the mode check skips Windows, which has no execute bit. - Expected paths are joined, and the icacls target is the namespaced path mkdir returns on Windows. - A transcript path that is a directory is refused: Windows opens one and reports it empty. - The husky caller is named with / on every platform, as the repository names it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): root the erasure sandbox at the native real path The temporary-directory guard now compares against the long real path, so the erasure test, still rooted at the 8.3 short one on Windows, was refused before it ran. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): keep a birth equal to the change time as a birth On Linux the file clock is coarse: a git dir made and filled within one tick has its birth time equal to its change time, to the nanosecond. Taking that as no birth made a copy answer only from ingest's first sighting, so the row "copy at the path" stored a call it should not have (81 runs in 200 on ext4, 0 in 400 with this rule). Any positive birth time is now kept, by the CLI and the hook alike. Where Linux refuses statx, the birth time is the change time and the identity moves; the usage contract states what that leaves open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): off closes the interval its key names at its path Where Linux refuses statx, the birth time is the change time, so a clone's identity moves with each entry added to its git dir, the key write of `on` included. `off` closed only the interval of the identity it read, and a call made after it could be stored. It now also closes the open interval its key names when that interval was opened at the same path. A copy elsewhere carries the token, not the path, and is left alone. With statx refused, 0 of 36 consent rows store more, 3 runs; with statx, 36 of 36 pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * ci(telemetry): probe the session id join against the real tool The identifier-join job lost its probe with the previous telemetry. This one runs claude -p against a dead API and judges, through the shipped hook predicates, that the hook's payload, its environment and every transcript line name one session, that CLAUDE_PID is the hook's parent, and that a headless run never reads as a person present. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): supersede the old plans, record the latest proofs The pending telemetry plans of August are superseded by this one. The mutation proofs record the off fix and the probe, and the oracle note names the one guard added to the transcript reader since. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * ci(telemetry): judge CLAUDE_PID as the hook uses it, isolate the probe The hook takes CLAUDE_PID and falls back to its parent pid only when it is unset, so the probe now fails only when CLAUDE_PID is gone, not when a shell stands between Claude and the hook. Its git init no longer inherits GIT_* from a git hook, its temp dirs are removed, and the job runs when the probe or the hook predicates it uses change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): the hook's parent is not Claude on linux The identifier-join probe's first CI run showed a shell between Claude and its hooks on Ubuntu: CLAUDE_PID 2565, hook parent 2588. The parent pid only stands in when CLAUDE_PID is unset; the comment now says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(cli): a flat cursor agent keeps a dot inside its name Removing the previous telemetry's fields from the cursor profile took killed mutants with them, and the tools-cursor score fell to 96.8, under its 97 floor. The surviving mutant on the agent name's .md is real: an agent named read.md-first.md lost its inner .md. Five of the others are equivalent. Score now 97.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): ask only where a person sees the hook's message Observed with the maintainer: the terminal and the desktop app set ATTENDED=1 and show a block's reason; the VS Code extension sets no ATTENDED and shows neither a reason nor a systemMessage, so a block there would eat the prompt silently. The presence rule now names the two surfaces seen to show the reason, cli and claude-desktop, instead of any entrypoint not starting with sdk, which would block a surface never seen. Each condition has a mutation that turns its row red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): record the vs code deviation and the presence rule The spec asked every surface where a person can be present, the IDE extension included. The extension shows no hook message, so it is not asked: recorded as a deviation in the spec and the plan, and the README tells a VS Code user to declare from a terminal, which binds the working branch. Three documents still stated the sdk rule. The presence matrix now judges every row before failing, so each mutation names all the rows it turns red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): say where it asks in one table IDE extensions in general, not VS Code alone: the rule asks only the terminal and the desktop app, and VS Code is the extension observed. The README section becomes a table and two bullets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * refactor(telemetry): ingest reads every tool through one reader each Ingest held Claude's reader directly. A reader is now a tool's transcripts and its line format, and ingest reads a list of them: a second tool is its format, its source and one wiring line. Each reader hands back its own files' positions, laid over the ones held, so no tool undoes another's and a file no longer listed keeps its place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): status shows the clone's state, task only declares `aidd telemetry status` says whether this clone is measured, since when, and the task its work is bound to; not measured is an answer, exit 0. `task` with no name used to show that and now refuses, pointing at status, so each command does one thing. Bare `aidd telemetry` already prints the group's help. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): a typed declaration needs a name or --none With `task` declaring only, a bare `aidd telemetry task` typed as a prompt would have run the CLI to be refused. The hook now treats it as a declaration it does not understand and shows the usage, without running anything. The end-to-end checks of what work is bound to now go through `status`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): name status where commands are listed, say how to add a tool The privacy contract and the CLI memory listed the commands without status. The memory now says what adding a tool takes: its format, its source, one reader in the wiring and its name in USAGE_TOOLS. The two-reader ingest test gives the second reader another tool's name, and the not-measured lines all start the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(cli): smoke the status command The smoke script lists every command the binary has and fails on a drift; status was missing, and is now run after a declaration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): ask and confirm in one line, one way Tried in a real session: the ask was five lines, and the confirmation of a declaration showed the CLI's whole output inside Claude's "blocked by hook" frame, which read as still blocked. The ask is now one line naming the one way that always works, the declaration typed as the next prompt: a terminal's `!` runs whatever `aidd` its PATH holds, not the one the hook found. A declaration is confirmed in one line that says to send the prompt again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * perf(telemetry): do not read transcripts no consent can cover A first ingest after `on` read the whole Claude history and asked git the repository of every old call, though none could be stored: over 22 minutes on a 6.2 GB history, never finished. A transcript last written more than a day before the first consent interval opened is now not read, and its position is set to its last complete line, so what it gains later is read. The day absorbs a file clock that lags the call clock. Same history: 246 s for the first ingest, 1.1 s for the next. What is stored does not change: a test ingests the same transcripts with and without the skip and stores the same records, and on a real session the stored calls match an independent count (5 calls, 217,337 tokens). Pre-consent calls are no longer counted under a reason, and forget may leave a hand-set key in a clone that never ran `on`; the contract says both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): record the proofs of the unread transcripts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): nothing is read while no clone opted in Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): pin what decides which transcripts are read The earliest interval of any clone decides, a damaged consent log reads everything so each call keeps its reason, and a read that was not skipped says so. The first interval is now Math.min of every start, infinity when there is none, with no separate empty case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(cli): pin the telemetry help to its golden, and its failures The presentation mutation score fell to 94.0 once next's recipe validation joined this branch's telemetry commands, whose descriptions no test of that scope read. Each subcommand's help is now compared with the help golden, and four commands are shown to report a failure and exit 1. Score 95.1 against a floor of 95. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): say why nothing is read, and pin reading after a late on With no clone opted in, ingest reads no transcript, so every count is zero and the usage skill could not say why: it now names that case. A test chains no consent, `on`, then a grown transcript, and only the new line is read. The contract states a network file clock lagging by over a day and transcripts copied from another machine, and the hook's refusal of unsafe characters says ASCII. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): report by the agent that ran Every stored call already says whether the main session, a sub-agent or the advisor made it. `--axis agent` splits a period that way, and adds up to the same totals as every other axis. The envelope stays at version 1: an axis value is added, nothing renamed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(telemetry): a README to follow, in four steps and two tables Start in four steps, every command in one table, when it asks in another, then what it measures, privacy, skills and hooks in short bullets. Nothing it said before is lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(telemetry): one owner per fact, diagrams and tables over prose The README shows what happens at a high level, the contract keeps every rule in tables and diagrams (4,600 to 3,000 words, each of 274 rules checked against the original), and the code map points to both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(telemetry): the real claude, on three systems, billed by a fake API The report must equal what the API billed, call for call and counter for counter, on the declared task and ticket. The API is a local server whose usage is the expected answer, so no key and no quota are spent, and the job runs on Linux, Windows and macOS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): a person at a terminal is asked, answered and keeps the task The real claude runs in a pseudo-terminal against the fake API: the first prompt is blocked before any call, the declaration typed as a prompt is answered by the hook, and /clear carries the declared session. Each step waits on what it causes on disk or at the API, not on the screen. Linux and macOS; Windows has no POSIX pty and keeps the headless check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): the person's session on Windows too, over ConPTY The same relay through pywinpty, with an aidd.cmd the gate can find, so Windows is asked, answered and keeps the task across /clear like the rest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): answer ConPTY's cursor query so Claude renders on Windows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): answer the terminal identity query on Windows as well Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): keep the person's session to POSIX, Windows stays headless Under ConPTY on the runner Claude never got past its terminal setup, even with the cursor and identity queries answered. Windows keeps the headless check, which passes there. 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>
…t adds (#1031) * docs(telemetry): frame the ledger by day, with the window measured Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): plan the ledger by day in five phases Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * refactor(telemetry): a record belongs to a utc day, an ingest to a window Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * perf(telemetry): the ledger is kept by day, and an ingest reads its window One ingested call now loads, folds and writes only its calls' days and the day on each side, plus any monthly partition those days fall in, which it splits into days. Damage and duplicates in the window are rewritten with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): a report leaves the ledger settled, by day The report rewrites, folded, every partition that is damaged, monthly, or holds a call another one holds, so the bytes on disk are those of one ingest of the same calls whatever order the ingests ran in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): the contract and the code map say the ledger is by day Pins that forget removes daily and monthly partitions as the ledger, never as an earlier day file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): a day holding one call twice is unsettled too Kills the two mutants the telemetry run left in the ledger adapter: an unanchored monthly name, and a duplicate inside one day going unseen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): the ledger by day is implemented Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): a call's day never depends on the machine's time zone dayOf now gives no day for a time without an explicit zone (Z or +-hh:mm), or whose UTC day falls outside four-digit years. Such a call is counted undated at ingest, and such a ledger line is counted and dropped as not a record, instead of landing in a file DAILY never reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): the report says a damaged ledger line is dropped The report settles the ledger, so a line that is not a record is gone after it, as after an ingest. Both now say so in the same words. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * refactor(telemetry): a split month is found without a filter that changed nothing A day's key never equals a month's, so filtering names to monthly files before matching them against the touched months was redundant: the equivalent mutant the review saw survive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): the ledger's invariants say when they hold Once on disk and order-independent bytes hold after a report settles the ledger; an ingest reads its window plus a month it splits; save removes a named day it holds no records for rather than writing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): the contract records the re-measured before and after Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * test(telemetry): the settled-ledger property covers damage and re-ingest Adds torn lines in a monthly file and in a stray day, calls already held in a monthly file read again, 200 runs, an assertion that the bytes are the canonical partitionByDay(foldUsage(calls)) with each call once, and that a second settle finds nothing to do and changes nothing. Red when settle ignores damaged partitions or save keeps an emptied day. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * refactor(telemetry): a day's year bound has no check a test cannot break The four-digit check on the computed day was a ten-character regex on a ten-character slice, so its anchors were equivalent mutants; it is now a sign check. Reading a stored time goes through dayOf alone. Tests pin the leading anchor and a zoned time naming no real day. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): a stored time that is not text is refused again 7ff1dee read a stored time through dayOf(String(at)), so a time held in a one-element array coerced to a zoned string and was accepted with a non-text at. The text check is restored, superseding that commit's "goes through dayOf alone"; the new refusal case also kills its mutant, so the check is no longer equivalent. The zone pattern's closing anchor is kept and says why no test can observe it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): say when a time is usable, and keep the review rounds 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>
) * docs(telemetry): a report reads its period, the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * perf(telemetry): a report reads its period, not the whole history A report with --days, --from or --to now loads only the days of its period and the day on each side, plus any monthly file one of them falls in, and settles only what it read. A report with no period reads and settles the whole ledger, as before. The copies of one call are less than a day apart (0.3 h at most over 262,631 real keys), so a call with a copy in the period has every copy in the window and folds to the same record: a fast-check property checks the bounded envelope equals the full report filtered, byte for byte. UsageLedger.load takes a test on the day instead of a set of days, so a window costs the partitions on disk, not the length of the period. One year at 3,400 calls a day: --days 7 goes from about 4.2 s to 95 ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * refactor(telemetry): one day arithmetic, one failing storage, one autumn Review round 1: shiftDay in the stored usage domain replaces three copies of the day length and its arithmetic; the failing storage and the September to November days are shared test helpers. The usage skill's envelope reference, the usage contract and the task contract say what a bounded report reads, and evidence.md records the mutation run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): the window's bound is observed, not guaranteed Review round 2: say outside the observed bound only an unbounded report folds every copy, and that skipped_ledger_lines covers a month kept in one file. 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>
…1033) * docs(telemetry): contract for filtering the report Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): plan for filtering the report Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): a filter keeps the calls of the row it names 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 * feat(telemetry): the report narrows to what its filters name 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 * feat(telemetry): the report takes a filter per axis it can select on 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 * docs(telemetry): say what a report filter selects, and record the gates 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 * docs(telemetry): state only what the gate runs showed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): the report refuses filters on one axis itself 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 * fix(telemetry): one refusal for filters on one axis, and `.` alone 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 * docs(telemetry): record the approved review of the report filters 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>
* docs(telemetry): contract for choosing who is measured at opt-in Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): order the opt-in question before the use case writes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): plan for choosing who is measured at opt-in Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): on writes the identity chosen, and only once it is on The on use case takes who the clone is measured as: the identity already set, none, or one chosen. It refuses an identifier the identity rules refuse, and staying anonymous while an identity is set, before writing anything, and writes the identity only after consent is granted. A proposal is read from git's user.name, else user.email, never under AIDD_TELEMETRY=0. The command still passes keep: nothing changes yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * feat(telemetry): on asks who is measured, and status names them `aidd telemetry on` takes --as <id> or --anonymous. Interactively, with no identity set, it asks once the opt-in is confirmed: the git name when there is one, another identifier, or anonymous. --yes with no identity and neither flag is an error before anything is written, so scripts and tests now pass --anonymous. `on` and `status` say who the measurement names, and the text report labels nobody `anonymous`; the envelope is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): say what on asks, and how to answer it without a terminal The usage contract, the contributor map and the plugin README describe who is measured at `on`. The 00-init start action reads the identity, asks how the person appears when none is set, offering the git name, and runs `on --yes --as <id>` or `--anonymous`. Gate evidence recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): ask again for an identifier on cannot keep A typed identifier the identity rules refuse is asked again instead of ending `on` after the opt-in was confirmed. The refusal and the prompt say "one non-empty line of at most 128 characters", and the command prints the refusal through the display instead of a copy of its text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): the start action reads no git name, and probes on --help The 00-init skill asks the person to type an identifier or stay anonymous, runs `on --yes --as '<id>'` quoted or `--anonymous`, and says that `aidd telemetry on` in a terminal offers the git name: no name reaches the session before the person chooses. Its probe now requires `--anonymous` in `aidd telemetry on --help`. The start test table loses the row its two new rows cover. Contract rules 5 and 6 say what ships. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * fix(telemetry): say why a typed identifier is asked for again A refused typed identifier now prints the identity rule before the prompt repeats. The start action says how to keep a quote inside the single-quoted --as value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp * docs(telemetry): close the identity-at-opt-in task with its review The review's two rounds, the gates rerun on the tree merged with the report filters, and the contract and plan marked done. 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>
Comment on lines
+78
to
+86
| run = spawnSync(env.ComSpec || "cmd.exe", ["/d", "/s", "/c", cmdLine(resolved.shim, argv)], { | ||
| cwd, | ||
| env: cleanEnv(env), | ||
| encoding: "utf8", | ||
| windowsHide: true, | ||
| windowsVerbatimArguments: true, | ||
| timeout, | ||
| stdio: ["ignore", "pipe", "pipe"], | ||
| }); |
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.
Automated promotion of
nexttomain, from a snapshot of next taken at run 38873662585. Merged as a merge commit somainkeeps every conventional commit for release-please, and so both branches keep a shared merge base for the back-merge. Do not squash, do not rebase.