Skip to content

fix(bin): isolate Claude hooks under Grok and floor Herdr default presentation - #31

Merged
withally merged 4 commits into
mainfrom
fm/fm-grok-primary-compat-c1
Aug 14, 2026
Merged

fix(bin): isolate Claude hooks under Grok and floor Herdr default presentation#31
withally merged 4 commits into
mainfrom
fm/fm-grok-primary-compat-c1

Conversation

@withally

Copy link
Copy Markdown
Owner

Intent

Make Grok 1.0.3 safe as this home Firstmate primary on Herdr by semantically adapting upstream commit 06b33aa hook isolation and the public Herdr 0.8.0 default-presentation floor without wholesale parent convergence. Exactly five tracked Claude entries - SessionStart, two Bash PreToolUse hooks, the turn-end Stop guard, and Claude auto-arm Stop - must be inert under either GROK_AGENT or GROK_HOOK_EVENT while the subagent pre-tool guard remains deliberately unguarded because Grok has no native counterpart. An absent Herdr presentation setting must stay flat below 0.8.0, default on at or above the floor, explicit on must remain an opt-in below the floor, explicit off must remain off, client and selected running-server evidence must compose conservatively, and warnings should deduplicate per detected release. Preserve this fork durable wake, send-confirmation, decision-receipt, focus-safe cleanup, session-start run-tier, crew and secondmate selection, Relay policy, quota dispatch, and unrelated parent deltas. Do not spawn Grok crews, change dispatch configuration, touch the live Grok primary session, upgrade live Herdr, install a global guaranteed-delivery session-start hook, merge public parent, or merge the PR. Accepted Grok live proof is grok 1.0.3 inspect JSON from this isolated worktree after the guards plus env-driven execution regressions proving five Claude counterparts inert under Grok and live under Claude while the subagent exception stays armed. A named non-default Herdr lab was attempted twice with the mandated helper and default-session tripwire, but Herdr 0.7.5 refused the fresh shell pane both times with agent_pane_busy before Grok launched; do not retry agent start and document this exact live-matrix limit in the PR body. Open the PR through no-mistakes and finish only when CI-ready checks are green.

What Changed

  • Made the five tracked Claude hook entries (SessionStart, both Bash PreToolUse guards, the turn-end Stop guard, and Claude auto-arm Stop) inert when running under Grok (GROK_AGENT or GROK_HOOK_EVENT), while the subagent pre-tool guard deliberately stays unguarded since Grok has no native counterpart; covered by new env-driven regressions in tests/fm-turnend-guard.test.sh.
  • Gated Herdr default presentation behind a 0.8.0 release floor in bin/backends/herdr.sh: an absent setting stays flat below 0.8.0 and defaults on at or above it, explicit on/off settings are honored either way, client and selected running-server evidence compose conservatively, and below-floor warnings deduplicate per detected release.
  • Restructured bin/fm-spawn.sh so journal-exists projection recovery runs whenever a .herdr-presentation journal exists — independent of the presentation preference or release floor — so previously projected workspaces are recovered instead of stranded (per the review finding fixed in this branch), with docs updated to match.

Live-matrix note: a named non-default Herdr lab run was attempted twice with the mandated helper and default-session tripwire, but Herdr 0.7.5 refused the fresh shell pane both times with agent_pane_busy before Grok launched; agent start was not retried. Live proof instead comes from grok inspect --json (grok 1.0.3, isolated trusted GROK_HOME) plus the env-driven hook regressions listed in the Test section.

Risk Assessment

✅ Low: The follow-up commit implements the requested fix exactly — journal-exists recovery now runs unconditionally while the floor/preference gate governs only fresh projections — with sound variable ordering, pure helpers on the new unconditional path, matching docs, and no remaining intent contradictions; the only residual note is an unpinned regression test, which is non-blocking.

Testing

Ran the three targeted suites (turn-end guard, herdr backend, secondmate harness) which all passed, captured grok 1.0.3 inspect JSON from this worktree showing the five guarded Claude hook entries and the deliberately unguarded subagent guard, re-ran the new Grok-inertness regression in isolation to transcript its pass, and produced a manual transcript of the Herdr presentation floor demonstrating default-on at 0.8.0, flat-with-deduplicated-warning below it, honored explicit on/off, and conservative client/server composition; no rendered UI surface exists for this shell/hook change, so CLI transcripts and inspect JSON are the end-user-visible evidence, and the worktree was left clean.

Evidence: grok 1.0.3 inspect JSON from this worktree (5 guarded entries + unguarded subagent exception)
{
  "grokVersion": "1.0.3",
  "channel": "unknown",
  "cwd": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT",
  "projectRoot": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/",
  "projectTrusted": true,
  "projectInstructions": [
    {
      "path": "/Users/ivan/.claude/Claude.md",
      "scope": "global",
      "fileType": "agents_md",
      "sizeBytes": 7437,
      "approxTokens": 1859,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/Agents.md",
      "scope": "project",
      "fileType": "agents_md",
      "sizeBytes": 65550,
      "approxTokens": 16387
    }
  ],
  "permissions": {
    "sources": [],
    "loaded": 0,
    "skipped": [],
    "mcpServerAllowlist": [],
    "marketplaceAllowlist": [],
    "managedSettingsPath": "/Library/Application Support/ClaudeCode/managed-settings.json",
    "managedSettingsExists": false,
    "managedSettingsActive": false
  },
  "loginPolicy": {
    "disableApiKeyAuth": null,
    "forceLoginTeamUuid": null,
    "apiKeyAuthDisabled": false
  },
  "hooks": [
    {
      "event": "session_start",
      "hookType": "command",
      "target": "chrome-devtools-axi",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "session_start",
      "hookType": "command",
      "target": "lavish-axi",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "session_start",
      "hookType": "command",
      "target": "gh-axi",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "session_start",
      "hookType": "command",
      "target": "'/opt/homebrew/bin/moshi-hook' claude-hook",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "session_start",
      "hookType": "command",
      "target": "[ -z \"${GROK_AGENT:-}${GROK_HOOK_EVENT:-}\" ] || exit 0; exec \"$CLAUDE_PROJECT_DIR\"/bin/fm-sessionstart-run.sh",
      "source": {
        "type": "project",
        "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "session_start",
      "hookType": "command",
      "target": "bash -lc '[ -n \"${GROK_WORKSPACE_ROOT:-}\" ] || exit 0; exec \"${GROK_WORKSPACE_ROOT:-}/bin/fm-sessionstart-nudge.sh\"'",
      "source": {
        "type": "project",
        "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/.grok/hooks"
      },
      "matcher": null
    },
    {
      "event": "user_prompt_submit",
      "hookType": "command",
      "target": "'/opt/homebrew/bin/moshi-hook' claude-hook",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "pre_tool_use",
      "hookType": "command",
      "target": "'/opt/homebrew/bin/moshi-hook' claude-hook",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": "AskUserQuestion",
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "pre_tool_use",
      "hookType": "command",
      "target": "'/opt/homebrew/bin/moshi-hook' claude-hook",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": "ExitPlanMode",
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "pre_tool_use",
      "hookType": "command",
      "target": "[ -z \"${GROK_AGENT:-}${GROK_HOOK_EVENT:-}\" ] || exit 0; exec \"$CLAUDE_PROJECT_DIR\"/bin/fm-arm-pretool-check.sh --claude",
      "source": {
        "type": "project",
        "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/.claude"
      },
      "matcher": "Bash",
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "pre_tool_use",
      "hookType": "command",
      "target": "[ -z \"${GROK_AGENT:-}${GROK_HOOK_EVENT:-}\" ] || exit 0; exec \"$CLAUDE_PROJECT_DIR\"/bin/fm-cd-pretool-check.sh --claude",
      "source": {
        "type": "project",
        "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/.claude"
      },
      "matcher": "Bash",
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "pre_tool_use",
      "hookType": "command",
      "target": "\"$CLAUDE_PROJECT_DIR\"/bin/fm-subagent-pretool-check.sh --claude",
      "source": {
        "type": "project",
        "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/.claude"
      },
      "matcher": ".*",
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "pre_tool_use",
      "hookType": "command",
      "target": "bash -lc '[ -n \"${GROK_WORKSPACE_ROOT:-}\" ] || exit 0; exec \"${GROK_WORKSPACE_ROOT:-}/bin/fm-cd-pretool-check.sh\"'",
      "source": {
        "type": "project",
        "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/.grok/hooks"
      },
      "matcher": "Bash"
    },
    {
      "event": "pre_tool_use",
      "hookType": "command",
      "target": "bash -lc '[ -n \"${GROK_WORKSPACE_ROOT:-}\" ] || exit 0; exec \"${GROK_WORKSPACE_ROOT:-}/bin/fm-arm-pretool-check.sh\"'",
      "source": {
        "type": "project",
        "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/.grok/hooks"
      },
      "matcher": "Bash"
    },
    {
      "event": "post_tool_use",
      "hookType": "command",
      "target": "'/opt/homebrew/bin/moshi-hook' claude-hook",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": "AskUserQuestion",
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "post_tool_use",
      "hookType": "command",
      "target": "'/opt/homebrew/bin/moshi-hook' claude-hook",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": "ExitPlanMode",
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "stop",
      "hookType": "command",
      "target": "/usr/bin/env AGENT_TRACE_PLATFORM=claude AGENT_TRACE_ROOT=/Users/ivan/Documents/Codex/traces /usr/bin/python3 \"/Users/ivan/Projects/agents-setup/scripts/session-trace/enqueue.py\"",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "stop",
      "hookType": "command",
      "target": "'/opt/homebrew/bin/moshi-hook' claude-hook",
      "source": {
        "type": "user",
        "path": "/Users/ivan/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "stop",
      "hookType": "command",
      "target": "[ -z \"${GROK_AGENT:-}${GROK_HOOK_EVENT:-}\" ] || exit 0; exec \"$CLAUDE_PROJECT_DIR\"/bin/fm-turnend-guard.sh --claude",
      "source": {
        "type": "project",
        "path": "/Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0059YDTR0E27433AZEJBZNT/.claude"
      },
      "matcher": null,
      "vendor": "claude",
      "compatibilityStatus": "enabled"
    },
    {
      "event": "stop",
      "hookType": "command",
      "target": "[ -z 

... [54599 bytes truncated] ...

ityStatus": "enabled",
      "collidesWith": "using-git-worktrees",
      "invocableAs": "superpowers:using-git-worktrees"
    },
    {
      "name": "using-superpowers",
      "description": "Use when starting any conversation - establishes how to find and use skills, requiring skill invocation before ANY response including clarifying questions",
      "source": {
        "type": "plugin",
        "plugin_name": "superpowers",
        "path": "/Users/ivan/.claude/plugins/cache/claude-plugins-official/superpowers/6.3.0/skills/using-superpowers/SKILL.md"
      },
      "userInvocable": true,
      "vendor": "claude",
      "compatibilityStatus": "enabled",
      "collidesWith": "using-superpowers",
      "invocableAs": "superpowers:using-superpowers"
    },
    {
      "name": "verification-before-completion",
      "description": "Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always",
      "source": {
        "type": "plugin",
        "plugin_name": "superpowers",
        "path": "/Users/ivan/.claude/plugins/cache/claude-plugins-official/superpowers/6.3.0/skills/verification-before-completion/SKILL.md"
      },
      "userInvocable": true,
      "vendor": "claude",
      "compatibilityStatus": "enabled",
      "collidesWith": "verification-before-completion",
      "invocableAs": "superpowers:verification-before-completion"
    },
    {
      "name": "writing-plans",
      "description": "Use when you have a spec or requirements for a multi-step task, before touching code",
      "source": {
        "type": "plugin",
        "plugin_name": "superpowers",
        "path": "/Users/ivan/.claude/plugins/cache/claude-plugins-official/superpowers/6.3.0/skills/writing-plans/SKILL.md"
      },
      "userInvocable": true,
      "vendor": "claude",
      "compatibilityStatus": "enabled",
      "collidesWith": "writing-plans",
      "invocableAs": "superpowers:writing-plans"
    },
    {
      "name": "writing-skills",
      "description": "Use when creating new skills, editing existing skills, or verifying skills work before deployment",
      "source": {
        "type": "plugin",
        "plugin_name": "superpowers",
        "path": "/Users/ivan/.claude/plugins/cache/claude-plugins-official/superpowers/6.3.0/skills/writing-skills/SKILL.md"
      },
      "userInvocable": true,
      "vendor": "claude",
      "compatibilityStatus": "enabled",
      "collidesWith": "writing-skills",
      "invocableAs": "superpowers:writing-skills"
    }
  ],
  "agents": [
    {
      "name": "general-purpose",
      "description": "General purpose agent for multi-step tasks.",
      "source": {
        "type": "builtin"
      }
    },
    {
      "name": "explore",
      "description": "Fast, read-only agent specialized for codebase exploration.",
      "source": {
        "type": "builtin"
      }
    },
    {
      "name": "plan",
      "description": "Software architect for planning implementation strategies.",
      "source": {
        "type": "builtin"
      }
    }
  ],
  "plugins": [
    {
      "name": "compound-engineering",
      "scope": "user",
      "path": "/Users/ivan/.claude/plugins/marketplaces/compound-engineering-plugin/plugins/compound-engineering",
      "enabled": true,
      "provides": {
        "skills": 39,
        "agents": 1,
        "hooks": false,
        "mcpServers": 0
      }
    },
    {
      "name": "marketing-skills",
      "scope": "user",
      "path": "/Users/ivan/.claude/plugins/cache/marketingskills/marketing-skills/1.9.0",
      "enabled": true,
      "provides": {
        "skills": 40,
        "agents": 0,
        "hooks": false,
        "mcpServers": 0
      }
    },
    {
      "name": "superpowers",
      "scope": "user",
      "path": "/Users/ivan/.claude/plugins/cache/claude-plugins-official/superpowers/6.3.0",
      "enabled": true,
      "provides": {
        "skills": 14,
        "agents": 0,
        "hooks": true,
        "mcpServers": 0
      }
    },
    {
      "name": "shopify-plugin",
      "scope": "user",
      "path": "/Users/ivan/.claude/plugins/cache/shopify-ai-toolkit/shopify-plugin/1.2.1",
      "enabled": true,
      "provides": {
        "skills": 19,
        "agents": 0,
        "hooks": false,
        "mcpServers": 1
      }
    },
    {
      "name": "hindsight-memory",
      "scope": "user",
      "path": "/Users/ivan/.claude/plugins/cache/hindsight/hindsight-memory/0.6.4",
      "enabled": true,
      "provides": {
        "skills": 1,
        "agents": 0,
        "hooks": true,
        "mcpServers": 1
      }
    }
  ],
  "marketplaces": [],
  "mcpServers": [
    {
      "name": "linear-personal",
      "transport": "stdio",
      "target": "node",
      "source": {
        "type": "claudeJson",
        "path": "/Users/ivan/.claude.json"
      },
      "compatibilityStatus": "enabled",
      "vendor": "claude"
    },
    {
      "name": "linear-openally",
      "transport": "stdio",
      "target": "node",
      "source": {
        "type": "claudeJson",
        "path": "/Users/ivan/.claude.json"
      },
      "compatibilityStatus": "enabled",
      "vendor": "claude"
    },
    {
      "name": "im8-sot",
      "transport": "http",
      "target": "https://im8-source-of-truth.netlify.app/api/mcp",
      "source": {
        "type": "claudeJson",
        "path": "/Users/ivan/.claude.json"
      },
      "compatibilityStatus": "enabled",
      "vendor": "claude"
    },
    {
      "name": "apify",
      "transport": "http",
      "target": "https://mcp.apify.com",
      "source": {
        "type": "claudeJson",
        "path": "/Users/ivan/.claude.json"
      },
      "compatibilityStatus": "enabled",
      "vendor": "claude"
    },
    {
      "name": "mobbin",
      "transport": "http",
      "target": "https://api.mobbin.com/mcp",
      "source": {
        "type": "claudeJson",
        "path": "/Users/ivan/.claude.json"
      },
      "compatibilityStatus": "enabled",
      "vendor": "claude"
    },
    {
      "name": "linear-withally",
      "transport": "stdio",
      "target": "node",
      "source": {
        "type": "claudeJson",
        "path": "/Users/ivan/.claude.json"
      },
      "compatibilityStatus": "enabled",
      "vendor": "claude"
    }
  ],
  "lspServers": [],
  "configSources": {
    "layers": []
  },
  "externalCompat": {
    "remoteSettingsLoaded": false,
    "cells": [
      {
        "vendor": "cursor",
        "surface": "skills",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "cursor",
        "surface": "rules",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "cursor",
        "surface": "agents",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "cursor",
        "surface": "mcps",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "cursor",
        "surface": "hooks",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "cursor",
        "surface": "sessions",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "claude",
        "surface": "skills",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "claude",
        "surface": "rules",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "claude",
        "surface": "agents",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "claude",
        "surface": "mcps",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "claude",
        "surface": "hooks",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "claude",
        "surface": "sessions",
        "enabled": true,
        "source": "default"
      },
      {
        "vendor": "codex",
        "surface": "sessions",
        "enabled": true,
        "source": "default"
      }
    ]
  }
}
Evidence: Isolated Grok-inertness regression transcript

ok - tracked .claude/settings.json entries: 5 inert under Grok, the documented subagent exception still armed, all live under Claude

ok - tracked .claude/settings.json entries: 5 inert under Grok, the documented subagent exception still armed, all live under Claude
Evidence: Herdr presentation floor end-to-end demo (real adapter, stubbed herdr status)
=== Herdr presentation floor demo (real bin/backends/herdr.sh, stubbed 'herdr status') ===

$ unconfigured home, herdr 0.8.0 client+server (at floor)
  -> layout: PROJECTED

$ unconfigured home, herdr 0.7.5 (below floor) - first spawn
  -> layout: FLAT
  stderr: warning: herdr version 0.7.5 (protocol 17) is older than the 0.8.0 floor for presentation spaces, where projected cleanup can steal the active workspace; using the ordinary flat layout instead. Upgrade herdr to 0.8.0 or newer (herdr update) to restore the projection, or write "on" into config/herdr-presentation-spaces to force it on this release.

$ unconfigured home, herdr 0.7.5 - second spawn (dedup: no repeat warning)
  -> layout: FLAT

$ explicit 'on' opt-in, herdr 0.7.5 (below floor)
  -> layout: PROJECTED

$ explicit 'off', herdr 0.8.0 (at floor)
  -> layout: FLAT

$ unconfigured home, 0.8.0 client but selected running server still 0.7.5
  -> layout: FLAT
  stderr: warning: herdr server version 0.7.5 (protocol 17) is older than the 0.8.0 floor for presentation spaces, where projected cleanup can steal the active workspace; using the ordinary flat layout instead. Upgrade herdr to 0.8.0 or newer (herdr update) to restore the projection, or write "on" into config/herdr-presentation-spaces to force it on this release.

(dedup is enforced via per-release dot-markers in the state dir; the second 0.7.5 spawn above is silent while the distinct 0.7.5-server release still warned once)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ⚠️ bin/fm-spawn.sh:1666 - The journal-exists projection-recovery branch is gated behind fm_backend_herdr_presentation_enabled, so when the unconfigured default now resolves below the 0.8.0 floor, a same-ID restart with an existing state/<id>.herdr-presentation journal skips herdr_projection_existing_meta_allows_flat and fm_backend_herdr_projection_recovery_allows_flat entirely and flat-spawns a new pane. Homes on Herdr 0.7.x that were projecting under the previous unconditional default-on hit this automatically when this change lands: the old projected workspace is stranded and fm-teardown.sh later quarantines the journal ("no workspace cleanup was attempted"), contradicting docs/herdr-backend.md's new claim that cleanup for an existing projection "always runs and never strands a workspace, whatever release the home is on now." Recommend running the journal-exists recovery branch whenever the journal exists and letting the floor gate govern only the fresh-create branch.
  • ℹ️ bin/backends/herdr.sh:160 - fm_backend_herdr_version_at_least strips pre-release suffixes, so a version like 0.8.0-preview.* passes the 0.8.0 floor, and fm_backend_herdr_release_floor_verdict deliberately lets that version-only "above" verdict override a below-floor protocol. A hypothetical early 0.8.0 preview at protocol 18 without the focus fixes would therefore project by default. Measured 0.8.0 previews carry protocol 19 and the exact prior-tab restore remains the backstop for this purely visual feature, so this reads as an accepted tradeoff; noting it for the record.
  • ℹ️ docs/turnend-guard.md:61 - docs/turnend-guard.md rejects widening the guard to GROK_SESSION_ID because Grok injects it into every child process and it can survive into a Claude session Grok launched — but GROK_AGENT is likewise set for Grok child/tool processes, so a Claude session launched directly from a Grok Bash tool now also loses its SessionStart run and both Bash PreToolUse checks (previously only the Stop entries were GROK_AGENT-guarded). The user intent explicitly mandates all five entries inert under GROK_AGENT, so this is an intent-mandated residual leak worth having on record, not a defect.

🔧 Fix: run herdr projection recovery whenever journal exists
1 info still open:

  • ℹ️ bin/fm-spawn.sh:1676 - The fix's new invariant — journal-exists projection recovery runs regardless of the presentation preference or release floor (bin/fm-spawn.sh restructure, documented in docs/herdr-backend.md) — is not pinned by any deterministic test: the presentation e2e exercises recovery only with projection enabled, so a future refactor could silently re-gate the recovery branch. Given this repo's pattern of pinning fixed regressions (e.g. test_tracked_claude_entries_inert_under_grok in the sibling commit), a targeted case asserting recovery runs under explicit off / below-floor with an existing journal would protect the fix; non-blocking.
✅ **Test** - passed

✅ No issues found.

  • grok inspect --json from this worktree under grok 1.0.3 with an isolated trusted GROK_HOME, confirming 5 guarded Claude entries + the unguarded subagent exception + .grok/hooks counterparts
  • test_tracked_claude_entries_inert_under_grok in isolation (env-driven regression: 5 entries inert under GROK_HOOK_EVENT-only and GROK_AGENT=1, live under native Claude, subagent exception armed under Grok)
  • bash tests/fm-turnend-guard.test.sh (full suite, exit 0)
  • bash tests/fm-backend-herdr.test.sh (full suite incl. new floor, dedup, marker-safety, server-composition, and release-classifier tests, exit 0)
  • bash tests/fm-secondmate-harness.test.sh (full suite incl. presentation-preference inheritance, exit 0)
  • Manual end-to-end demo of fm_backend_herdr_presentation_enabled with a stubbed herdr status: default-on at 0.8.0, flat + warning at 0.7.5, dedup on repeat, explicit on below floor, explicit off at floor, below-floor running server keeps at-floor client flat
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

withally and others added 4 commits August 14, 2026 20:52
…e floor

The real-Herdr presentation E2E still asserted that an unconfigured home is
always projected, but the presentation floor added in this change keeps an
absent setting flat below Herdr 0.8.0, so the test failed on CI's herdr 0.7.4.
The default-on scenario now composes the same client and running-server
evidence as the adapter's gate: at or above the floor it keeps the projection
assertions, below it it asserts the flat firstmate placement, the floor
warning, and its per-release dedupe marker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@withally
withally merged commit 5ada9b0 into main Aug 14, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant