Conversation
…self An agent that signs a commit makes a claim about who did the work on the one surface a reader trusts without checking. This repository prosecutes its claims and cannot leave that one unprosecuted. .githooks/commit-msg strips machine-attribution trailers before a message is written. scripts/verify_attribution.py is the gate behind it: every commit after the recorded baseline must carry a Cubits11 author and committer and no such trailer. Both enter the manifest, so a hook someone forgot to install fails CI rather than passing silently. A Co-Authored-By naming a person is left alone; co-authorship by a human is a fact. The rule binds forward and not backward, and metrics/attribution_baseline.json records where it starts. Rewriting the 43 pre-baseline commits that carry a model trailer would change every SHA on the default branch, and those SHAs are pinned by published permalinks in distribution/traction/events.json, distribution/dispatch-log.yaml, distribution/dossiers/ and missing-column/index.html. A binding that no longer resolves is a worse failure than a trailer that accurately records who drafted a sentence. --history reports that count rather than hiding it.
|
So this is not a failure to fix in the branch. Adding the two checks to What makes it worth naming here rather than merging past it quietly: the subject of this PR is a rule constraining what an agent may claim about its own work, and the check that stops it is the rule that no change may certify its own trust root. The same principle, one level down. An agent clearing that gate with its own hand to install a rule about agent restraint is the exact move the gate exists to catch — so the merge is the owner's, not this branch's. The other three checks are unaffected by the trust-root refusal and run on their own. Generated by Claude Code |
scripts/repo_graph.py:139 reads the first add of each result file with `git log --diff-filter=A`, and takes the oldest. For experiments/e2 the true first add is dd36c1a (1788395285, 2026-09-02) and it is an ancestor of HEAD, but history simplification through the later merge hides it from that command on this container's git, which reports 4fa9a22 (1788656837, 2026-09-05) instead. Regenerating here therefore wrote a value three days late, and CI's git 2.55 recomputed the earlier one and failed the drift gate. The committed value goes back to 1788395285, which is the correct answer and the one CI derives. Everything else in this regeneration stands: the two attribution checks and their static-reference edges. The nondeterminism itself is not fixed here. first_result_commit_epoch feeds D-series detectors that decide whether a governing file was edited after a result existed, so a value that varies with the traversing git is a real weakness in a post-hoc-edit detector. Pinning it to the oldest add over --full-history, or to the commit recorded in the experiment's own freeze, would remove the dependence. That is a change to a detector and does not belong in a commit about attribution.
The gate failed on its own pull request. CI checks out the synthetic commit GitHub builds to test a PR, whose author and committer are `GitHub <noreply@github.com>`, and the identity rule rejected it. The same would have happened to every merge the button writes, so the rule as shipped would have failed on main permanently from the first merge onward. A merge GitHub wrote is plumbing, not authorship: nobody is claiming credit for a merge, and the content entering through it is in the parents, which are each checked on their own. So a commit with two or more parents carrying exactly that identity is exempt from the identity check. It stays subject to the trailer check, and the exemption requires the merge shape as well as the identity — a single-parent commit authored as GitHub is someone committing as GitHub, and still fails. Four fixtures cover the distinction a live run found before they did: a GitHub merge, a GitHub single-parent commit, a model-identity merge, an owner merge. The residual is stated in the docstring rather than papered over: a merge can carry conflict-resolution content present in no parent, and that content is unattributed under this exemption. Closing it means checking merge-diff provenance, which is a different gate.
The previous commit's exemption never fired. GitHub's merge keeps the person as author and replaces only the committer, but the exemption demanded that both slots be GitHub's, so the third CI run failed exactly as the first two did. The exemption now applies to the committer slot alone, on a merge, which is both correct and narrower: the author is always checked, so a merge still names a person, a single-parent commit made as GitHub still fails, and a merge authored by a model fails on its author. The fixtures deserve the larger share of the blame. They re-implemented the condition inline rather than calling the gate, so they agreed with the bug and reported 17 passing while CI failed on the same commit. The decision now lives in identities_to_check(), and the fixtures call it, so the gate and its tests cannot disagree again. Five cases replace the four that were testing a copy. Verified against fabricated commits of the exact shapes rather than against a description of them: author Cubits11 / committer GitHub / two parents is exempt; committing as GitHub on one parent gives 4 violations; a model-authored merge gives 2.
An agent that signs a commit makes a claim about who did the work, on the one surface a reader trusts without checking. This repository prosecutes its claims and cannot leave that one unprosecuted.
What this adds
.githooks/commit-msgstrips machine-attribution trailers before a message is written:Co-Authored-Bynaming a model or vendor no-reply address, the session backlink, the "Generated with" footer. ACo-Authored-Bynaming a person is left alone — co-authorship by a human is a fact, and this hook is not in the business of deleting facts.scripts/verify_attribution.pyis the gate behind the hook. Every commit after the recorded baseline must carry a Cubits11 author and no such trailer.--historyreports the pre-baseline count;--testruns 19 fixtures in memory.CHECKS, so a hook someone forgot to install fails CI rather than passing silently. The trunk goes 83 → 85 checks.metrics/attribution_baseline.jsonrecords where the rule starts and who decided it.The trade, stated
The rule binds forward and not backward. 43 of the 129 pre-baseline commits carry a model trailer. Rewriting them would change every SHA on the default branch, and those SHAs are pinned by published permalinks in
distribution/traction/events.json,distribution/dispatch-log.yaml,distribution/dossiers/bells-misuse-2025.mdandmissing-column/index.html.A binding that no longer resolves is a worse failure than a trailer that accurately records who drafted a sentence. So the history stands, the baseline records where the rule starts, and the count is reported rather than hidden.
Reversing this judgement is a one-command rewrite plus a re-pin of those permalinks. It is the owner's call, not this branch's.
The web-flow exemption, and how it was found
On a merge GitHub writes, the author stays the person and the committer becomes
GitHub <noreply@github.com>. The committer slot alone is exempt, and only on a merge. The author is always checked, so a merge still names a person; a single-parent commit made as GitHub fails; a merge authored by a model fails on its author.This was not designed in. The gate failed CI three times on its own pull request before it was right:
mainforever, starting with this one.The fixtures share the blame: they re-implemented the condition inline instead of calling the gate, so they agreed with the bug and reported green while CI was red. The decision now lives in one function,
identities_to_check(), which both the gate and its fixtures call, and the shapes were verified against fabricated commits rather than against a description of them.Residual, stated rather than hidden: a merge can carry conflict-resolution content present in no parent, attributed only by the merge's author. Closing that means checking merge-diff provenance, which is a different gate.
CI
Three checks green on
8732e64.Trusted evidence boundaryis red and will stay red until this is merged.scripts/verification_manifest.pyis insecurity/evidence-policy.json:trust_root, andevidence_guard.py:68refuses any diff to a trust-root file unconditionally — no bypass flag — because adding checks widens the verification surface. Its own comment: "An upgrade needs an explicit admin migration." Every past manifest change crossed the same gate (c9dc338,5a39b06,48e98e4,6728545,f3a6a1e,0aad901).There is no push that clears it. That is the design: the merge is a deliberate human act, which is the right shape for a PR whose subject is constraining what an agent may claim about its own work.
Also fixed here:
docs/graph/repo-graph.json'sexperiments/e2.first_result_commit_epoch. The true first add (dd36c1a) is an ancestor of HEAD, but history simplification through a later merge hides it fromgit log --diff-filter=Aon some git builds. The committed value is the correct one. The underlying nondeterminism is not fixed — that field feeds the D-series post-hoc-edit detectors, so a value that varies with whose git computes it is a weakness in a detector whose job is catching forbidden rescues. Worth its own change.Generated by Claude Code