Repository navigation
fix: never read a failed git call as no repository, and never lose a staged transcript silently - #1104
Merged
Conversation
Refs: iss-2610051434156079 Assisted-by: Claude:claude-opus-5-5
A git call that fails once, as a refused fork or an early-killed git does on a loaded machine, reads as no root commit. A burst's first sub-agent completions run before any sibling has noted the session, so the hook took a repository-shaped cwd for one outside any repository, staged nothing and exited 0, saying so only on a stderr no one reads. When the cwd carries a .git entry but git gave no root commit, the hook now asks again for a few seconds, re-reading the session note between tries. A stage refused by a lock held past its timeout is tried again. A transcript read and still not staged, with the store known, is counted in a marker that history staged reports. The burst test prints each hook's stderr when it fails, so a loss names its cause. Refs: iss-2610051434156079 Assisted-by: Claude:claude-opus-5-5
gitutil.RootCommit returned "" for every git failure but a timeout, so a
refused fork or an early-killed git on a loaded machine read as "no root
commit" at every caller. It now returns (string, error). ("", nil) means
only a missing directory, no repository at or above it, or a repository
with no commits, which `rev-parse --verify -q HEAD` confirms. Any other
failure is ErrNoRootAnswer or ErrGitTimedOut.
Every caller now gives the error a deliberate answer. Where it returns
errors, it propagates. Where it is total, it keeps its safe fallback and
says git did not answer. Nowhere does the error take the empty path with
consequences: keying, creating or migrating a store, adopting a
transcript, or refusing as "not a repository". ahoy's identity carries the
failure apart from a definite "no root". The session-end hook now retries
it, as the sub-agent hook does, and both mark a transcript they still
cannot stage. The stage-miss marker covers both.
Refs: iss-2610051434156079
Assisted-by: Claude:claude-opus-5-5
The host gives a SessionEnd hook 1.5 seconds unless it asks for more, so the sub-agent hook's three and a half seconds of resolution waits and its stage retries would have been cut off there, leaving neither the stage nor the marker. Session end now pauses twice, briefly, for the repository and does not retry the stage. Refs: iss-2610051434156079 Assisted-by: Claude:claude-opus-5-5
…s deadline Refs: iss-2610181802344551 Assisted-by: Claude:claude-opus-5-5
The partial-clone guard probed git's version once per process and kept the answer even when the probe failed, so a git that could not run once on a loaded machine read as a git too old for the rest of the process, refusing every object read in a partial clone. A failed probe is no longer kept; a line git answered with still is. The deadline test now warms the probe with the real git first, so it no longer depends on test order. Refs: iss-2610051434156079 Refs: iss-2610181802344551 Assisted-by: Claude:claude-opus-5-5
The root-commit lookup took a path with no .git entry on it for no repository without asking git, so a directory reached through a symlink, or a bare repository, read as none. git is now asked first. A failure is read as "not a repository" only when git says so in its pinned C locale, or when neither the path nor its resolved form carries a .git entry. history staged --json printed only the array, so an agent told to report a lost transcript saw none. Both notes now also go to stderr there, and the text render no longer says every session is stored above a loss. Install no longer asks to adopt a repository git did not answer for; an explicit --adopt still stands. ahoy's status names the failure instead of "unmanaged". The hooks do not retry the partial-clone floor, which answers the same every time. A marker that no longer parses is replaced with a reason saying earlier misses were counted there. Refs: iss-2610051434156079 Assisted-by: Claude:claude-opus-5-5
…s unread A git version probe that could not run is not a git below the floor. Refusing it as ErrLazyFetchFloor made both hooks skip their retries, so a transcript in a partial clone was lost to one failed fork after all. The read is still refused, now as ErrGitVersionUnread, which a later try may clear. An explicit --refuse-adopt with git unanswered reports the abort it asked for. Refs: iss-2610051434156079 Assisted-by: Claude:claude-opus-5-5
…y time A directory with no .git entry, as written or resolved, is in no repository whatever git did, so git missing from PATH no longer turns a plain folder into one git did not answer for. Session end's retries are now bounded by time from the hook's start, each costed at what the first detection took, since detection takes most of a second on the loaded machine where git fails. The stage-miss marker keeps, through later misses, when an unreadable earlier marker was replaced. Refs: iss-2610051434156079 Assisted-by: Claude:claude-opus-5-5
Resolves: iss-2610051434156079 Assisted-by: Claude:claude-opus-5-5
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.
A burst of twelve simultaneous sub-agent completions on a loaded CI runner staged only ten or eleven transcripts, while every hook exited 0. The record had been deferred twice because its cause was not known. This change establishes the cause and fixes it at the primitive.
Cause. It was not the staging lock: each agent has had its own lock since an earlier fix. A git call that fails once (a refused fork, an early-killed git) was read as "no root commit" by
gitutil.RootCommit. The sub-agent hook then took a real repository for none. A burst's first completions run before any sibling has noted the session, so the hook staged nothing and exited 0, saying so only on a stderr no one reads. A git shim failing the root lookup for six of twelve hooks loses exactly six on main.The fix, in three layers:
RootCommitnow returns(string, error). An empty answer means only a missing directory, no repository (git says so, or no.gitentry at or above the path, as written or resolved), or a repository with no commits. Any other failure is an error. All twenty callers handle it: they propagate it, or keep a safe fallback and name the failure, and never take the "no repository" path that keys, creates, migrates or refuses. A related cache is fixed too: a git version probe that could not run was being kept as "git too old" for the rest of the process.abcd history stagedreports; under--jsonit goes to stderr.New tests cover each layer, and each one was watched failing on the code before it. These include a twelve-way burst with failing root lookups, a staging lock held past its timeout, a session-end retry, a symlinked path, a bare repository, and a version probe that fails once. The hook and marker tests passed three times under
-race. An Opus and Sonnet review pair reviewed the change twice, and every finding is folded in.The guard's own probes not honouring a caller's deadline are captured separately, as a smaller follow-up.
Resolves: iss-2610051434156079
Refs: iss-2610181802344551
Assisted-by: Claude:claude-opus-5-5