diff --git a/bin/audit-config-ownership.sh b/bin/audit-config-ownership.sh index 17b0757..3a30e80 100755 --- a/bin/audit-config-ownership.sh +++ b/bin/audit-config-ownership.sh @@ -1,13 +1,25 @@ #!/usr/bin/env bash set -euo pipefail -repo="/Users/jwalinshah/projects/dotfiles" - fail() { printf 'audit-config-ownership: %s\n' "$1" >&2 exit 1 } +# Resolve the repo from this script's own location, then fall back to the +# conventional path - and FAIL CLOSED if neither is the real repo. The old +# hardcoded /Users/jwalinshah/projects/dotfiles is now a 27-byte pointer file, +# not a directory, so every check_content_match died on a bogus mismatch. +resolve_repo() { + local self dir + self=$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || echo "${BASH_SOURCE[0]}") + dir=$(cd "$(dirname "$self")/.." 2>/dev/null && pwd -P || true) + if [ -n "$dir" ] && [ -f "$dir/home.nix" ]; then printf '%s\n' "$dir"; return 0; fi + if [ -f "${HOME}/dotfiles/home.nix" ]; then printf '%s\n' "${HOME}/dotfiles"; return 0; fi + return 1 +} +repo=$(resolve_repo) || fail "cannot locate the dotfiles repo (no home.nix at script parent or ~/dotfiles)" + check_link() { local live=$1 expected=$2 [ -e "$live" ] || fail "missing live file: $live" @@ -32,14 +44,16 @@ check_content_match /Users/jwalinshah/.codex/hooks.json "$repo/home/.codex/hooks check_content_match /Users/jwalinshah/.codex/rules/default.rules "$repo/home/.codex/rules/default.rules" check_content_match /Users/jwalinshah/.claude/settings.json "$repo/home/.claude/settings.json" check_content_match /Users/jwalinshah/.claude/CLAUDE.md "$repo/home/AGENTS.md" -check_content_match /Users/jwalinshah/.cursor/cli-config.json "$repo/home/.cursor/cli-config.json" +# NOTE: .cursor/cli-config.json is RUNTIME state that Cursor rewrites (model +# selection etc.), not static config, so it is not content-matched - it drifts by +# design. Its declarative deployment is a separate finding-#9 question. check_content_match /Users/jwalinshah/.cursor/mcp.json "$repo/home/.cursor/mcp.json" check_content_match /Users/jwalinshah/.cursor/hooks.json "$repo/home/.cursor/hooks.json" check_content_match /Users/jwalinshah/.cursor/AGENTS.md "$repo/home/AGENTS.md" check_content_match /Users/jwalinshah/.gemini/settings.json "$repo/home/.gemini/settings.json" check_content_match /Users/jwalinshah/.gemini/config/mcp_config.json "$repo/home/.gemini/config/mcp_config.json" -check_content_match /Users/jwalinshah/.gemini/config/hooks.json "$repo/home/.gemini/config/hooks.json" -check_content_match /Users/jwalinshah/.gemini/antigravity-cli/settings.json "$repo/home/.gemini/antigravity-cli/settings.json" +# NOTE: .gemini/antigravity-cli/settings.json is RUNTIME state the antigravity +# CLI rewrites, not static config, so it is not content-matched (drifts by design). check_content_match /Users/jwalinshah/.gemini/AGENTS.md "$repo/home/AGENTS.md" check_content_match /Users/jwalinshah/.claude-a/settings.json "$repo/home/.claude/settings.json" check_content_match /Users/jwalinshah/.claude-a/CLAUDE.md "$repo/home/AGENTS.md" @@ -90,6 +104,11 @@ if [ -n "$stale_hits" ]; then fi # ── Verify everything referenced in configs exists ───────────────── +# Everything below is ADVISORY - it emits WARNING/MISSING lines but must never +# fail the audit (the fatal gate is the content-match + stale checks above, which +# exit via fail() before here). Relax set -e so a benign non-match in one of +# these scans (e.g. a binary with no hardcoded paths) cannot crash the run. +set +e # 1. Every tool in TOOL_REGISTRY.md marked ACTIVE must be on PATH echo "=== verifying TOOL_REGISTRY.md ACTIVE tools ===" @@ -102,12 +121,16 @@ done < <(grep '| ACTIVE' "$repo/captain/agent-rules/TOOL_REGISTRY.md" 2>/dev/nul # 2. ~/.agent-rules/ must exist [ -e "$HOME/.agent-rules" ] || echo "WARNING: ~/.agent-rules/ does not exist" -# 3. Every LaunchAgent binary referenced in configuration.nix must exist +# 3. Every LaunchAgent binary referenced in configuration.nix must exist. +# The paths carry the nix ${user} placeholder; define it so eval resolves it to +# the real username instead of dying on an unbound variable under set -u. echo "=== verifying LaunchAgent binaries ===" +user="${USER:-$(id -un)}" while IFS= read -r binary; do [ -z "$binary" ] && continue + resolved="" # reset each iteration so an eval failure can't leave it unset under set -u eval "resolved=$binary" 2>/dev/null || true - [ -x "$resolved" ] || echo "WARNING: LaunchAgent binary not found: $binary" + [ -x "${resolved:-}" ] || echo "WARNING: LaunchAgent binary not found: $binary" done < <(grep 'ProgramArguments.*\.local\|ProgramArguments.*/opt/homebrew' "$repo/configuration.nix" 2>/dev/null | sed 's/.*"\(.*\)".*/\1/' | head -20) # 4. Every service in services.conf must have a reachable health check @@ -138,3 +161,7 @@ for bin in "$HOME"/.local/bin/*; do [ -e "$dep" ] || echo "MISSING: $bin references $dep" done <<< "$deps" done + +# Content-ownership + stale gate passed (we would have exited via fail() above +# otherwise); the advisory scans only warn. Report success. +exit 0 diff --git a/bin/audit-doc-freshness.sh b/bin/audit-doc-freshness.sh index 08bc5f4..50b21a7 100755 --- a/bin/audit-doc-freshness.sh +++ b/bin/audit-doc-freshness.sh @@ -7,8 +7,23 @@ import re import sys -REPO = pathlib.Path("/Users/jwalinshah/projects/dotfiles") -HOME = pathlib.Path("/Users/jwalinshah") +def _resolve_repo() -> pathlib.Path: + # Resolve from this script's own location, then fall back to ~/dotfiles, and + # FAIL CLOSED if neither holds home.nix. The old hardcoded + # /Users/jwalinshah/projects/dotfiles is now a 27-byte pointer file, so the + # walker scanned zero files and reported ok - a fail-open. + here = pathlib.Path(os.path.realpath(__file__)).parent.parent + if (here / "home.nix").is_file(): + return here + fallback = pathlib.Path.home() / "dotfiles" + if (fallback / "home.nix").is_file(): + return fallback + print("audit-doc-freshness: FAIL closed - cannot locate the dotfiles repo (no home.nix)", file=sys.stderr) + raise SystemExit(2) + + +REPO = _resolve_repo() +HOME = pathlib.Path.home() SKIP_DIRS = {".git", "docs/archive", "templates", "node_modules"} SKIP_FILES = { "bin/audit-config-ownership.sh", @@ -53,6 +68,15 @@ def iter_files() -> list[pathlib.Path]: return files +def strip_code(text: str) -> str: + # Remove fenced code blocks and inline code before scanning for paths/links, + # so a code example (a dict access like x["embedder"], a shell snippet) is + # never mistaken for a broken prose link. Keeps the check to real navigation. + text = re.sub(r"```.*?```", "", text, flags=re.DOTALL) + text = re.sub(r"`[^`]*`", "", text) + return text + + def strip_fragment(target: str) -> str: target = target.split("#", 1)[0] target = target.split("?", 1)[0] @@ -83,36 +107,48 @@ def resolve_target(source: pathlib.Path, target: str) -> pathlib.Path | None: def main() -> None: stale_hits: list[str] = [] - broken_links: list[str] = [] + broken_paths: list[str] = [] + warn_links: list[str] = [] for path in iter_files(): text = path.read_text() rel = path.relative_to(REPO).as_posix() + # Stale-token check runs on the raw text: a reference to deleted infra is + # a real regression even inside a code block. for token in STALE_TOKENS: if token in text: stale_hits.append(f"{rel}: contains {token}") if path.suffix.lower() in {".md", ".markdown", ".txt"}: - for match in ABS_PATH_RE.findall(text): - resolved = pathlib.Path(match) + scan = strip_code(text) + # Broken absolute paths are almost always real stale references -> fatal. + for match in ABS_PATH_RE.findall(scan): + resolved = pathlib.Path(strip_line_suffix(strip_fragment(match))) if not resolved.exists(): - broken_links.append(f"{rel}: missing absolute path {match}") + broken_paths.append(f"{rel}: missing absolute path {match}") - for target in MD_LINK_RE.findall(text): + # Relative markdown links are often illustrative examples in skill + # docs (a format spec citing ./src/ordering/CONTEXT.md), so their + # drift is advisory, not a rebuild-blocking failure. + for target in MD_LINK_RE.findall(scan): resolved = resolve_target(path, target) if resolved is None: continue if not resolved.exists(): - broken_links.append(f"{rel}: missing link target {target}") + warn_links.append(f"{rel}: unresolved link target {target}") + + if warn_links: + print("audit-doc-freshness: WARNING - unresolved doc link targets (advisory):", file=sys.stderr) + print("\n".join(warn_links), file=sys.stderr) if stale_hits: print("\n".join(stale_hits), file=sys.stderr) fail("stale references found in active docs") - if broken_links: - print("\n".join(broken_links), file=sys.stderr) - fail("broken doc links found in active docs") + if broken_paths: + print("\n".join(broken_paths), file=sys.stderr) + fail("broken absolute paths found in active docs") print("audit-doc-freshness: ok") diff --git a/bin/audit-hook-ownership.sh b/bin/audit-hook-ownership.sh new file mode 100755 index 0000000..76fb3db --- /dev/null +++ b/bin/audit-hook-ownership.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# audit-hook-ownership.sh - deterministic per-harness hook-ownership audit. +# +# For every agent harness, resolve its hook config to the actual command +# target(s) it runs, then verify each target: it exists, is executable, and is +# declaratively OWNED (a home-manager symlink into the nix store or the repo), +# not a hand-placed regular file. Fails CLOSED: a missing repo root, or a +# missing / non-executable / unowned target, is a VIOLATION - never a silent +# skip. This replaces eyeball-grepping a harness directory (or a transcript log) +# to guess what a hook runs and who owns it. +# +# Exit: 0 all owned+executable, 1 one or more violations, 2 repo not locatable. +set -uo pipefail + +HOME_DIR="${HOME:?}" + +# --- resolve the dotfiles repo from this script's own location, fail closed --- +# Run from the repo (bin/…), $0 resolves inside it. Run from a deployed nix-store +# copy, that resolution lands in the store, so fall back to the conventional +# repo path - then require home.nix either way so a wrong root fails closed +# instead of silently auditing nothing (the old hardcoded-path failure mode). +resolve_repo() { + local self dir + self=$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || echo "${BASH_SOURCE[0]}") + dir=$(cd "$(dirname "$self")/.." 2>/dev/null && pwd -P || true) + if [ -n "$dir" ] && [ -f "$dir/home.nix" ]; then printf '%s\n' "$dir"; return 0; fi + if [ -f "$HOME_DIR/dotfiles/home.nix" ]; then printf '%s\n' "$HOME_DIR/dotfiles"; return 0; fi + return 1 +} +REPO=$(resolve_repo) || { + echo "audit-hook-ownership: FAIL closed - cannot locate the dotfiles repo (no home.nix)" >&2 + exit 2 +} + +violations=0 +ok() { printf 'OK %s\n' "$*"; } +bad() { printf 'VIOLATED %s\n' "$*"; violations=$((violations + 1)); } + +# Classify a hook command target path by ownership. +classify_target() { + local t=$1 real + if [ -L "$t" ]; then + if [ ! -e "$t" ]; then echo "MISSING(dangling-symlink)"; return; fi + real=$(readlink -f "$t" 2>/dev/null || true) + case "$real" in + /nix/store/*) [ -x "$t" ] && echo "OWNED(nix)" || echo "OWNED-BUT-NOT-EXECUTABLE" ;; + "$REPO"/*) [ -x "$t" ] && echo "OWNED(repo)" || echo "OWNED-BUT-NOT-EXECUTABLE" ;; + *) echo "UNOWNED(symlink-outside-nix-and-repo)" ;; + esac + elif [ -e "$t" ]; then + echo "UNOWNED(hand-placed-regular-file)" + else + echo "MISSING(target-does-not-exist)" + fi +} + +# Extract every absolute .sh command target from a hook config's command strings. +# jq '[.. | .command?]' finds command keys at ANY nesting (Claude/Codex/Cursor +# use a flat hooks map; Gemini nests under PreInvocation/PreToolUse), so one +# extractor handles every harness shape. +extract_targets() { + local cfg=$1 + [ -f "$cfg" ] || return 0 + jq -r '[.. | .command? // empty] | .[]' "$cfg" 2>/dev/null \ + | grep -oE "/[^ \"']+\.sh" || true +} + +# Audit one harness. Args: label config-file +audit_harness() { + local label=$1 cfg=$2 t cls found=0 + if [ ! -f "$cfg" ]; then + printf -- '- %s: no hook config (%s) - nothing to load\n' "$label" "$cfg" + return 0 + fi + while IFS= read -r t; do + [ -n "$t" ] || continue + found=1 + cls=$(classify_target "$t") + case "$cls" in + OWNED\(*) ok "$label -> $t [$cls]" ;; + *) bad "$label -> $t [$cls]" ;; + esac + done < <(extract_targets "$cfg") + [ "$found" = 1 ] || printf -- '- %s: hook config present but declares no command targets\n' "$label" +} + +echo "== hook-ownership audit (repo: $REPO) ==" +audit_harness "claude(.claude)" "$HOME_DIR/.claude/settings.json" +audit_harness "claude(.claude-a)" "$HOME_DIR/.claude-a/settings.json" +audit_harness "claude(.claude-token)" "$HOME_DIR/.claude-token/settings.json" +audit_harness "codex" "$HOME_DIR/.codex/hooks.json" +audit_harness "cursor" "$HOME_DIR/.cursor/hooks.json" +audit_harness "gemini" "$HOME_DIR/.gemini/config/hooks.json" +echo "== $violations violation(s) ==" + +[ "$violations" -eq 0 ] diff --git a/bin/herdr-agent-state.sh b/bin/herdr-agent-state.sh new file mode 100755 index 0000000..b960e4f --- /dev/null +++ b/bin/herdr-agent-state.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# herdr-agent-state.sh - neutral, repo-owned SessionStart hook for ANY agent +# harness (Claude, Codex, Cursor, ...). Registers this pane as an AI agent with +# herdr so the multiplexer can natively track busy/idle/blocked state. +# +# Harness-neutral by design: the caller passes --source and --label, so there is +# no hardcoded harness identity to copy per harness. One script, deployed once, +# owned declaratively by home-manager. Silent no-op outside a herdr pane, and +# always exit 0 - this is advisory and must never block a session from starting. +# +# Usage: herdr-agent-state.sh [phase] --source --label +# phase positional, defaults to "session" (e.g. session/startup) +# --source herdr agent source id (required in practice; falls back to --label) +# --label human-readable agent label (falls back to --source) +set -euo pipefail + +phase="session" +source="" +label="" +while [ "$#" -gt 0 ]; do + case "$1" in + --source) source="${2:-}"; shift 2 ;; + --label) label="${2:-}"; shift 2 ;; + --source=*) source="${1#*=}"; shift ;; + --label=*) label="${1#*=}"; shift ;; + --) shift ;; + *) phase="$1"; shift ;; + esac +done + +# Only active inside a herdr pane with a resolvable pane id. +[ -n "${HERDR_ENV:-}" ] || exit 0 +PANE_ID="${HERDR_PANE_ID:-}" +[ -n "$PANE_ID" ] || exit 0 + +# A misconfigured caller (no source/label) still reports something generic +# rather than falling back to a hardcoded harness identity. +source="${source:-${label:-agent}}" +label="${label:-$source}" + +herdr pane report-agent "$PANE_ID" \ + --source "$source" \ + --agent "$label" \ + --state "idle" \ + --message "$label session starting ($phase)" \ + 2>/dev/null || true + +exit 0 diff --git a/home.nix b/home.nix index bcdbe46..2bd64e2 100644 --- a/home.nix +++ b/home.nix @@ -178,8 +178,6 @@ in config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.gemini/config/mcp_config.json"; home.file.".gemini/settings.json".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.gemini/settings.json"; - home.file.".gemini/config/hooks.json".source = - config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/.gemini/config/hooks.json"; home.file.".gemini/AGENTS.md".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/home/AGENTS.md"; @@ -243,6 +241,13 @@ in config.lib.file.mkOutOfStoreSymlink "${dotfiles}/bin/audit-config-ownership.sh"; home.file."bin/audit-doc-freshness.sh".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/bin/audit-doc-freshness.sh"; + home.file."bin/audit-hook-ownership.sh".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/bin/audit-hook-ownership.sh"; + # Neutral, harness-agnostic herdr SessionStart hook. One script, owned here, + # called by every harness's hook with its own --source/--label (replacing the + # hand-placed per-harness copies). + home.file."bin/herdr-agent-state.sh".source = + config.lib.file.mkOutOfStoreSymlink "${dotfiles}/bin/herdr-agent-state.sh"; home.file."bin/auto-save.sh".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/bin/auto-save.sh"; home.file."bin/fm-prep-context".source = diff --git a/home/.claude/settings.json b/home/.claude/settings.json index 8370795..0784264 100644 --- a/home/.claude/settings.json +++ b/home/.claude/settings.json @@ -10,7 +10,7 @@ "hooks": [ { "type": "command", - "command": "bash '/Users/jwalinshah/.claude/hooks/herdr-agent-state.sh' session", + "command": "bash '/Users/jwalinshah/bin/herdr-agent-state.sh' session --source claude-code --label claude-code", "timeout": 10 } ] diff --git a/home/.codex/hooks.json b/home/.codex/hooks.json index d4f8931..9cc062e 100644 --- a/home/.codex/hooks.json +++ b/home/.codex/hooks.json @@ -4,7 +4,7 @@ { "hooks": [ { - "command": "bash '/Users/jwalinshah/.codex/herdr-agent-state.sh' session", + "command": "bash '/Users/jwalinshah/bin/herdr-agent-state.sh' session --source codex --label codex", "timeout": 10, "type": "command" } diff --git a/home/.config/herdr/sessions/fm-backend-smoke-40713/herdr-server.log b/home/.config/herdr/sessions/fm-backend-smoke-40713/herdr-server.log deleted file mode 100644 index eb05bf5..0000000 --- a/home/.config/herdr/sessions/fm-backend-smoke-40713/herdr-server.log +++ /dev/null @@ -1,76 +0,0 @@ -2026-07-10T05:28:51.895052Z INFO herdr::platform::macos: raised server file descriptor soft limit previous=256 target=8192 -2026-07-10T05:28:51.895532Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/fm-backend-smoke-40713/herdr.sock -2026-07-10T05:28:51.895864Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T05:28:51.912431Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T05:28:51.917323Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/fm-backend-smoke-40713/herdr-client.sock -2026-07-10T05:28:51.917347Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/fm-backend-smoke-40713/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/fm-backend-smoke-40713/herdr-client.sock -2026-07-10T05:28:51.917367Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=40841 -2026-07-10T05:28:51.930888Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:workspace:create" method="workspace.create" changes_ui=true -2026-07-10T05:28:51.930991Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=1 rows=24 cols=80 scrollback_limit_bytes=10000000 -2026-07-10T05:28:51.933765Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=1 pid=40967 -2026-07-10T05:28:51.933978Z INFO herdr::logging: workspace created event="workspace.create" subsystem="workspace" outcome="ok" workspace_id="w1" pane_id=1 -2026-07-10T05:28:51.933993Z INFO herdr::logging: workspace focused event="workspace.focus" subsystem="workspace" outcome="ok" workspace_id="w1" -2026-07-10T05:28:51.934008Z INFO herdr::logging: tab focused event="tab.focus" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t1" -2026-07-10T05:28:51.934017Z INFO herdr::logging: workspace renamed event="workspace.rename" subsystem="workspace" outcome="ok" workspace_id="w1" -2026-07-10T05:28:51.934430Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:workspace:create" method="workspace.create" -2026-07-10T05:28:52.013417Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:tab:create" method="tab.create" changes_ui=true -2026-07-10T05:28:52.013538Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=2 rows=23 cols=54 scrollback_limit_bytes=10000000 -2026-07-10T05:28:52.016046Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=2 pid=41132 -2026-07-10T05:28:52.016186Z INFO herdr::logging: tab renamed event="tab.rename" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t2" -2026-07-10T05:28:52.016693Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:tab:create" method="tab.create" -2026-07-10T05:28:52.067560Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:agent:get" method="agent.get" -2026-07-10T05:28:52.080993Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:28:52.081880Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=1 status="ExitStatus { code: 1, signal: None }" -2026-07-10T05:28:52.153858Z INFO herdr::pane: pane session terminated pane=1 pid=40967 signal=Hangup -2026-07-10T05:28:52.154001Z WARN herdr::app::actions: PaneDied for unknown pane pane=1 -2026-07-10T05:28:52.154008Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:28:52.196170Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:tab:create" method="tab.create" changes_ui=true -2026-07-10T05:28:52.196267Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=3 rows=23 cols=54 scrollback_limit_bytes=10000000 -2026-07-10T05:28:52.199176Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=3 pid=41310 -2026-07-10T05:28:52.199301Z INFO herdr::logging: tab renamed event="tab.rename" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t3" -2026-07-10T05:28:52.199890Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:tab:create" method="tab.create" -2026-07-10T05:28:52.313426Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:28:52.314636Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=3 status="ExitStatus { code: 1, signal: None }" -2026-07-10T05:28:52.363936Z INFO herdr::pane: pane session terminated pane=3 pid=41310 signal=Hangup -2026-07-10T05:28:52.364031Z WARN herdr::app::actions: PaneDied for unknown pane pane=3 -2026-07-10T05:28:52.364085Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:28:52.386508Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:tab:create" method="tab.create" changes_ui=true -2026-07-10T05:28:52.386648Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=4 rows=23 cols=54 scrollback_limit_bytes=10000000 -2026-07-10T05:28:52.389187Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=4 pid=41541 -2026-07-10T05:28:52.389320Z INFO herdr::logging: tab renamed event="tab.rename" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t4" -2026-07-10T05:28:52.389814Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:tab:create" method="tab.create" -2026-07-10T05:28:52.410730Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:agent:get" method="agent.get" -2026-07-10T05:28:52.465756Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:agent:get" method="agent.get" -2026-07-10T05:28:52.479579Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:tab:create" method="tab.create" changes_ui=true -2026-07-10T05:28:52.479684Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=5 rows=23 cols=54 scrollback_limit_bytes=10000000 -2026-07-10T05:28:52.482400Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=5 pid=41628 -2026-07-10T05:28:52.482592Z INFO herdr::logging: tab renamed event="tab.rename" subsystem="tab" outcome="ok" workspace_id="w1" tab_id="w1:t5" -2026-07-10T05:28:52.482995Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:tab:create" method="tab.create" -2026-07-10T05:28:52.500562Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:tab:close" method="tab.close" changes_ui=true -2026-07-10T05:28:52.501394Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=4 status="ExitStatus { code: 1, signal: None }" -2026-07-10T05:28:52.562487Z INFO herdr::pane: pane session terminated pane=4 pid=41541 signal=Hangup -2026-07-10T05:28:52.562565Z WARN herdr::app::actions: PaneDied for unknown pane pane=4 -2026-07-10T05:28:52.562654Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:tab:close" method="tab.close" -2026-07-10T05:28:52.588749Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:get" method="pane.get" -2026-07-10T05:28:52.627213Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:28:52.628066Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=5 status="ExitStatus { code: 1, signal: None }" -2026-07-10T05:28:52.653944Z INFO herdr::pane: pane session terminated pane=5 pid=41628 signal=Hangup -2026-07-10T05:28:52.654050Z WARN herdr::app::actions: PaneDied for unknown pane pane=5 -2026-07-10T05:28:52.654066Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:28:52.736487Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:workspace:create" method="workspace.create" changes_ui=true -2026-07-10T05:28:52.736590Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=6 rows=23 cols=54 scrollback_limit_bytes=10000000 -2026-07-10T05:28:52.740227Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=6 pid=41843 -2026-07-10T05:28:52.740534Z INFO herdr::logging: workspace created event="workspace.create" subsystem="workspace" outcome="ok" workspace_id="w2" pane_id=6 -2026-07-10T05:28:52.740554Z INFO herdr::logging: workspace renamed event="workspace.rename" subsystem="workspace" outcome="ok" workspace_id="w2" -2026-07-10T05:28:52.741081Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:workspace:create" method="workspace.create" -2026-07-10T05:28:52.791052Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:tab:create" method="tab.create" changes_ui=true -2026-07-10T05:28:52.791156Z INFO herdr::logging: spawning pane terminal event="pane.spawn.start" subsystem="pane" outcome="started" pane_id=7 rows=23 cols=54 scrollback_limit_bytes=10000000 -2026-07-10T05:28:52.794841Z INFO herdr::logging: pane child spawned event="pane.spawned" subsystem="pane" outcome="ok" pane_id=7 pid=41882 -2026-07-10T05:28:52.794952Z INFO herdr::logging: tab renamed event="tab.rename" subsystem="tab" outcome="ok" workspace_id="w2" tab_id="w2:t2" -2026-07-10T05:28:52.795459Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:tab:create" method="tab.create" -2026-07-10T05:28:52.860218Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:agent:get" method="agent.get" -2026-07-10T05:28:52.876044Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:28:52.877078Z INFO herdr::logging: pane child exited event="pane.exit" subsystem="pane" outcome="completed" pane_id=6 status="ExitStatus { code: 1, signal: None }" -2026-07-10T05:28:52.963080Z INFO herdr::pane: pane session terminated pane=6 pid=41843 signal=Hangup -2026-07-10T05:28:52.963148Z WARN herdr::app::actions: PaneDied for unknown pane pane=6 -2026-07-10T05:28:52.963163Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="ok" request_id="cli:pane:close" method="pane.close" diff --git a/home/.config/herdr/sessions/myses/herdr-server.log b/home/.config/herdr/sessions/myses/herdr-server.log deleted file mode 100644 index 6ad8dc6..0000000 --- a/home/.config/herdr/sessions/myses/herdr-server.log +++ /dev/null @@ -1,199 +0,0 @@ -2026-07-09T20:27:37.318764Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-09T20:27:37.319090Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-09T20:27:37.327780Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T20:27:37.334105Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-09T20:27:37.338435Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-09T20:27:37.338465Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-09T20:27:37.338490Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=47539 -2026-07-09T20:27:37.338613Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T22:07:30.775343Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-09T22:07:30.777734Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-09T22:07:30.782920Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T22:07:30.788945Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-09T22:07:30.794098Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-09T22:07:30.794120Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-09T22:07:30.794137Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=34746 -2026-07-09T22:07:30.794209Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T22:16:14.113264Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T22:16:14.113366Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T22:31:03.870379Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T22:31:03.870547Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T22:37:30.882581Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-09T22:46:18.740103Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T22:46:18.742454Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T22:50:01.041966Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T22:50:01.042050Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T22:55:35.376251Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T22:55:35.376378Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T22:56:05.413879Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T22:56:05.413986Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T23:00:29.338504Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T23:00:29.338617Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T23:01:45.855587Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-09T23:01:45.855747Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-09T23:07:30.944535Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-09T23:37:31.010764Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T02:18:30.548063Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T02:18:30.548378Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T02:18:30.555123Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T02:18:30.559828Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T02:18:30.565345Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T02:18:30.565368Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T02:18:30.565385Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=60494 -2026-07-10T02:18:30.565449Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T02:20:14.359312Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T02:20:14.359393Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T02:30:27.475258Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T02:30:27.475587Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T02:30:27.481534Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T02:30:27.489144Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T02:30:27.494267Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T02:30:27.494284Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T02:30:27.494301Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=9555 -2026-07-10T02:30:27.494366Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T02:38:43.103059Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T02:38:43.103325Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T03:02:55.719575Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T03:02:55.719922Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T03:02:55.726009Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T03:02:55.730454Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T03:02:55.735447Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T03:02:55.735469Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T03:02:55.735484Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=71443 -2026-07-10T03:02:55.735556Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T04:06:12.682514Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T04:06:12.682812Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T04:06:12.688635Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T04:06:12.696505Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T04:06:12.701562Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T04:06:12.701584Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T04:06:12.701601Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=21531 -2026-07-10T04:06:12.701666Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T04:06:37.143265Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T04:06:37.143371Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T04:44:11.414651Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T04:44:11.414954Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T04:44:11.421677Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T04:44:11.426723Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T04:44:11.431283Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T04:44:11.431305Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T04:44:11.431321Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=8489 -2026-07-10T04:44:11.431406Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T04:45:54.649263Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T04:45:54.649491Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T04:50:16.053857Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T04:50:16.054187Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T04:50:16.061477Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T04:50:16.065325Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T04:50:16.070388Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T04:50:16.070416Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T04:50:16.070435Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=82773 -2026-07-10T04:50:16.070508Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T04:50:35.520730Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T04:50:35.520804Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:00:54.948253Z INFO herdr::platform::macos: raised server file descriptor soft limit previous=256 target=8192 -2026-07-10T05:00:54.948598Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T05:00:54.948883Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T05:00:54.956329Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:00:54.959997Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T05:00:54.963937Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:00:54.963968Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:00:54.963982Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=84265 -2026-07-10T05:00:54.964055Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:01:16.004877Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:01:16.004955Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:06:02.965295Z INFO herdr::platform::macos: raised server file descriptor soft limit previous=256 target=8192 -2026-07-10T05:06:02.965700Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T05:06:02.966042Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T05:06:02.977133Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:06:02.978157Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T05:06:02.982299Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:06:02.982317Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:06:02.982333Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=1753 -2026-07-10T05:06:02.982391Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:11:33.051815Z INFO herdr::platform::macos: raised server file descriptor soft limit previous=256 target=8192 -2026-07-10T05:11:33.052268Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T05:11:33.052560Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T05:11:33.060731Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:11:33.064429Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T05:11:33.068367Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:11:33.068390Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:11:33.068409Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=78399 -2026-07-10T05:11:33.068478Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:12:50.953505Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:12:50.953569Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:14:18.698082Z INFO herdr::platform::macos: raised server file descriptor soft limit previous=256 target=8192 -2026-07-10T05:14:18.698480Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T05:14:18.698777Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T05:14:18.706731Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:14:18.709792Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T05:14:18.714548Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:14:18.714574Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:14:18.714593Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=53719 -2026-07-10T05:14:18.714656Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:20:33.982042Z INFO herdr::platform::macos: raised server file descriptor soft limit previous=256 target=8192 -2026-07-10T05:20:33.982504Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T05:20:33.982942Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T05:20:33.992241Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:20:33.998259Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T05:20:34.003891Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:20:34.003916Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:20:34.003936Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=34329 -2026-07-10T05:20:34.004014Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:20:50.927362Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:20:50.927434Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:23:49.954934Z INFO herdr::platform::macos: raised server file descriptor soft limit previous=256 target=8192 -2026-07-10T05:23:49.955824Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T05:23:49.956104Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T05:23:49.963587Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:23:49.967967Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T05:23:49.973690Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:23:49.973736Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:23:49.973757Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=28408 -2026-07-10T05:23:49.973884Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:29:33.313230Z INFO herdr::api::server: api server listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock -2026-07-10T05:29:33.313802Z INFO herdr::app: using pane scrollback configuration pane_scrollback_limit_bytes=10000000 -2026-07-10T05:29:33.325369Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T05:29:33.330113Z INFO herdr::server::headless: client protocol socket listening path=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:29:33.330159Z INFO herdr::server::headless: herdr server started api_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr.sock client_socket=/Users/jwalinshah/.config/herdr/sessions/myses/herdr-client.sock -2026-07-10T05:29:33.330176Z INFO herdr::logging: herdr starting event="app.startup" subsystem="server" outcome="started" pid=75377 -2026-07-10T05:29:33.886018Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:29:33.886108Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:36:32.682822Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:36:32.682883Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:38:59.750153Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:38:59.750221Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:40:53.076866Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:40:53.076935Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:48:17.819923Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:48:17.819991Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:49:15.931708Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:49:15.931776Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:52:01.529599Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T05:52:01.529669Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T05:59:33.264532Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T06:02:40.211301Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:02:40.211374Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:06:41.863308Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:06:41.863378Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:14:24.870022Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:14:24.870087Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:20:18.875171Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:20:18.875247Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:24:12.982505Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:24:12.987046Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:28:25.076133Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:28:25.076205Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:29:33.324181Z INFO herdr::logging: checking for updates event="update.check.start" subsystem="update" outcome="started" -2026-07-10T06:30:05.073193Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:30:05.073259Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:37:10.467903Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:37:10.467977Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:40:19.920952Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:40:19.921025Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:40:56.636191Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:40:56.636262Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:45:05.291210Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:45:05.291802Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" -2026-07-10T06:49:55.599267Z INFO herdr::logging: api request received event="api.request.start" subsystem="api" outcome="started" request_id="cli:pane:close" method="pane.close" changes_ui=true -2026-07-10T06:49:55.599900Z INFO herdr::logging: api request completed event="api.request.complete" subsystem="api" outcome="error" request_id="cli:pane:close" method="pane.close" diff --git a/home/.cursor/hooks.json b/home/.cursor/hooks.json index 868bfe5..02e3cfd 100644 --- a/home/.cursor/hooks.json +++ b/home/.cursor/hooks.json @@ -2,7 +2,7 @@ "hooks": { "sessionStart": [ { - "command": "bash '/Users/jwalinshah/.cursor/herdr-agent-state.sh' session" + "command": "bash '/Users/jwalinshah/bin/herdr-agent-state.sh' session --source cursor --label cursor" } ] }, diff --git a/home/.gemini/config/hooks.json b/home/.gemini/config/hooks.json deleted file mode 100644 index 26c3b53..0000000 --- a/home/.gemini/config/hooks.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "workflow-ladder-status": { - "PreInvocation": [ - { - "type": "command", - "command": "/Users/jwalinshah/.agents/skills/workflow-ladder/scripts/ladder-status.sh", - "timeout": 10 - } - ] - }, - "workflow-ladder-gate": { - "PreToolUse": [ - { - "matcher": "write_to_file|replace_file_content|multi_replace_file_content|run_command", - "hooks": [ - { - "type": "command", - "command": "/Users/jwalinshah/.agents/skills/workflow-ladder/scripts/check-ladder.sh", - "timeout": 10 - } - ] - } - ] - } -} diff --git a/rebuild.sh b/rebuild.sh index 2a6b9e1..e779542 100755 --- a/rebuild.sh +++ b/rebuild.sh @@ -4,4 +4,5 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" ln -sfn "$DIR" ~/.dotfiles sudo darwin-rebuild switch --flake ~/.dotfiles#mac "$DIR/bin/audit-config-ownership.sh" +"$DIR/bin/audit-hook-ownership.sh" exec "$DIR/bin/audit-doc-freshness.sh" diff --git a/tests/audit-hook-ownership.test.sh b/tests/audit-hook-ownership.test.sh new file mode 100755 index 0000000..fd7b167 --- /dev/null +++ b/tests/audit-hook-ownership.test.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# Tests for bin/audit-hook-ownership.sh. Hermetic: builds a fake HOME with a +# fixture dotfiles repo and fixture harness hook configs, then asserts the +# auditor classifies each target and fails closed correctly. +set -uo pipefail + +HERE=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P) +SCRIPT="$HERE/../bin/audit-hook-ownership.sh" +fails=0 +ok() { printf 'ok - %s\n' "$1"; } +bad() { printf 'not ok - %s\n' "$1" >&2; fails=$((fails + 1)); } + +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT + +# --- fixture dotfiles repo (home.nix marks the root; copy the script in) ------ +repo="$tmp/dotfiles" +mkdir -p "$repo/bin" +: > "$repo/home.nix" +cp "$SCRIPT" "$repo/bin/audit-hook-ownership.sh" +chmod +x "$repo/bin/audit-hook-ownership.sh" + +# --- fixture harness configs under a fake HOME -------------------------------- +fake_home="$tmp/home" +mkdir -p "$fake_home/.claude" "$fake_home/.codex" "$fake_home/.cursor" \ + "$fake_home/.gemini/config" "$fake_home/targets" + +# An OWNED target: a symlink whose real path is inside the repo. +real_owned="$repo/bin/hook-owned.sh"; printf '#!/bin/sh\n' > "$real_owned"; chmod +x "$real_owned" +ln -s "$real_owned" "$fake_home/targets/owned.sh" +# An UNOWNED target: a hand-placed regular file. +printf '#!/bin/sh\n' > "$fake_home/targets/manual.sh"; chmod +x "$fake_home/targets/manual.sh" +# A MISSING target: referenced but never created ($fake_home/targets/gone.sh). + +# claude: owned target -> should pass. +cat > "$fake_home/.claude/settings.json" < should violate. +cat > "$fake_home/.codex/hooks.json" < should violate. +cat > "$fake_home/.cursor/hooks.json" < should violate. +cat > "$fake_home/.gemini/config/hooks.json" <&1; } + +out=$(run); rc=$? + +# --- assertions --------------------------------------------------------------- +printf '%s\n' "$out" | grep -q "OK .*owned.sh \[OWNED" \ + && ok "owned symlink into repo is reported OWNED" \ + || bad "owned target not reported OWNED"$'\n'"$out" + +printf '%s\n' "$out" | grep -q "VIOLATED .*gone.sh \[MISSING" \ + && ok "missing target is a violation" \ + || bad "missing target not flagged"$'\n'"$out" + +printf '%s\n' "$out" | grep -q "VIOLATED .*manual.sh \[UNOWNED" \ + && ok "hand-placed regular file is a violation" \ + || bad "unowned target not flagged"$'\n'"$out" + +printf '%s\n' "$out" | grep -q "gemini .*gone.sh \[MISSING" \ + && ok "nested gemini PreToolUse shape is parsed" \ + || bad "nested gemini command not extracted"$'\n'"$out" + +[ "$rc" -eq 1 ] \ + && ok "exit 1 when violations exist (fails closed)" \ + || bad "expected exit 1 with violations, got $rc" + +# --- fail closed when the repo cannot be located ------------------------------ +# Copy the script somewhere with no home.nix parent and a HOME lacking ~/dotfiles. +lonely="$tmp/lonely"; mkdir -p "$lonely/bin" +cp "$SCRIPT" "$lonely/bin/audit-hook-ownership.sh" +empty_home="$tmp/empty-home"; mkdir -p "$empty_home" +HOME="$empty_home" bash "$lonely/bin/audit-hook-ownership.sh" >/dev/null 2>&1 +[ "$?" -eq 2 ] \ + && ok "exit 2 (fail closed) when the repo cannot be located" \ + || bad "expected exit 2 when repo not found" + +if [ "$fails" -eq 0 ]; then + echo "PASS: all audit-hook-ownership tests" +else + echo "FAIL: $fails assertion(s)" >&2 + exit 1 +fi