Sign the commits a run's own workers make - #1495
Merged
Merged
Conversation
…ber) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SignCommitMessageOnce leaves a message that already carries both codeaf lines byte for byte, gives a message with neither the same two-line block SignCommitMessage writes, and gives a message with one of them only the line it lacks. ContributingRefusesTrailers is the one conservative reading of a repository's ban on AI trailers: a sentence is a ban only when it names attribution, names AI and prohibits, and a requirement to add or keep the lines (including "do not remove") always wins. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…work they committed On the worker harness a commit the worker made itself carried no attribution: the landing signed only the work still uncommitted when the run ended. Now, before its work goes home, the landing walks the commits the run made on its own branch since its base and gives each one that lacks them the same bare Assisted-by and co-author lines, as a message-only rewrite (commit-tree with the original tree, author and committer, then one compare-and-swap update-ref), so the tree, the index and the repository's own hooks are untouched. Commits another branch, tag, remote-tracking ref or FETCH_HEAD reaches, and a range holding a gpgsig/mergetag/encoding header, are left exactly as they are. A repository whose CONTRIBUTING forbids AI trailers gets none, from the rewrite or from the landing's own commit. The copy record keeps the run's base across a restart, and a run whose worker committed everything (or committed and reverted) now names its branch and files instead of saying nothing to land. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
codeaf do still makes no commit of its own in the folder it was handed. When its run ends it signs the commits its workers made there since the run began (every commit on the branch when the folder had none), with the landing's bare line, before it names the files in the envelope. A signing failure is one codeaf: line on stderr and never changes the exit status; the work is on disk either way. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
permissions.md says the worker harness signs its workers' commits too, once, with the bare line, and when it leaves one alone (a GPG/SSH-signed range, a pushed, tagged, other-branch or fetched commit, a detached HEAD); that codeaf now reads CONTRIBUTING itself, and which files; and no longer promises a sentence the harness does not say. The do and /task pages stop saying a run commits nothing or lands nothing when its worker committed. Two probes in a person's own words reach the page. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AbirAbbas
marked this pull request as ready for review
September 25, 2026 15:12
ZeroPoint95
added a commit
that referenced
this pull request
Sep 25, 2026
Resolves the two conflicts #1495 made: Land takes the run's base and the copy record carries CheckBase beside Continues. The clock test's slow landing takes the base too, and places.md says where the here ~/codeaf chip sat, so the manual's search still reaches it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AbirAbbas
added a commit
that referenced
this pull request
Sep 25, 2026
…and #1495 Four tui3 conflicts, each #1429's wall and team code beside the plan page this branch removes. Both are kept: the wall's paint term stands before the task room's, the wall, team menu and zoom keys are read where the plan page's and work tab's keys were, the row keeps both its plan and open fields, and the frame draws the wall where it drew the plan page and the work tab. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AbirAbbas
added a commit
that referenced
this pull request
Sep 25, 2026
Merged through a commit that stands for #1429's squash with the branch this one was built on (6416a6c) as its second parent, so only the real conflicts show. tabtitle: #1496 put the hovered tab's full title on the head's blank row under the rule; on this branch a chat's head is the nav, the strip, the rule and the blank, so that row is chatHeadRows-1 and the strip's row is tabStripRow. notice: #1487's quiet tip reads the keys row of a put-away column, which this branch spells through sideBackHint (`alt+l tasks`, `alt+l traffic` for a manager) rather than the retired railBackHint. manual: #1495's workers heading stands before this branch's rewritten roster paragraph; team_stop keeps #1429's background-member sentence beside this branch's new rows; the dock and wall probes are both kept. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was wrong
On the default task belt (the worker harness, what
/taskandcodeaf douse unlessCODEAF_TASK_BELT=node), a commit the task worker made itself carried no attribution lines. The harness signed only work still uncommitted when the run landed, so/task "create hello.txt containing hi and commit it"orcodeaf do "…and commit it"put an unsigned commit on the person's branch, while the manual said a task landed on the default belt carriesAssisted-by: CodeAF.Two adjacent faults on the same road were fixed in the same change:
nothing to landwhilecomeHomemerged its work into the person's checkout.Design (harness-side)
When a run ends, before its work comes home, the harness walks the commits the run made on the branch its working copy stands on since the run's base and gives each one that lacks them the same lines the landing writes: the bare
Assisted-by: CodeAFand the co-author (exec.SignCommitMessageOnce, throughgitSignature). It is a message-only rewrite with plumbing:git commit-tree --no-gpg-signwith the original tree, remapped parents, and the original author and committer (names, emails, dates), then one compare-and-swapgit update-ref. The tree, the index and the working directory do not move, and no hook runs twice.Why not a
commit-msghook throughGIT_CONFIG_*/core.hooksPath: that environment reaches every git the worker runs (a project's own test repositories would get trailers), and it replaces the repository's own hooks unless every hook is wrapped and chained. Rewriting at the end touches only the run's own unpublished commits, in one place./task:LandRunTree(dir, base, title, model)signs first, then commits leftovers, then names the paths since the base.RunEngine.Landtakes the run'scheckBase. The copy record keeps it across a restart; an old record without it signs nothing.codeaf do: still makes no commit of its own.runengine.SignWork(before)signs its workers' commits in place when the run ends. A repository with no commits yet counts all of its first commits. A signing failure prints onecodeaf:line on stderr and never changes the exit status.ContributingRefusesTrailers(a conservative text reading: a sentence must name attribution, name AI and prohibit it; a requirement to add or keep the lines, including "do not remove", always wins) plusrepositoryRefusesTrailers, which reads the first 256 KiB of regularCONTRIBUTING{,.md,.rst,.txt}files at the repository's top level, in.github/and indocs/. It controls the rewrite and the landing's own commit (commitTaskWorkAs).gpgsig/mergetag/encodingheader is left byte-for-byte as it is. A commit that another branch, a tag, a remote-tracking ref or FETCH_HEAD reaches is never rewritten (pushed, tagged, other-branch or fetched-and-merged work). A detached HEAD rewrites nothing.codeaf doworks in place, so a commit a person makes in that folder during the run is signed too.Validation contract, and how each item is proven
Each test uses real temp git repositories through the real landing path; only the model is faked. The new regression tests were each seen failing before the fix.
TestLandSignsWorkerCommitAndNamesCommittedWork(real Supervisor, thenrun.Land, with and without leftover work).TestBeltRunSignsWorkerCommitsInGround(the/taskdoor throughcomeHome: two worker commits plus leftovers, all three signed once on the ground's branch).TestLandRunTreeSignsWorkerCommitsAndNamesTheirPaths.TestSignRunCommitsCompletesOnceAndKeepsWorkerIdentity(a second pass rewrites 0; a message with one line gains only the other).TestSignRunCommitsKeepsAlreadySignedWorkerSHA.TestSignCommitMessageOncePreservesAndCompletesWorkerTrailers.TestSignRunCommitsKeepsRawWorkerMessageAndCommitIdentity.TestSignRunCommitsKeepsOtherRefsAndMergeParents(base history, a merged outside branch, a tag, a remote-tracking ref).TestSignRunCommitsKeepsFetchedMergeParent(FETCH_HEAD).TestRunCommitIdentityKeepsAngleBracketInName.TestContributingRefusesTrailersOnlyForAIBans(a table checked in both directions).TestRepositoryRefusesTrailersReadsOnlyRegularContributingFiles. The CONTRIBUTING arms ofTestLandRunTreeSignsWorkerCommitsAndNamesTheirPaths(the worker's commit and the landing's own commit stay unsigned) and ofTestDoOnTheRunEngineSignsWorkerCommitsUnlessContributingForbids.TestLandSignsWorkerCommitAndNamesCommittedWorkandTestLandRunTreeSignsWorkerCommitsAndNamesTheirPaths, a realpre-commithook writes a marker when the worker commits. The landing does not run it again, andcore.hooksPath/ local config are unchanged.codeaf dobehaves the same as/task.TestDoOnTheRunEngineSignsWorkerCommitsUnlessContributingForbids(a scripted bash worker runsgit add hello.txt && git committhroughrunErrand; the commit is signed once with bare lines, the person's earlier commit keeps its sha, the envelope names hello.txt, exit 0).TestDoOnTheRunEngineSignsFirstCommitOnUnbornBranch.TestRunTreeSnapshotSignsFirstCommitsOnUnbornBranch.landed on run-work: 1 fileinTestLandSignsWorkerCommitAndNamesCommittedWork.TestLandNamesCommittedThenRevertedWork/TestLandRunTreeNamesCommittedThenRevertedWorkcover a commit followed by its revert. A read-only run still answersnothing to land(TestLandRunTreeWithBaseRefusesReadOnlyRun,TestLandRefusesARunThatWroteNothing).TestSignRunCommitsLeavesProtectedAndUnbasedHistoryUntouchedcovers a craftedgpgsigrange (left byte-for-byte), a detached HEAD and an empty base.Hand check: the real binary built from this branch
Setup:
make build; a throwaway home with every model seat pinned todeepseek/deepseek-v4-flash,CODEAF_MODEL_POOL=read, telemetry off. Total spend was about $0.01./task create hello.txt containing hi and commit itin the TUI, in a scratch repo on branchworkthat has apre-commithook writing a marker. The task row readdone · 56s · 1 file · mergedand the run's note readlanded on work: 1 file.The same
/taskrun in a repo checked out onmainkept its branch, which is the existing protected-branch rule (your checkout is on main, which tasks do not merge into automatically). On that kept branch the worker commitaca75ec(authoragent1) became707db24(codeaf: sign the run's own commits), with the lines once and the author unchanged.codeaf do --dir <repo> --yes-spend --json … "create hello.txt containing hi and commit it"in a repo with apre-commithook. Result:ok: true,stop: done,files: [".../hello.txt"], spend $0.002.codeaf do …in a repo whoseCONTRIBUTING.mdsays "Do not add AI co-author trailers to commits.":ok: true, and nothing was rewritten.codeaf do …in a freshlygit inited repo (no commits): the worker's first commitAdd hello.txtcarries the two lines once.Manual and change entry
permissions.mdnow says the worker harness signs its workers' own commits once, with the bare line, when the run ends, and thatcodeaf dodoes the same in place. It lists when a commit is left alone, and says codeaf reads CONTRIBUTING itself and names the files it reads. It no longer promises a "tells you it did" sentence the harness does not say. The signing section was split so each section answers its own heading.worker-harness.mdno longer saysdocommits nothing or that a worker-committed run lands nothing, and fixes a stale "How to turn it on" pointer. It also adds a heading for when a task's work comes home.permissions.md.docs/changes/unreleased/1495-worker-commits-signed.mdstates what was true and what is true now.Gates on the head after merging
origin/dev:make pr-ready's light gate (build, vet, fmt, packed manual, change entries, manual gates, laws) passes. The fresh touched-package run was taken on a shared box at load average 12–14. A handful ofinternal/runandinternal/sessiontests failed only under that load, and a different handful on each run. They are timing and concurrency tests the machine governor holds back on a busy host (e.g.peak concurrency = 5 … want all 6). They are not in code this change touches, and each passes when run on its own. CI'stouched packagesis the clean-runner answer.🤖 Generated with Claude Code