You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The commit trailer installs by appending one line to prepare-commit-msg. In a repository
where another tool owns that file, the line is erased the next time that tool regenerates
it, the trailer silently stops being written, and nothing anywhere says so.
Measured, on this repository
aidd-session-trailer.sh is ours outright and nobody rewrites it. What is fragile is the
call site, and this repository shows it happening:
.git/hooks/commit-msg Sep 2 07:06 regenerated by lefthook
.git/hooks/pre-commit Sep 2 07:06 regenerated by lefthook
.git/hooks/pre-push Sep 2 07:06 regenerated by lefthook
.git/hooks/prepare-commit-msg Aug 22 11:58 untouched
Lefthook rewrote three hook files that morning. prepare-commit-msg survived for one
reason: lefthook.yml declares pre-commit, pre-push and commit-msg, and no prepare-commit-msg job. That is a coincidence of configuration, not a guarantee — adding
such a job makes the next lefthook install erase the appended line. husky regenerates the
same way.
And nothing reports it: aidd telemetry check contains no claim about the trailer at all.
The commits keep succeeding, the journal keeps recording, and only the commit-to-session
join stops — which is exactly the shape this layer exists to refuse. An unknown is never a
zero.
The approach, and why it is general
Do not defend the call site. Re-establish it.
On session-start, where journal.cjs already pays a once-per-session cost with that
argument written down (see maybeRecordUnrecognisedPayload): if the delegate exists in the
hooks directory git actually resolves, and prepare-commit-msg no longer calls it, append
the line back.
It never asks why the line is gone, which is what makes one code path cover every case —
a regenerated hook, an overwrite by hand, a core.hooksPath move, a hook that never
existed. No third-party tool is named, so nothing here rots when their formats change.
The opt-out is the one people already know: aidd telemetry off deletes the delegate, and
the repair only ever runs when the delegate is present. Nothing is resurrected after off.
Done when
A prepare-commit-msg overwritten between two commits calls the delegate again after the next session start, asserted without installing lefthook or husky — the failure is reproduced by its shape, a hook file replaced, not by its brand.
The repair never runs when the delegate file is absent, so off stays final.
Its cost is one resolve plus one small read per session, and it never runs on tool-used.
aidd telemetry check states, each independently verifiable: where git resolves the hooks directory, whether the delegate exists and is executable, whether prepare-commit-msg calls it, whether that hook carries somebody else's content as well, and how many of the last commits actually carry AIDD-Session-Id.
That last claim is the one that proves the chain rather than its parts, and reads as a count, never as a bare pass.
A commit still succeeds when the delegate is deleted, made non-executable, or made to fail.
The delegate is asserted to spawn no Node and reach no network, and to end every path in exit 0.
#630 asks for p95 under 200 ms. A wall-clock threshold on a shared CI runner measures the
runner's load, not the hook, and becomes a flake generator. The property that protects a
commit is structural and is what the boxes above assert. Measure the duration once, record
the number here, and gate on the properties.
Bounds
The default case — a repository whose prepare-commit-msg nobody else generates — works
today and is proven by eight end-to-end tests in telemetry-commit-trailer.e2e.test.ts. An
erased line costs a join, never a record: sessions keep being measured, and only
commit-level attribution is lost until the next session repairs it.
The commit trailer installs by appending one line to
prepare-commit-msg. In a repositorywhere another tool owns that file, the line is erased the next time that tool regenerates
it, the trailer silently stops being written, and nothing anywhere says so.
Measured, on this repository
aidd-session-trailer.shis ours outright and nobody rewrites it. What is fragile is thecall site, and this repository shows it happening:
Lefthook rewrote three hook files that morning.
prepare-commit-msgsurvived for onereason:
lefthook.ymldeclarespre-commit,pre-pushandcommit-msg, and noprepare-commit-msgjob. That is a coincidence of configuration, not a guarantee — addingsuch a job makes the next
lefthook installerase the appended line. husky regenerates thesame way.
And nothing reports it:
aidd telemetry checkcontains no claim about the trailer at all.The commits keep succeeding, the journal keeps recording, and only the commit-to-session
join stops — which is exactly the shape this layer exists to refuse. An unknown is never a
zero.
The approach, and why it is general
Do not defend the call site. Re-establish it.
On
session-start, wherejournal.cjsalready pays a once-per-session cost with thatargument written down (see
maybeRecordUnrecognisedPayload): if the delegate exists in thehooks directory git actually resolves, and
prepare-commit-msgno longer calls it, appendthe line back.
It never asks why the line is gone, which is what makes one code path cover every case —
a regenerated hook, an overwrite by hand, a
core.hooksPathmove, a hook that neverexisted. No third-party tool is named, so nothing here rots when their formats change.
The opt-out is the one people already know:
aidd telemetry offdeletes the delegate, andthe repair only ever runs when the delegate is present. Nothing is resurrected after
off.Done when
prepare-commit-msgoverwritten between two commits calls the delegate again after the next session start, asserted without installing lefthook or husky — the failure is reproduced by its shape, a hook file replaced, not by its brand.offstays final.tool-used.aidd telemetry checkstates, each independently verifiable: where git resolves the hooks directory, whether the delegate exists and is executable, whetherprepare-commit-msgcalls it, whether that hook carries somebody else's content as well, and how many of the last commits actually carryAIDD-Session-Id.exit 0.Replacing #630's p95 box
#630 asks for p95 under 200 ms. A wall-clock threshold on a shared CI runner measures the
runner's load, not the hook, and becomes a flake generator. The property that protects a
commit is structural and is what the boxes above assert. Measure the duration once, record
the number here, and gate on the properties.
Bounds
The default case — a repository whose
prepare-commit-msgnobody else generates — workstoday and is proven by eight end-to-end tests in
telemetry-commit-trailer.e2e.test.ts. Anerased line costs a join, never a record: sessions keep being measured, and only
commit-level attribution is lost until the next session repairs it.
Relations