Skip to content
Back to skills

Done

ASecurity

`_has_skill()` in [`skill-reachability-reminder.py`](../../.agents/hooks/skill-reachability-reminder.py) returns true for **any** directory containing a `SKILL.md`. The contract asks a narrower question. The Proposed Surface row in [`cloud-executable.md`](../../docs/spec/cloud-executable.md) reads "at least one **kit skill** directory present at the repository's project-scope discovery directory, or at any user-scope discovery directory `install.py` targets", and `S-008`'s **Given** is "a clone

  • 2 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 3, 2026
developmentpythonrustgo

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned September 3, 2026

npx -y skills add hams-ollo/zen-agent-skills --skill done --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Done?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Done
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/hams-ollo-done/badge)](https://www.skillsdirectory.com/skills/hams-ollo-done)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
id: bug-0021
title: The reachability bootstrap counts a foreign skill library as reachable, so it is silent in the cloud session it exists for
type: bug
status: done
priority: P1
parent: "ROADMAP Epic E #2: make this repository cloud-executable"
depends_on: []
spec: "docs/spec/cloud-executable.md"
scenarios: ["S-008", "S-010"]
touched_files:
  - .agents/hooks/skill-reachability-reminder.py
  - tests/test_hooks_reachability.py
  - docs/spec/cloud-executable.md
created: 2026-08-08
---

## Problem

`_has_skill()` in [`skill-reachability-reminder.py`](../../.agents/hooks/skill-reachability-reminder.py)
returns true for **any** directory containing a `SKILL.md`. The contract asks a narrower question.
The Proposed Surface row in [`cloud-executable.md`](../../docs/spec/cloud-executable.md) reads "at least
one **kit skill** directory present at the repository's project-scope discovery directory, or at any
user-scope discovery directory `install.py` targets", and `S-008`'s **Given** is "a clone where no
**kit skill** is present at project scope or at any user-scope discovery directory".

So a machine carrying somebody else's skill library satisfies the implementation and not the
contract, and the hook stays silent in exactly the state `S-008` says it must speak.

Measured 2026-08-08 in a live cloud session on branch
`claude/developer-branch-review-j26u9s`, with none of this kit installed:

```text
$ echo '{"hook_event_name":"SessionStart","source":"startup","cwd":"/home/user/zen-agent-skills"}' \
    | python3 .agents/hooks/skill-reachability-reminder.py
(no output)

$ ls ~/.claude/skills | head
algorithmic-art  brain-dump  brand-guidelines  canvas-design
docx  pdf  pptx  xlsx  skill-creator  theme-factory      ... 24 total

$ reachable(repo_root, Path.home())
True
```

None of those 24 directories belongs to this kit. `doc-sync`, `fix-batch`, `house-review`,
`new-task`, `verifier-agent`, and `spec-author` are all absent, and the session proceeded with the
report suppressed.

**This falsifies a recorded prediction.**
[`cloud-executable.verification.md`](../../docs/spec/cloud-executable.verification.md) writes seven
falsifiable predictions before the Phase 4 proof run. Prediction 1 is *"The session reports `NO
SKILLS REACHABLE` at startup (`S-008`), falsified if nothing appears, or it appears when skills were
reachable."* Nothing appeared. The proof run is still owed, and running it against the current code
would produce a result nobody can read: "nothing appeared" would be indistinguishable between a
broken hook and a correct hook meeting a populated home.

**The failure shape is the one the hook was written to remove.** A cloud container that ships its own
`~/.claude/skills` is not an edge case, it is the default on the platform this epic targets, so the
committed `.claude/settings.json` exception buys nothing where it was granted. The module docstring
does say "it cannot tell whose skills it found", but that sentence only ships inside `REPORT`, the
string that never prints when the hook is silent.

## Scope

**In scope:** make `reachable()` answer the contract's question. Decide and implement how a kit skill
is recognised at a discovery directory, extend
[`test_hooks_reachability.py`](../../tests/test_hooks_reachability.py) with a case that fails against the
current hook, and reconcile `cloud-executable.md` with whatever is built.

**Out of scope:**

- Any environment detection. `S-016` forbids it, and a populated foreign home is not a cloud signal.
- Currency. `S-011` is unchanged: the hook answers reachability and never claims the skills are
  current. Do not call `install.py --check` from the hook, for the reason its own docstring gives.
- Widening `.claude/settings.json` beyond the one reminder registered there. That exception is
  written up in the conventions section of [`AGENTS.md`](../../AGENTS.md) and is not this task's to
  bend.
- The Phase 4 proof run itself. This task makes its result readable; it does not run it.

## Implementation notes

Three candidate recognitions, and the choice is the task's real work. State which was taken and why
in the module docstring, since the next reader will ask.

1. **Name the kit's own skills.** A directory whose name matches one this repository ships. Exact and
   self-contained, and it goes stale the moment the catalog changes unless it is derived at runtime,
   which a hook cannot do without importing from this repository, forbidden by
   [the hooks module contract](../../.agents/hooks/README.md).
2. **Read the install manifest.** Authoritative, and it makes a portable hook depend on one
   repository's script layout, which the docstring already rejects for the currency question.
3. **Narrow the claim instead of the check.** Keep counting any skill, and report when none of *this
   repository's* skills is present, using a marker the kit places at install time.

Whatever is chosen, the silence contract in `S-010` still holds: a session with the kit's skills
reachable receives nothing. Do not trade the noise property for the correctness one.

**The spec may need an amendment rather than the code moving to it.** If the honest answer is that a
hook cannot tell whose skills it found, then the contract is wrong and `S-008` needs rewriting, not
the hook. Per the convention recorded in [`docs/spec/README.md`](../../docs/spec/README.md), an
amendment to an approved spec keeps `status: approved`, carries a dated note, and is marked pending
the author's re-approval. Do not silently rewrite the scenario to match the code.

## Decisions

- **Candidate 1 was taken: a kit skill is recognised by directory name.** `KIT_SKILL_NAMES` in the
  hook lists the twenty skills this kit ships, and one match at any discovery directory is
  reachability.
- **Rejected, candidate 2, reading the install manifest.** It is authoritative and unusable here:
  `scripts/.install-manifest.json` lives in the checkout that ran the installer, so a cloud clone has
  none, which is the exact case being answered, and consulting it would tie a portable hook to one
  repository's script layout.
- **Rejected, candidate 3, a marker placed at install time.** Retroactively wrong: every install that
  already exists carries no marker, so the hook would cry wolf at all of them until re-run, which is
  the failure the Risks section names. It also needs `install.py`, outside this task's
  `touched_files`.
- **The staleness objection to candidate 1 is answered at test time, not runtime.** A hook may not
  import from this repository, so it cannot derive the catalog; the repository can check the constant
  against what it ships, and `test_the_recognised_names_are_exactly_the_skills_this_kit_ships`
  asserts set equality in both directions. Adding, renaming, or removing a skill now means editing
  that constant in the same commit.
- **Seam left open: the hook matches a name, not a provenance.** A foreign library shipping a
  directory called `doc-sync` would silence it. Deliberate, and it is the cheap direction to be wrong
  in for a reminder: the cost is one missing paragraph, against a false alarm that costs the adopter
  their trust in the hook.
- **Rejected wording: a `NO KIT SKILLS REACHABLE` banner.** The report now reads `NO SKILLS REACHABLE
  FROM THIS KIT`, keeping the original phrase at the head, because
  [`cloud-executable.runbook.md`](../../docs/spec/cloud-executable.runbook.md) tells a person to look for
  a message beginning with those exact words and the proof run it governs has not happened yet.
  Retargeting that string silently would have broken the runbook from a file it does not name.
- **Premise qualified: `cloud-executable.md` needed no amendment, so it was left untouched.** The
  task lists it in `touched_files` and offers an amendment branch, but the contract already asks the
  narrower question in both places that matter (`S-008`'s Given and the Proposed Surface `Reachable`
  row read "kit skill"). The code moved to the contract, so `status: approved` stands with no dated
  note and the `docs/spec/README.md` re-approval queue is not extended.

## Risks and rollback

Touches the hooks module and an approved contract. The risk is a false positive in the other
direction: a recognition too narrow reports "no skills reachable" to an adopter who installed a
subset, and a reminder that cries wolf is uninstalled within a week, which costs more than the miss
it fixes. Cover the adopter-with-a-partial-install case in a test.

Reversible by reverting one commit; the hook writes nothing and holds no state.

## Acceptance criteria (mechanically verifiable)

    python -m unittest discover -s tests -p "test_*.py" && python scripts/run-checks.py

- [ ] A test that places a foreign skill library at user scope, with no kit skill anywhere, and
      asserts the hook reports. It must fail against the current hook.
- [ ] A test that places kit skills at user scope beside a foreign library and asserts silence
      (`S-010`).
- [ ] A test that places kit skills at project scope only and asserts silence (`S-009`).
- [ ] The empty-discovery-directory case still counts as unreachable, so `--uninstall` leaving the
      parent behind does not read as success.
- [ ] No environment detection is introduced; the existing `S-016` test still passes unchanged.
- [ ] The hook still writes nothing, exits 0 on malformed input, and imports nothing from this
      repository.
- [ ] Either the code matches the contract's "kit skill" wording, or `cloud-executable.md` is amended
      to match what a hook can honestly answer, dated and marked pending re-approval, with the
      `docs/spec/README.md` re-approval queue extended.
- [ ] Existing tests still pass, unchanged in intent.

## Definition of done

- [ ] Acceptance command(s) pass locally.
- [ ] Conventions in AGENTS.md's conventions section followed.
- [ ] `doc-sync` run over the reader-facing documents and its findings applied or dismissed with a reason.
- [ ] File moved to `.tasks/done/`, `status: done`; one dated line added to `CHANGELOG.md` referencing this task id.

Files in this skill

  • bug-0001-fix-script-review-findings.md3 KB
  • bug-0002-agents-section-references-by-name.md5.7 KB
  • bug-0003-uninstall-ignores-home.md5.9 KB
  • bug-0004-reinstall-not-idempotent-windows-py39.md6.8 KB
  • bug-0005-trim-over-limit-descriptions.md7 KB
  • bug-0006-adapters-emit-block-scalar-indicator.md6.4 KB
  • bug-0007-descriptions-are-not-valid-yaml.md9.5 KB
  • bug-0008-description-angle-brackets-and-frontmatter-schema.md8.5 KB
  • bug-0009-uninstall-compares-an-unresolved-home.md8.6 KB
  • bug-0010-install-records-an-unresolved-home.md11.7 KB
  • bug-0011-tasks-links-break-on-move-to-done.md13.6 KB
  • bug-0012-links-that-resolve-to-the-wrong-file.md8 KB
  • bug-0013-validator-rejects-file-scheme-links.md8.1 KB
  • bug-0014-doc-sync-applied-edit-has-no-link-check.md5.7 KB
  • bug-0015-link-check-fires-inside-code-spans.md9.1 KB
  • bug-0016-provenance-block-blank-line-silent-skip.md7.2 KB
  • bug-0017-mislabelled-link-check-fires-inside-fenced-blocks.md8.2 KB
  • bug-0018-reinstall-destroys-an-adopter-edited-lens.md11.9 KB
  • bug-0019-provenance-check-drops-unreadable-files-silently.md6.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.