4 Commits
Author SHA1 Message Date
mathiasandClaude Opus 5 abd81f939a fix(doctor): report a real harness directory as unmanaged, not as "outside"
release / tag (push) Successful in 1s
Found on flamingo during the first Mac install. ~/.config/crush/skills held
real directories from a pre-plugin install, and doctor said:

  FAIL outside: ~/.config/crush/skills resolves to ~/.config/crush/skills,
  outside the active checkout ~/.local/share/skills/skills

Naming a directory as its own resolution target is true and useless. Worse, it
pointed at the wrong fix: "outside" means a link into a foreign checkout, which
you repair by re-pointing the link. A real directory is repaired by moving it
aside and re-running install.sh — install.sh already refuses to delete it,
correctly, since it did not create it and cannot know what is in there.

Now reported as its own class, with the skill count so it is obvious whether
the directory holds a stale copy of the library or something unique that
belongs in the repo:

  FAIL unmanaged: <dir> is a real directory, not a link into the checkout —
  it holds N unmanaged skill(s); move it aside and re-run install.sh

The distinction matters because content in that directory reaches exactly one
harness and cannot receive new skills — the failure mode is silent divergence,
which is the same family as everything else this tool exists to catch.

Bump-Type: patch

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-26 07:53:14 +02:00
mathiasandClaude Opus 5 e2a312d05e fix(ci): anchor the release-skip marker to a trailer, and test it
release / tag (push) Successful in 1s
The tagger searched the entire commit message for a bracketed marker as a
substring. The commit that introduced the manifest-bump step described that
marker in its own body — so the tagger read its own documentation as an
instruction and skipped the release. Run 15 reported success with no tag, no
manifest bump and no error. Same failure family as everything else in this
sweep: the wrong thing happened silently.

The rule is now a git trailer matched anchored to its own line, consistent
with Bump-Type, and it lives in bin/should-skip-release rather than untested
inside YAML. tests/release_skip_test.sh pins six cases, including the exact
regression: a message that spells the marker out in prose must still tag.

A marker you cannot mention without triggering is a footgun, not a mechanism.

Bump-Type: patch

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-23 14:39:31 +02:00
mathiasandClaude Opus 5 88c54399fe feat(doctor): fail loud on skills drift, wired to SessionStart
Every failure this fleet actually hit was silent. 243 dangling symlinks were
invisible to git because **/.claude/skills/ is in the global gitignore. A
checkout 20 skills behind still resolved, so nothing complained. A loose
secret-hygiene.md loaded in no harness and errored in none. Two machines had
the library missing entirely. Exit status is the signal that was absent.

bin/doctor reports, with a non-zero exit:
  - dangling links, anywhere under the harness dirs
  - links that RESOLVE but land outside the active checkout — the dangerous
    case, because it works while serving stale content
  - skill-count mismatch between a harness and the checkout
  - stray entries where <name>/SKILL.md was expected
  - leftover per-skill Claude symlinks (plugin skills do not override
    same-named user skills, so leftovers mean everything loads twice)
  - divergence between the plugin copy and the copy the other harnesses link to

Wired as a SessionStart hook with --quiet, so a healthy host prints nothing and
a drifting one says so at the top of the next session. That beats a timer:
no launchd/systemd wiring, and it reaches every host that installs the plugin.
27ms on koala, no network, no git fetch.

Two design points found by the tests, both worth keeping:
  - The canonical checkout is whatever ~/.agents/skills resolves to, NOT the
    copy doctor runs from. As a plugin hook $0 lives in the plugin cache, which
    is legitimately a second copy; judging harness links against it flagged
    every one of them as "outside".
  - "cd X/.." resolves ".." lexically against the link path and lands back in
    ~/.agents. Resolve the link first, then take the parent.

tests/doctor_test.sh drives seven fixtures through $HOME and $DOCTOR_CHECKOUT,
so no test touches the real host. Each asserts both the non-zero exit and that
the report names the fault — an exit-code-only assertion would pass while the
message said nothing useful.

Bump-Type: minor

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-23 14:34:32 +02:00
mathiasandClaude Opus 5 747c6c7f4c feat(plugin): ship the secret-guard and pre-task-ritual hooks + MCP servers
The plugin now carries the two hooks Mathias owns and the brain/gitea/infra
MCP server definitions, not just skills.

Why this matters more than tidiness: both hooks existed ONLY in koala's
~/.claude/, which is host-local and unsynced. flamingo and iguana have been
running with no secret-guard at all — that is the control which blocks the
decrypt-and-print shape behind the 2026-08-05 ntfy-token leak, the op-item-get
shape behind 2026-07-30, and the base64-bypass shape behind 2026-06-11. Its
absence on two of three machines is a security gap, not a config gap. Shipping
it in the plugin means installing the plugin installs the guard.

.mcp.json references tokens as ${BRAIN_MCP_TOKEN} / ${GITEA_MCP_TOKEN} only —
env indirection, never literal values, so the manifest is safe to commit.

tests/secret_guard_test.sh pins 14 cases. It asserts on
hookSpecificOutput.permissionDecision, NOT exit status: the guard always exits
0 by contract, so an exit-status assertion would pass vacuously while the guard
silently allowed everything. Each deny case names the incident that produced it.

gsd-* hooks are deliberately NOT migrated — GSD owns and updates those in place.

Bump-Type: minor

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m
2026-08-23 14:31:04 +02:00