Skip to content

The harness layer's commit gate still cannot report its own absence — §3.4's fail-open-but-loud rule is half-implemented #298

Description

@thrillmot

#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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions