#270 makes the git commit gate report its own absence, as SPEC §3.4 requires.
The harness layer still does not. Filed from that work; the fix was out of its
file set.
The gap
internal/claudehook/claudehook.go:83:
func CanonicalCommand() string {
return "logmind guard-commit --layer harness " + hooks.HookVersionPrefix + version.Version
}
Bare name, same as the git hooks had. If PATH answers with something that is not
this binary — or with nothing — the PreToolUse hook exits non-2, Claude Code
allows the tool call, and nothing is printed.
The existing reasoning is half right, and says so
The function's own comment explains the choice:
Deliberately NOT prefixed with a command -v logmind guard: the command must
stay a single cross-platform line, and a missing logmind binary already
produces a non-2 "command not found" exit — which is the correct fail-open
behavior for a PreToolUse hook (only exit code 2 blocks the tool call). Adding
our own existence check would only introduce a platform-specific branch for no
behavioral gain.
The fail-open half is correct and §3.4 agrees with it. What has changed is
that §3.4 now also says fail-open must not be silent:
Failing open MUST NOT be silent. A hook that cannot run the engine it was
installed for MUST say so on stderr, naming what it looked for and what it
found.
A gate that cannot report its own absence will be trusted long after it stopped
working.
"No behavioral gain" was written before that requirement existed. The gain is not
behavioural — it is that the operator finds out.
Why the git-layer fix does not transfer
#270 solved this with an LOGMIND_HOOK_VERSION env-var handshake plus a stderr
notice, inside a POSIX sh hook body. This command has a harder constraint: it
is one line, run through whatever shell the OS gives Claude Code — the
comment above notes the marker is chosen to be valid in bash and PowerShell. A
command -v guard is not portable across that.
So this needs its own design rather than a copy. Options worth weighing, none
obviously right:
- Have
guard-commit --layer harness itself emit the skew notice when the
handshake env var disagrees — moves the check inside the binary, where
portability is not a shell problem. Does nothing when the binary is missing.
- Have
logmind doctor report harness-layer engine skew, accepting that it is
discovered on demand rather than at the moment it matters.
- Accept the gap explicitly and document it, rather than leaving §3.4 partially
implemented with no record of which half.
Related: #270 (git layer, fixed), #288. docs/plan.md:172-179 describes the git
layer as "fails open on any other error" — still true, now incomplete, and it
should say which layer reports and which does not once this is decided.
#270 makes the git commit gate report its own absence, as SPEC §3.4 requires.
The harness layer still does not. Filed from that work; the fix was out of its
file set.
The gap
internal/claudehook/claudehook.go:83:Bare name, same as the git hooks had. If PATH answers with something that is not
this binary — or with nothing — the PreToolUse hook exits non-2, Claude Code
allows the tool call, and nothing is printed.
The existing reasoning is half right, and says so
The function's own comment explains the choice:
The fail-open half is correct and §3.4 agrees with it. What has changed is
that §3.4 now also says fail-open must not be silent:
"No behavioral gain" was written before that requirement existed. The gain is not
behavioural — it is that the operator finds out.
Why the git-layer fix does not transfer
#270 solved this with an
LOGMIND_HOOK_VERSIONenv-var handshake plus a stderrnotice, inside a POSIX
shhook body. This command has a harder constraint: itis one line, run through whatever shell the OS gives Claude Code — the
comment above notes the marker is chosen to be valid in bash and PowerShell. A
command -vguard is not portable across that.So this needs its own design rather than a copy. Options worth weighing, none
obviously right:
guard-commit --layer harnessitself emit the skew notice when thehandshake env var disagrees — moves the check inside the binary, where
portability is not a shell problem. Does nothing when the binary is missing.
logmind doctorreport harness-layer engine skew, accepting that it isdiscovered on demand rather than at the moment it matters.
implemented with no record of which half.
Related: #270 (git layer, fixed), #288.
docs/plan.md:172-179describes the gitlayer as "fails open on any other error" — still true, now incomplete, and it
should say which layer reports and which does not once this is decided.