Skip to content

ci: promote next to main - #1103

Merged
aidd-bot[bot] merged 43 commits into
mainfrom
promote/next-to-main-40384291991
Oct 26, 2026
Merged

aidd-bot[bot] merged 43 commits into
mainfrom
promote/next-to-main-40384291991

Conversation

@aidd-bot

@aidd-bot aidd-bot Bot commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Automated promotion of next to main, from a snapshot of next taken at run 40384291991. Merged as a merge commit so main keeps 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.

aidd-bot Bot and others added 30 commits October 16, 2026 08:39
* feat(aidd-context): assess existing rules before writing

Compare complete candidate policies and applicable project guidance before publication. Require an explicit resolution and preserve the approved body through the existing writer.

Refs #791

* fix(aidd-context): include applicable claude project guidance

Include local Claude policies and conditionally loaded AGENTS sources in the discovery contract. Record a Claude-only conflict assessment that leaves every project byte unchanged.

Refs #791

* docs(aidd-context): record rule assessment review and evidence

Record independent review and outcome challenge, mark the plan reviewed, and preserve reproducible native receipts and fixture teardown evidence.

Refs #791

* docs(aidd-context): remove rule contract timestamp

* docs(aidd-context): record concise skill verification

* refactor(aidd-context): keep rule generation minimal

Keep delivery evidence local; retain plugin-source writing separately from host publication.

* refactor(aidd-context): remove duplicate publication guidance

* refactor(aidd-context): simplify rule assessment actions

* refactor(aidd-context): fold rule assessment into capture

* fix(aidd-context): preserve rule capture contracts

Keep category and workflow semantics lost during compression. Make assessed
approval, explicit artifact language and host applicability unambiguous.

Refs #791

* refactor(aidd-context): clarify rule generation handoffs

Keep routing in the skill and detailed contracts in existing references.
Give each action an explicit input and output without repeating the frame
or assessment fields in its process.

Refs #791

* fix(aidd-context): restore host selection discovery

Preserve installed-tool discovery when destinations are unspecified.
Shared AGENTS guidance alone cannot identify an installed host.

Refs #791

* fix(aidd-context): clarify capture decision order

Await missing destination confirmation before assessing candidates.
Give unresolved applicability priority over conflict resolution so
unknown Copilot interfaces cannot justify changing imported policies.
Scope that uncertainty to existing-policy comparisons rather than
requiring activation proof for future publications.

Refs #791

* docs(aidd-context): name rule generation action files

Resolve installed action filenames without guessing numeric prefixes.

Refs #791

* docs(aidd-context): trim rule generation references

Keep agent decisions and usable publication contracts concise while leaving
mechanical diagnostics with the existing writer.

Refs #791

* docs(aidd-context): restore concrete rule generation actions

Keep the workflow in actions and reusable contracts in references so the
agent can follow the process without chasing procedural redirects.

Refs #791

* docs(aidd-context): clarify review before resolution

Put clarification and reassessment before policy-change choices when
comparison evidence is missing; keep label definitions in the reference.

Refs #791

* docs(aidd-context): shorten rule actions and split responsibilities

Keep the action flow readable while loading assessment, discovery and publication contracts only when needed. Preserve approval and host-format safeguards.

Refs #791

* docs(aidd-context): align rule generation with the lean skill style

Give each action a one-line purpose and bold-led imperative steps, keep the approval gate in the router, and phrase rules positively. References stay self-contained.

Refs #791

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(aidd-context): plan and verify rule publication in the writer

Add --plan (dry run listing exact paths, same refusals) and --check --publish (drift report, non-zero on drift) to the standalone writer. Rule generation drops its validate action: capture plans before approval, write verifies after publishing.

Refs #791

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(aidd-context): a resolution is not an approval in rule generation

Present ends the turn, replans after any change and waits for a later explicit approval. The writer path is resolved absolute once, in capture.

Refs #791

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(aidd-context): define approval where rule generation writes

Write requires a user message that approves the displayed candidate unchanged; a choice, edit or rename is a resolution.

Refs #791

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(aidd-context): drop the redundant relative-path warning from write-rule

Refs #791

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(aidd-context): lean rule-generate wording and single approval rule

Drop redundant bold step labels and filler across the skill, state the
approval rule once in the transversal rules, show description, scope and
hosts in the presented candidate, and reuse the existing rule's hosts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* docs(telemetry): frame what status says after off

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): plan what status says after off

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): keep consentOf as the hooks' fixture pins it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): status after off says measurement was turned off

After `on` then `off`, status and task said the clone had never run
`on`. The key `off` that `off` writes now reads as a refusal of its
own, `turned-off`, worded with how to measure again. A clone that never
ran `on` keeps its words; no JSON carries the reason.

Closes #1035

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record the gates of status after off

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): say turned off only where on would not refuse

A clone turned off with a damaged consent log, or with no identity,
was told to run `aidd telemetry on`, which then refuses. It now reads
`unreadable-consent` there, and `turned-off` only once the log is read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record the review fixes of status after off

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>
…1043)

* docs(cli): contract for faster mutation and test runs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(cli): plan for faster mutation and test runs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(cli): a mutant runs its unit tests first, the smallest file first

Stryker stops a mutant at its first failing test, but Vitest ran the
integration project before the unit one and the longest file first, so
most mutants paid for slow integration files before the unit test that
kills them in milliseconds. The mutation config now orders files unit
before integration, then by size, then by path. Size is the same on every
machine and sandbox; the ordinary suite keeps Vitest's order.

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(cli): compute product values inside the tests that use them

Six telemetry test files called product code while Vitest collected them:
parsers at file level, repositoryIdOf, daysOf in an it.each table. Stryker
counts code reached outside a test as static and reruns every related test
for each of its mutants, so 172 mutants in task-declaration, stored-usage,
repository-identity and text-order ran up to 881 tests each. Each value is
now computed inside the tests that read it: static mutants 330 to 158,
test runs 54490 to 39327, no mutant status changed.

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(cli): split the telemetry report e2e file by subject

tests/e2e/telemetry-report.e2e.test.ts ran 22 tests in one file for 33 s,
the critical path of the whole suite, since Vitest runs files in
parallel, not describes. Its three describes are now three files, report,
report narrowed and identity, starting each test from the same sandbox in
telemetry-report-sandbox.ts. Same tests, same assertions.

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(cli): capture each golden cell twice, not three times

Both golden suites captured every cell twice to prove determinism, then a
third time to compare with the stored snapshot. The baseline test now
compares the first of the two captures, which are still taken one after
the other in two sandboxes, so a difference that depends on the time
still shows. A third of the capture work goes; no comparison is dropped.

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(cli): say how a mutation run orders tests and what makes a mutant static

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* build(cli): give a mutant three times its tests' time before a timeout

Under load, a survivor that runs all its covering tests (up to 37 s net)
crossed 1.5 x net + 20 s and was scored as a timeout, which counts as
detected: 12 survivors read Timeout in one sharded telemetry run. Since
unit tests run first, the only timeouts left are 26 real hangs, so a
factor of 3 costs about 360 worker-seconds and no survivor reads Timeout
in the run that measured it (6.7 min).

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(cli): record the evidence for faster mutation and test runs

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(cli): guard the sequencer class Vitest loads, sizes read from disk

Replacing sort with return files left every sequencer test green, and the
default statSync size was never exercised. The new test sorts two temp
files of known sizes through CheapestKillerFirst and fails on that change.
sort now takes any spec that has a project name and a module id, so the
test passes plain values instead of widening a type.

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(cli): share the two golden captures through one helper

Both golden suites carried the same block that takes two captures one
after the other and hands the first to the baseline test. It lives once
in tests/golden/two-captures.ts, given the capture to run.

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(cli): state the score and test-time criteria as measured

The baseline read 96.5 because load scored some survivors as timeouts;
counted alike on both sides, both score 96.33, and no baseline kill
survives. pnpm test gains only in the e2e subset and CPU, not above noise
in wall time. The evidence names the seventh load-hidden survivor, keeps
the flaky locator mutant apart, and gives framework's CI score before.

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(cli): review of faster mutation and test runs, round 1 fixed

Refs #1036

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(cli): review of faster mutation and test runs, round 2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(cli): record the maintainer's acceptance of the measured score

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>
…1045)

* docs(telemetry): frame what binds a branch and what the gate asks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): bind a branch only by the command's own declaration

A task set by hand without aiddDeclaredAt, or a declaration carried to
another branch by git branch -c or -m, bound that branch in both the CLI
and the hook. The command now stamps aiddDeclaredFor with the branch it
declared on; both readers count a declaration only when aiddDeclaredAt is
set and the stamp, when present, names the branch read. Unstamped
declarations from earlier releases still read. A branch snapshot with no
declaration time attributes nothing. forget removes the new key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): say what declares a branch, and that the gate holds every prompt

The usage contract said the gate asks once per working branch; it holds
every prompt until the branch is declared, as the spec that introduced it
requires. The contract, the plugin README, the hook header and the shared
fixture now state the branch declaration rule, the rename and legacy
decisions, and the limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* refactor(telemetry): say outright that a branch with no snapshot binds nothing

Replaces a fallback to an empty list, whose only mutant was equivalent,
and records the mutation and gate runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): declare again over a branch copied onto with -C

git branch -C or -M onto an existing branch leaves each declaration key
twice, and a plain git config set refuses such a key, so the command
could no longer declare that branch. The store now replaces or unsets
every value. Also from review round 1: the rename e2e checks a call made
on the renamed branch, the architecture doc no longer says the gate asks
once, the shared fixture keeps its formatting, and conditions made
always true by the new rule are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record review round 1 and the gates it reran

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): a branch whose declaration keys are listed twice is undeclared

git branch -C or -M onto a branch that already had declaration keys
keeps both sets. Reading the last value of each mixed the copied
declaration into the branch's own: a task or ticket the own declaration
never set came from the copy, and its stamp passed. Both readers now
treat any declaration key listed twice as no declaration; declaring
again replaces every value. The shared fixture can now list keys in
order, so both readers are pinned on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record review round 2 and the gates it reran

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): a branch starts when it takes its name, so a copy's redeclaration counts

git branch -c, -m, -C and -M bring the source's reflog along, so the
oldest entry is the source's creation. On a name used before, that made
the earlier branch's declaration look newer than one made after the
copy, and the report kept it while status and the gate showed the new
one. The start is now the newest entry where the branch took its name,
else its oldest entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): tell a reflog selector without its brace apart

Kills the one mutant left on a changed line, and records round 3's gates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): read a rename made through libgit2 as one made by git

libgit2 writes "branch: renamed" in lower case. The reflog subject is
now matched without regard to case, so such a rename starts the branch
too. Also from review round 4: the limit for calls on a reused name
before it is declared again, and stale comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): a newer generation no command declared does not fall back to the older

Picks the generation over every snapshot before keeping only declared
ones, so a reused name whose only snapshot was hand-set attributes
nothing instead of the earlier branch's task. Also from review round 5:
the rename of an unstamped declaration is stated as a limit, and the
task contract lists every limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record the approving review and widen the stated limits

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>
* refactor(telemetry): one path for the commands' wiring and filters

Each telemetry command repeated the same try, dependency wiring and
error handling; the nine filter flags were listed twice, by the command
and by the envelope. One helper each, same behaviour, room in the bundle
for the next change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): a ledger line no reader knows is kept, never deleted

A partition rewritten by settle or ingest dropped every line that was
not a record, so an older CLI erased a newer one's records and a torn
line vanished unsaid. A rewrite now keeps each such line in its day,
after the records, in text order and once; a monthly file's go to the
first day of its month. A day holding only such lines stays, and
skipped_ledger_lines counts them on every read.

The byte-identity property of a full settle now holds with such lines
present, and a second one checks that no ingest or settle, bounded or
not, ever loses one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* refactor(telemetry): say what was left out one way, shared by ingest and report

The ingest and the report each printed the shapes not recognised, the
calls not stored, a damaged consent log and the ledger lines kept, with
their own wording of the counts; both now print the same lines, so
`ingest` groups thousands as the report does and prints its snapshot
count before them. The identity rule and the consent-log recovery
sentence are written once; the four counters are listed once by the
report and its envelope; the commands share one wiring path; the
absent-path test is one function. Same output otherwise: room in the
bundle for the next change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* refactor(telemetry): name what a filter flag's help says it keeps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): what an ingest leaves out reaches every report

The catch-up hook runs `aidd telemetry ingest --quiet`, which printed
nothing and moved the offsets on, so the calls it did not store and the
lines that were no usage line were counted for that run only: a report
run later read nothing new and printed 0.

An ingest now keeps what it leaves out under ledger/left-out/: a call
with a day by its key, agent, day and reason, in the ledger's own form,
fold and settle; an undated call and a line that is no usage line as a
hash in no-day.txt. Nothing else of a call is kept, and no transcript
text. A report counts the left-out calls of its period that no read
stored since, and every entry with no day. Keyed, so a re-read or an
offsets reset counts nothing twice; a call is dated by its earliest
copy, so the bytes do not depend on how many ingests read it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): ingest prints a call not stored on stdout, as before

The shared left-out lines sent every one of them to stderr, so a call
not stored stopped reaching stdout. It is information again; a line
that could not be read stays a warning, each level now pinned by a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): concurrent ingests keep what they left out once

The ledger directory now holds left-out/; three ingests started
together leave one line in it for the one call not stored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): kill the mutants the left-out changes left alive

A line that is neither a call nor a shape not recognised (a prompt) is
never kept as dropped; two undated calls are two; a telemetry directory
that cannot be looked into fails instead of reading as empty. The days
a save writes need no order: every write comes before any removal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): plan, phases and evidence for nothing is lost silently

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): say what a left-out key holds, and what left-out costs

A call with no requestId is keyed by message id, session id and time,
so its left-out entry holds both: the contract, the usage contract and
the code now say so instead of "no session". The reach and the
unmeasured read cost of left-out/ are stated as limits, and the gate
evidence names the commit it was taken on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): keep only what was measured, and only by hash

Review round 1 found the left-out store kept the key of every call not
stored, a clone that never opted in included: the product rule is that
such a clone, or work after `off`, leaves nothing on disk.

What consent or scope excludes is no longer kept: it is counted by the
run that read it, as before. Only what was measured and cannot be
stored is kept, in ledger/left-out.txt, by a 32-hex hash: an undated
call of a clone that consents, and a line that is no usage line naming
such a clone's directory. Reports print it as coverage.left_out, the
same in every report; not_stored and unrecognised_shapes keep their
run-only meaning, so ingest and report count shapes alike. The store is
its own small adapter; the per-day left-out partitions, the earliest-copy
dating and the report-time matching are gone. Ingest groups `updated`
and picks each line's level from its type, not its text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): kill the mutants keeping what was measured left alive

A call made before its clone consented is not kept though it consents
now; an undated call naming no directory is never resolved, wherever
git would look; a line with an unusable time is judged at the read;
the kept counts say one and many. A line that gives a call names
nothing, so the reader's guard on it is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record round 1, its fixes, red runs and gates

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): read again a call whose consent cannot be read yet

Review round 2: a call judged unreadable-consent (git refusing the
.git once, a config that does not parse) was counted by the hook's
quiet run only, then the offsets moved on, so a repaired clone never
got it back and no report said it. That is not a no: ingest now keeps
the position of each transcript holding such a call from before the
read, so the next ingest, a report's included, reads it again; it is
stored once readable, and every run says it until then. Nothing is
written. A damaged consent log still moves on: forget is its way out.

A call of a directory gone before any ingest saw it alive stays a
stated loss: its consent is unknown and reading it again would never
end. Run-only report lines now say "by this read". The usage contract
quotes the current wording, names the undated exception in Consent,
and notes roots.json beside its row.

Bytes for it: the lock and the report destructure what they use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record round 2, its fixes, red runs and gates

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* chore(cli): raise the bundle budget to 709 KB for #1046

Measured 695.3 KB with the left-out store, kept ledger lines and the
bounded re-read; 2.0% headroom, recorded in the registry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): hold a transcript only while its clone may still consent

Review round 3: a directory git refuses for good (a repository owned by
another user that never opted in, an unmounted share) held every
transcript with one call there, re-read by each ingest without end.

A transcript is now held only when a clone roots.json remembers at that
directory consented at the call's time: repairing that clone ends the
hold. Anything else moves on and only that read says it. An undated
call and a line that is no usage line follow the same rule, so a
consenting clone's are kept once it can be read. A damaged consent log
moves on because it stays damaged until forget, not because forget
reads again. P3 now has a consenting clone git refuses until the last
ingest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): a kept undated call or line moves its transcript on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record round 3, its fixes, red runs and gates

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): a clone's directory left by git moves on, not held

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): close round 4: what ends a hold, the budget, 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>
* docs(telemetry): frame reading a transcript as a stream

The R1 telemetry audit (M3) found ingest reads a transcript's unread
tail whole. Contract, plan and phases for reading it a block at a time
with the same lines and positions, byte for byte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): read a transcript a block at a time

Ingest read a transcript's unread tail into one buffer and one string,
then one string per line, all alive at once: memory grew with the
tail, and a tail over 512 MB cannot be one string (inferred, not run).
The port now hands each complete line on as it is found, and the
adapter reads 1 MiB at a time from the handle it stat'd, up to the size
stat'd. Lines, positions and ledger bytes are the same as the
whole-tail reader's, which the tests keep as an oracle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): say a vanished or skipped transcript is not restarted

Two mutants on the lines the stream touched survived the telemetry
mutation run: `restarted: true` for a file that vanished and for one
skipped as old. Both are now asserted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record evidence for reading a transcript as a stream

Red runs, the hand mutation behind each guard, peak memory before and
after on generated transcripts, the mutation score and every gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): prove the remaining stream guards and that S2 stores

A hand mutation for the empty-line, offset-past-size and empty-file
guards, and counts showing the ingest property stores calls and holds
positions. The audit's transcript sizes are attributed to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record review round 1 of reading a transcript as a stream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): count nothing of a transcript changed under its read

A read now spans many block reads, so a transcript truncated or
rewritten in place between two of them could hand over a line spliced
from two versions and save an offset inside the new one. After the last
block the adapter stats the open handle and the path: shorter than when
the read began, or another file at the path, and it returns no
position. The reading then keeps none of that file's records or lines
and ingest keeps its position, so the next ingest reads it again.

A line read whole from one block is now decoded in place, without the
copy Buffer.concat made. The docs state the memory bound in full.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): read a transcript deleted mid-read as it was

The open handle still holds a deleted transcript whole, so its lines
are consistent and keep their position; a torn read is not skipped.
Both were mutants alive after the torn-read change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): keep the memory bench and record review round 1

The bench that measured peak memory is kept beside the evidence so it
can be rerun, and was: 371 MB before, 62 MB after at 151 MB, 67 MB at
402 MB. Red run, hand mutations, mutation score and gates of round 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record review round 2 of reading a transcript as a stream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* fix(telemetry): read a transcript replaced mid-read as it was opened

The open handle reads a file replaced at its path whole and
consistently, so discarding that read lost the old file's lines, and a
path rotated at every read would never be counted. Only a file that
shrank under the read is discarded now; a replaced one keeps its lines
and identity, and the next read restarts on the new file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record review round 2 evidence for the stream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* docs(telemetry): record review round 3 and the row it corrected

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp

* test(telemetry): skip the replaced-file read on windows, which cannot replace an open 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>
…run (#1048)

The report envelope moves to version 2: figures stay under totals and coverage, and what the read did moves to run. unknown_records is stated once, in the totals. The skill names the fix on a version mismatch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
A repository gets one id whatever the spelling of its address, and a call without a request id is keyed by message and session only. Stores written by an earlier version migrate on the first ingest: the ledger, roots.json and the branch snapshots read through an alias table, deterministic, idempotent and safe to stop at any write. A clone unreadable at the first run stays pending and is retried.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Committed docs no longer carry local machine paths, usernames, session ids or machine fingerprints; placeholders keep each sentence and command meaningful. A guard (scripts/check-no-local-paths.js) runs on staged files at pre-commit and on every tracked file in CI, and never prints what it matched. Git history still holds the old values; rewriting it was deliberately not done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Removes executable code and prototypes from task folders, the deterministic-tokens phase history, and the month-file ledger path no release ever wrote. Fixes stale docs, makes envelope.md the one definition of the report fields, sets merged task statuses to done, and records the slim task-folder rule once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
…1054)

A call whose transcript record carries no request id is no longer stored: the report states it as left out (coverage.left_out.requestless), from transcripts and from the migration's one pass. One the ledger already holds is a line this version does not accept, counted in run.skipped_ledger_lines. The full-ledger read, the second period bound, the older-CLI limits and the property exemption are gone; both properties hold with none. The local-path guard reads line by line. The rule against code for an unobserved case is recorded once in coding-assertions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
A --days reaching before year 0000 is refused instead of crashing; a closed stdout (report | head) ends quietly; the forget listing says repositories; a call's thinking tokens fill reasoning and the advisor's one-hour cache write is read. The ten-minute lock and older records' null reasoning are written as limits. Each defect was reproduced first; the rest was measured and left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Bumps [ink](https://github.com/vadimdemedes/ink) from 7.1.1 to 8.0.0.
- [Release notes](https://github.com/vadimdemedes/ink/releases)
- [Commits](vadimdemedes/ink@v7.1.1...v8.0.0)

---
updated-dependencies:
- dependency-name: ink
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* refactor(telemetry): one block reader for every tool, a format per file

The block reader, the listing and the open-stat-read skeleton move out of
the Claude Code source into a module every source shares. A source may
hand lines before the saved offset as not counted, and the read use case
makes its format once per file read, so a format can hold what earlier
lines said. Claude Code reads exactly as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* feat(telemetry): measure Codex from its own session files

A reader of Codex's rollout files joins the ingest list. Only a
token_usage_record line is a call; its model and directory come from the
latest turn_context before it, its version and branch from the file's
first session_meta, so a file that moved is read from its start and the
lines before the saved offset only build that context. Compressed files
are read whole, once, as a stream; a Node without zstd names each one.
Every sandbox that runs the CLI now relocates CODEX_HOME.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* test(telemetry): pin the Codex file names, oldest time and own session

Kills the mutants the changed-line run left alive in the Codex reader:
the rollout name pattern's anchors and wildcard, the oldest-written time
of its files, and a session_meta read after a turn_context.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): say that Codex is measured, its record source and limits

The usage contract gains Codex's files, its line-to-record table and its
residual limits, among them a thread resumed from a history with no
per-call records. The plugin README, the envelope reference and both
memory banks stop saying Claude Code only. The task folder holds the
contract, the plan and the evidence, the real-corpus reconciliation
included.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* fix(telemetry): name codex where the command and the readme describe it

The telemetry command's description said it measures Claude Code
sessions only. The plugin README now says why a Codex call takes its task
from its branch alone: the plugin's hooks run in Claude Code.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* fix(telemetry): a compressed file that does not decompress stops nothing

A .jsonl.zst whose stream zlib cannot decompress failed every ingest and
report, for both tools, and the session-start hook hid it. It is now one
unrecognised shape naming zlib's code; the file keeps its position and
is tried again by the next run. An I/O error still fails the read.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* test(telemetry): hold the sandbox's codex home inside it

The memory bank said the sandbox guard holds the CODEX_HOME relocation;
it did not. It now does, even when the runner sets its own CODEX_HOME.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* test(telemetry): pin zlib's code for a compressed stream cut short

A truncated frame is named Z_BUF_ERROR, not ZSTD_error_*: the test holds
both prefixes of what is passed over. The code is read with String()
rather than a fallback no error reaches.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): say codex is read wherever measurement is described

The consent explanation shown before opting in, the stop and answer
steps, the plugin and marketplace descriptions, the README, the
architecture, FAQ and catalog pages said Claude Code only. The retention
hint now says a tool may have removed older session files. The usage
contract states what a .jsonl.zst's offset is and what a file turning
compressed counts again, and the evidence records review round 1.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* chore: never track the session journals an earlier plugin wrote

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* fix(telemetry): a compressed file cut short is never read as whole

Node 22's zlib ends a cut zstd frame with no error and short output, so
a truncated .jsonl.zst was read as complete, its position saved and its
missing calls lost. The reader now counts the decompressed bytes against
the content size the frame header declares; another size is one
unrecognised shape, and the file keeps its position to be read again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* test(telemetry): pin each width a zstd frame gives its size

Kills the mutants the changed-line run left in the frame header reader:
a single segment's width read from the flag, a frame declaring no size,
the shortest header holding one, and a stream left unchecked when its
frame declares nothing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): record the cut frame on node 22 and its fix

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* test(telemetry): a compacted line's copy of the latest call is no call

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): state codex's limits as its own source sets them

Per-call records ship from Codex 0.153, earlier sessions count nothing;
the calls Codex writes no record for, the cost fields its file lacks,
cache writes as OpenAI bills them, frames without a size before 0.152,
coexisting plain and compressed copies, and forks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): mark the codex contract and plan done

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…1059)

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.3 to 5.5.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@5.4.3...5.5.0)

Updates `lefthook` from 2.2.1 to 2.2.2
- [Release notes](https://github.com/evilmartians/lefthook/releases)
- [Changelog](https://github.com/evilmartians/lefthook/blob/master/CHANGELOG.md)
- [Commits](evilmartians/lefthook@v2.2.1...v2.2.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 5.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-dev-dependencies
- dependency-name: lefthook
  dependency-version: 2.2.2
  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 [react](https://github.com/react/react/tree/HEAD/packages/react) from 19.3.0 to 19.3.1.
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.3.1/packages/react)

---
updated-dependencies:
- dependency-name: react
  dependency-version: 19.3.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>
…m-dev-dependencies group (#1061)

chore(deps-dev): bump vitest

Bumps the npm-dev-dependencies group in /kanban with 1 update: [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `vitest` from 5.0.3 to 5.0.5
- [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.5/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 5.0.5
  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 5 updates:

| Package | From | To |
| --- | --- | --- |
| [actions/create-github-app-token](https://github.com/actions/create-github-app-token) | `3.2.0` | `3.3.0` |
| [actions/setup-node](https://github.com/actions/setup-node) | `7.0.0` | `7.1.0` |
| [github/codeql-action/init](https://github.com/github/codeql-action) | `4.38.3` | `4.39.0` |
| [github/codeql-action/autobuild](https://github.com/github/codeql-action) | `4.38.3` | `4.39.0` |
| [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.38.3` | `4.39.0` |


Updates `actions/create-github-app-token` from 3.2.0 to 3.3.0
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Changelog](https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md)
- [Commits](actions/create-github-app-token@bcd2ba4...64ab729)

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@v7...949feb2)

Updates `github/codeql-action/init` from 4.38.3 to 4.39.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@24c5418...6f2ac81)

Updates `github/codeql-action/autobuild` from 4.38.3 to 4.39.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@24c5418...6f2ac81)

Updates `github/codeql-action/analyze` from 4.38.3 to 4.39.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@24c5418...6f2ac81)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: 3.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- 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.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat(telemetry): bind a declaration to the session a hook names

`aidd telemetry task` binds AIDD_TELEMETRY_SESSION_ID, else
CLAUDE_CODE_SESSION_ID. Codex sets no session variable, so its hook
names the session for the command it runs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* feat(telemetry): ask a codex tui session for its task

The hooks tell a Codex payload from Claude Code's by its session file.
A Codex prompt on an undeclared branch is held only when the file's
first line, read up to 1 MiB, says the TUI made it, no sub-agent sent
it, and approvals are on. A typed declaration binds the payload's
session. Session start does nothing for Codex; the catch-up ingests.
Claude Code behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* fix(cli): keep codex hook entries whole in the merged hooks.json

The merged .codex/hooks.json kept only type, command, timeout and
statusMessage, so the telemetry catch-up lost `async` and would run in
the way of the session start. Each entry is now kept whole, as the
plugin tree already keeps it. An entry merged before changes, so Codex
asks for it to be trusted again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): say where codex asks for a task, and its limits

Usage contract, plugin README, the start and stop texts and the CLI
memory: the Codex TUI is asked once its hooks are trusted, never
`codex exec`, the IDE extension, the desktop app or full access. The
task folder holds the contract, the plan and the evidence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* fix(telemetry): name the session for both tools, state the resumed-thread limit

The hook names the payload's session for every declaration, so a stale
exported AIDD_TELEMETRY_SESSION_ID cannot win in Claude Code. The Codex
session-file pattern leaves claude-only.cjs for its own module. A TUI
thread resumed in the IDE extension or desktop app is written down as a
limit, with a real-run step to observe it; the presence row reads
permission_mode exactly default.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* fix(telemetry): tell the codex tui by its originator alone

A real Codex 0.163.0 TUI with plugins installed runs through the app
server and records source vscode, not cli, so the prompt was never
held. The presence rule drops the source condition: originator
codex-tui, no agent_id and permission_mode default. An object source,
a sub-agent's own file, is still refused.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): record the real run and the desktop app's behaviour

The real run on Codex 0.163.0 held a TUI prompt, bound its declaration,
never held codex exec, and attributed the calls. The desktop app shows
a hook's block reason, so a TUI thread resumed there is asked visibly.
The README and usage contract say hooks are trusted with /hooks in the
TUI and that the desktop app offers no review of its own. aidd
telemetry on now names the Codex TUI beside Claude Code.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): mark the codex asks task done

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): say only what was seen of a resumed codex thread

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(telemetry): split and filter the report by tool

`aidd telemetry report --axis tool` gives one row per tool, and `--tool
<claude-code|codex>` keeps only that tool's calls, a closed set refused
like `--agent`. The envelope stays version 2: `filters` gains `tool` as
its last key, `null` when not given.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): document the report by tool

The envelope reference lists the `tool` axis and the last `filters` key,
the `01-usage` skill maps "Claude Code and Codex apart" to `--axis tool`
and `--tool`, and the usage contract lists the new filter.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): mark the report by tool done

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* test(telemetry): pin the tool axis on the binary and in the partition property

The journey e2e reports `--axis tool` for Codex and in its per-axis loop,
the partition property counts records by tool, the plugin descriptions
name the axis, and the evidence states only what is true.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

* docs(telemetry): state the property timeout's real reason

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…'s git (#1065)

A test that set its fixture's Claude Code session got the runner's
CODEX_THREAD_ID in its commit trailer instead, since the sandbox's git
environment inherited it. The sandbox now hands git no ambient session.

Closes #1050


Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Update tsup to 9.0.0 and Vitest / V8 coverage to 5.0.5. Align contributor Node requirements and preflight checks with tsup 9 while preserving the published CLI runtime requirement.

Validation: 6,854 CLI tests passed (1 skipped), 477 framework script tests, 68 Kanban tests, 132 CLI smoke checks including Node 22.12, build on Node 22.18 within budget, and mutation runner canary at 100%.
…1066)

Two placeholders in the vcs memory template: where tickets live and how
to read one, what runs on commit and what runs on push. The ticket-info
skill resolves its tool from this memory, and the implement skill
commits per task; both depend on the project having answered.
The repository memory carried a rule that overrode the phase files
aidd-dev:01-plan writes and the review aidd-dev:05-review writes, so an
agent following those skills here met two opposite instructions. The two
lines go back to what they said before; folders already written keep
their files.


Claude-Session: https://claude.ai/code/session_01TnCgcujM2xi6T6dkmVogVw

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
)

* refactor(aidd-refine): lean the brainstorm skill for small models

Merge probe and integrate into explore, rename finalize to conclude, fold the
single-use references into their action, drop the point labels from the
discovery map, and give the brief a Clear / Assumed / Open template saved with
status: approved. The skill goes from 13 files and 1506 words to 8 files and
about 940.

Extend the router citation guard to fenced blocks, which covers the mermaid
routers and drops the 10-batch exception.

Refs #1071

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015M5pyeN9UkURg1E85cHMU5

* refactor(aidd-refine): name the brainstorm rules section transversal

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015M5pyeN9UkURg1E85cHMU5

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(aidd-pm): plan and render the release video locally

Add a coverage check that fails when a feature shipped since the last release has no scene
in the release video and no written reason to skip it, a local renderer that turns the scene
plan into an MP4 with a pinned Hyperframes, and the 11-release-video skill that writes the plan.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: keep the release video skill in the repository's own skills

The release video is a maintainer tool, not product: move its skill out of aidd-pm into
.claude/skills, link it for Codex from .agents/skills, and add a render action next to the plan.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: drop removed surfaces from the release video check and plan the next release

A surface added then renamed or removed in the same cycle is no longer asked for. The plan
covers the rule generation change and skips the telemetry report format change, with its reason.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: type the release video version from an empty intro and show a note after its rows

The version was on screen from the first frame and typed again later; the note of a scene
appeared before its rows. The intro now starts empty and the note follows the last row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: plan the fixes and contributors scenes of the release video

The plan action now groups the user-visible fix commits into one scene and ends with the
contributors, listed from the compare between the last release tag and next.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* ci: refuse a promotion whose release video misses a shipped feature

The promote workflow runs the release video check on next's tip before it pushes the snapshot,
so a refusal leaves no branch or pull request behind. The skip_video input lets an urgent
promotion through. The weekly steps and the pipeline table say so.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: say what the release holds in the video intro and in each scene, with optional music

The intro now carries a tagline and a few highlights, each scene a plain-language lead, and a
music track placed in the release video assets plays under the whole video with a fade in and out.
The plan action asks for both, taken from the release announcement.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: fit the release video music to the video and keep it under the picture

The track is looped or cut to the length of the video, and its fade now peaks at the music
level: the fade lane is absolute, so a peak at full volume played the track at full volume.
A --music option renders with a track kept outside the repository.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: shape the release video music to what is on screen and write a hook intro

The track's loud hook now plays under the intro, its calm bed under the scenes and its loud
finale under the outro, found from the track's own energy. The plan action asks for an intro
that makes a newcomer want to know more, and the next release's plan is rewritten that way.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: refuse a release video intro or lead too long to read at a glance and plan the next release

The check names an intro tagline over six words, more than three highlights, a highlight over
three words and a lead over fourteen. The plan now covers Codex measurement, the rule assessment,
the telemetry reliability fixes and the contributors.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: write the release video in everyday words and refuse internal jargon

The check now names internal words (tokens, telemetry, envelope, axis, transcript, payload, hook)
in the intro, titles and leads. The plan action asks for outcomes in everyday words, and the next
release's plan is rewritten that way.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: move the release video into its skill and give it 8-bit sound and clear intro tags

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: publish the release video and recap from the release run

release-please now drafts the umbrella release; CI attaches the bundle,
distributions, archives and video, publishes it, then posts a recap.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* chore: check the release recap link by substring, not a pattern

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* chore: leave the release Slack message to n8n and only print the CI recap

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* docs: lean the deployment memory and draw the release flow

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* refactor(aidd-dev): lean the plan skill for small models

Four actions become three (capture, explore, conclude) behind a mermaid
router. The plan folder is checked by a bundled validator built from the
templates, then by one independent read against plan-checks. Phases gain
a Depends on column.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(aidd-dev): accept a plan folder on another drive in the plan validator

path.relative returns an absolute path across Windows drives, which the
inside-the-skill check read as inside.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(aidd-dev): strip template comments without a regex in the plan validator test

CodeQL flags the multi-character comment replace as incomplete
sanitization.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(aidd-dev): read a plan saved with Windows line endings in the plan validator

A CRLF plan lost its front matter and table headers to the line-ending
sensitive patterns and was rejected.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(aidd-dev): link capture to explore in the plan skill

The plain "Go to explore" let a small model stop after capture to ask
whether to continue. A markdown link makes the next action a file to read.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Scripts and bundled hooks touch paths, line endings and shells, and only
Linux and Windows ran the suite. A fan-in test keeps the gate in step.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
blafourcade and others added 13 commits October 26, 2026 04:53
…ses (#1085)

* chore: replan the release video from the full log and shorten its pauses

The plan is rebuilt from every commit since the last tag, not only feat
commits, and the intro takes up to five tags. Contributors include whoever
opened an issue closed since the tag. A scene lasts 3.5 seconds instead of
4.5, plus the lead and one beat per row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* chore: replace the 8-bit release video sound with a softer track and a sound for every element

The music is now a stereo track of felt piano and soft pads over a gentle pulse, with a four-note
motif in the hook and the ending. The interface sounds are soft tones and a breath of air, and
every element that appears has one: pills, titles, leads, notes and the intro and outro logos.
The hook is found from 3 dB over the bed, since it now sits 4 dB above it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* chore: make release video titles state the gain and drop subtitles

Titles say what the user gains, tags are larger, leads are left out, and a
changed skill is shown as a before and after card with its real file text and
a single title carrying the measured figure.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
)

* ci: render the release video on a pull request before the release

The video was only rendered once a release existed, on a runner never tried
with the current renderer. A dry run on the same runner shows a broken
render on the pull request and keeps the MP4 as an artifact.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* ci: name the next release in the release video dry run

The dry run showed v0.0. It now renders the minor after the last release,
the name the release job would give the video.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* ci: fix the version the release video dry run computes

The first attempt read 5.12.0 as 5.12.13. It now increments the minor and
keeps the major, giving 5.13.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
One file per feature under profiles/<tool>/, the profile's own declarations in profile.unit.test.ts. Same test titles and counts per host as the monolithic files.
…by-feature

test(cli): split each tool profile's tests by feature
* ci: hold the release until its video is attached

The release was published once its bundle and distributions were attached,
video or not, and its publication is what n8n announces on Discord and Skool.
A release whose video failed was announced without it. The video is now
required: a failed video keeps the release a draft.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* ci: remove the way to skip the release video check

The video is required for the release to publish, so a promotion that skips
its coverage check only moves the failure to release day.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
The announcements on Discord and Skool are built from what the video shows.
release-video.json already held the programme as description text; it now
also holds the themes and the programme as data.


Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* chore: group the release video programme by theme

The announcements built from the video show one section per theme, not one per
command, so each programme line names its theme and the check refuses a line
whose theme is not one of the plan's themes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

* chore: carry the video contributors in the release video text

The announcements credited the authors named in the release notes, while the
video credits the plan's contributors scene, which also holds the people who
opened the issues. The text now carries the same logins.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
A chat shows the first frame of a video as its preview, and the intro's was
the bare background: the logo, the pill and the version came in afterwards.
They are now on screen from the start, so they have no entrance and no
sound; the tags still come in one by one.


Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
The render now writes the thumbnail next to the video: the intro once its last
tag is in. The release job attaches it with the video, and the dry run keeps
it, so the announcement workflow can set it on YouTube.


Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* Revert "chore: draw the release video intro at its first frame (#1097)"

This reverts commit 790c03b.

* chore: take the thumbnail on the logo and the whole version

The intro goes back to its animated opening: the logo, the pill and the typed
version come in, then the tags. The thumbnail is taken at the moment the
version is typed and the first tag has not come in yet, so it shows the logo
and the version.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
The thumbnail was a frame of the video, taken while the intro was still
building, and it came out unbalanced. It is now a still composition rendered
beside the video: the logo, the release, the whole version and the themes.


Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
A chat shows the first frame of a video as its preview, and the intro starts
on the bare background, so the Discord announcement showed an empty picture.
The video now begins with the thumbnail for one frame, then the intro as
before. It is encoded again at its own bitrate, which kept it smaller.


Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
The plan said the opt-in distinguishes the result by tool, which is the
report's job. The opt-in turns the measurement on, after consent, for the
work done with Claude Code and with Codex.


Claude-Session: https://claude.ai/code/session_019tw8HkVy69fuukqR971Roz

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
@aidd-bot
aidd-bot Bot requested a review from a team as a code owner October 26, 2026 19:10
@aidd-bot
aidd-bot Bot enabled auto-merge October 26, 2026 19:10
@aidd-bot
aidd-bot Bot merged commit fedc086 into main Oct 26, 2026
53 checks passed
@aidd-bot
aidd-bot Bot deleted the promote/next-to-main-40384291991 branch October 26, 2026 19:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants