Route adopt's refusal-counter reset through fsys, and name the wait in the Stop hook's suggestions (issues 1749, 1757)
issue 1749: AdoptName reset both session ids' refusal-counter files with os.WriteFile, bypassing fsys; on a node whose agent has its own account (ADR 0266) the module runs as the operator's account and cannot write into the agent's home directly, so the reset silently failed. writeRefusalCounters now goes through fsys (MkdirAll then WriteFile), as every other write into the agent's home does; it is also called from the Stop hook itself, which already runs as the agent's own account, where fsys is its zero value and behaves exactly as the plain write did before. issue 1757: the Stop hook's suggested watched and said calls left out `by`, which claude_code_wait requires on every op, so a session following the suggestion word for word was refused. Both now carry "by" from the wait's own By field (already kept per wait since issue 1629). The SessionStart hook's suggested adopt call already carried by; added a test for it too, alongside the Stop hook's two, each extracting the exact JSON the hook prints and feeding it back to the tool's own dispatch to prove it is accepted as printed.
This commit is contained in:
@@ -52,12 +52,12 @@ func waitBlocksStop(session string, w Wait, now time.Time) (bool, string) {
|
||||
}
|
||||
return true, fmt.Sprintf("wait %s (%s) is open and nothing follows it yet. Read where it stands with %s, then "+
|
||||
"%s, and mark it watched: claude_code_wait "+
|
||||
"{\"op\":\"watched\",\"session\":%q,\"id\":%q,\"watcher\":\"...\"}.", w.ID, w.What, w.Read.Address, watcherAdvice(w),
|
||||
session, w.ID)
|
||||
"{\"op\":\"watched\",\"session\":%q,\"by\":%q,\"id\":%q,\"watcher\":\"...\"}.", w.ID, w.What, w.Read.Address,
|
||||
watcherAdvice(w), session, w.By, w.ID)
|
||||
}
|
||||
return true, fmt.Sprintf("wait %s (%s) is past its bound and the operator has not been told. Tell the operator what "+
|
||||
"waits, since when, and what you are doing about it, then mark it: claude_code_wait "+
|
||||
"{\"op\":\"said\",\"session\":%q,\"id\":%q}.", w.ID, w.What, session, w.ID)
|
||||
"{\"op\":\"said\",\"session\":%q,\"by\":%q,\"id\":%q}.", w.ID, w.What, session, w.By, w.ID)
|
||||
}
|
||||
|
||||
// watcherAdvice is what a session is told to do to follow a wait (ADR 0489 §3, review round 1 item 3): a
|
||||
@@ -113,15 +113,22 @@ func readRefusalCounters(waitsDir, session string) map[string]int {
|
||||
return m
|
||||
}
|
||||
|
||||
// writeRefusalCounters writes the counter file beside the waits file. Its caller decides what an error here
|
||||
// means: harmless when the stop is already allowed, but when the stop was about to be refused, a cap that
|
||||
// cannot be written is a cap that cannot take effect (ADR 0489 §5 "never loops", review round 1 item 2).
|
||||
// writeRefusalCounters writes the counter file beside the waits file, through fsys like every other write
|
||||
// into the agent's home (novox/hq ADR 0266, issue 1749): called from the Stop hook, which already runs as
|
||||
// the agent's own account (fsys is then its zero value, a plain write, as before), and from the tool's
|
||||
// AdoptName, which runs as the operator's account and must reach a confined node's agent home through the
|
||||
// sudo helper fsys carries such a write through. Its caller decides what an error here means: harmless when
|
||||
// the stop is already allowed, but when the stop was about to be refused, a cap that cannot be written is a
|
||||
// cap that cannot take effect (ADR 0489 §5 "never loops", review round 1 item 2).
|
||||
func writeRefusalCounters(waitsDir, session string, m map[string]int) error {
|
||||
raw, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(refusalCountersPath(waitsDir, session), raw, 0o600)
|
||||
if err := fsys.MkdirAll(waitsDir, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return fsys.WriteFile(refusalCountersPath(waitsDir, session), raw, 0o600)
|
||||
}
|
||||
|
||||
// notice says a fail-open or let-through reason on both the log (stderr) and the screen: Claude Code shows a
|
||||
|
||||
Reference in New Issue
Block a user