Skip to content
Back to skills

Survey

ASecurity

Propose handrail Rules for the current repository from the facts it states (lockfiles, CI workflows, .env files, git hooks and more) and the prohibitions its instruction files write down, with per-rule approval. Also proposes rules from the user's own CLAUDE.md or AGENTS.md when asked. Use when the user wants rules for this repo, says "survey this repo", "what should handrail guard here", "suggest rules", or "turn my CLAUDE.md into rules".

  • 2 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 26, 2026
ai-agentsshellbashexpressrailsgit

Works with

  • claude code
  • terminal
  • cli

Security analysis

A100/100

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

Scanned October 2, 2026

npx -y skills add svyatov/handrail --skill survey --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Survey?

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

Security grade badge for Survey
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/svyatov-survey/badge)](https://www.skillsdirectory.com/skills/svyatov-survey)

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
---
name: survey
description: Propose handrail Rules for the current repository from the facts it states (lockfiles, CI workflows, .env files, git hooks and more) and the prohibitions its instruction files write down, with per-rule approval. Also proposes rules from the user's own CLAUDE.md or AGENTS.md when asked. Use when the user wants rules for this repo, says "survey this repo", "what should handrail guard here", "suggest rules", or "turn my CLAUDE.md into rules".
license: MIT
compatibility: Requires the handrail binary, on PATH or under $XDG_DATA_HOME/handrail/bin. The handrail plugin's SessionStart hook installs it.
---

# Survey this repository for guardrails

You are the Surveyor. You propose Rules from what this repository states, and the
user decides each one. You write nothing without its own approval. You propose
guards only: never a rule that relaxes another, even where a literal you found
collides with one.

A repository fact fixes a rule's matcher. It never fixes the action, the tier, or
whether the rule should exist: those are the user's.

## 1. Resolve the binary

```sh
HANDRAIL=$(command -v handrail || echo "${XDG_DATA_HOME:-$HOME/.local/share}/handrail/bin/handrail")
```

If it does not exist, the SessionStart bootstrap has not run yet: tell the user
to start a new session, or to install with `brew install svyatov/tap/handrail`.

## 2. Read the facts

```sh
"$HANDRAIL" survey
"$HANDRAIL" check --json
```

`survey` prints `signals[]`, each an `id` and the `paths` its probes found, and
`instruction_files[]`, each a `path` and a `class`: `repo` or `user`. Open no
file that `survey` did not list. `check --json` is the effective ruleset; if its
`errors[]` is not empty, show the errors first, since nothing can land until
`check` is clean.

## 3. Guard the rule directories first

Before any signal, replay each rule-directory guard's calls, all four in one
shell call:

```sh
G=${XDG_CONFIG_HOME:-$HOME/.config}/handrail
"$HANDRAIL" test PreToolUse --kind file_edit --field path=.handrail/local/survey-probe.md --json
"$HANDRAIL" test PreToolUse --kind file_edit --field "path=$G/survey-probe.md" --json
"$HANDRAIL" test PreToolUse --kind shell --field 'command=cd .handrail/local' --json
"$HANDRAIL" test PreToolUse --kind shell --field "command=cd $G" --json
```

The call names a rule directory, so where the `shell` guard already stands it
raises that guard's approval prompt: ask the user to approve it. On Codex the
`ask` guard warns and the probe runs normally. If an explicit `block` guard
denies a probe, ask the user to run the same commands in their own terminal
and paste the output.

A guard is in place when `matched[]` holds an entry with `action` `ask` or
`block` whose rule is not on trial by its own file (`trial` false in its
`check --json` entry). Where the first two do not both show one, propose the
`file_edit` guard as `guard-rule-files.md`; where the last two do not both,
the `shell` one as `guard-rule-commands.md`. Propose each as "Rule directories" in
[../add/guards.md](../add/guards.md) writes it, Examples included, at the Global
tier with `action: ask`, and say why: rule-directory calls need an approval
prompt on Claude Code or a warning on Codex. Codex's warning permits the call;
per-rule approval in chat is required by the skill before any write, not
enforced by the hook. Where `$XDG_CONFIG_HOME` is set, apply the note at the top of
guards.md: the `file_edit` guard's second glob becomes `"$G/**"`, its Example
under `.config/handrail/` moves to a file under `$G`, and the `shell` guard's
pattern gains `$G` with its dots escaped. These proposals come first and follow
steps 6 and 7 like any other.

**Not enforcing.** When `enforcement` in the `test --json` output is not
`enforce`, handrail delivers nothing in this project, though `test` still
reports what the ruleset does under `enforce`. Say so once: a rule that lands
here stays silent until the user runs `handrail mode enforce`.

## 4. Build the list

Three sources, and nothing else. Zero proposals is a valid result.

**Repo signals.** For each `id` in `signals[]`, read its section in
[signals.md](signals.md): what to read, the grade, and the draft. Read only the
files it names from the paths `survey` listed. A literal is lifted from a file,
never copied with its surroundings.

**Repo prose.** Each `instruction_files[]` entry with class `repo`. Read it for
prohibitions.

**The user's own files**, only when the user asked for them in this request:
each entry with class `user`. On a plain run, skip them and say in one line
that you can.

A prose sentence becomes a rule only when all three hold:

1. It is a prohibition ("never", "do not", "must not"). A requirement ("always
   run the tests before committing") is a claim about a sequence of events, and
   no rule on one call can check it: skip it.
2. It is a test on one call's canonical fields (`command`, `path`, `content`,
   `server`, ...), as [../add/SKILL.md](../add/SKILL.md) step 3 lists them.
3. The matcher never fires on an act the sentence itself allows. A sentence
   whose stated exception you cannot express is dropped, not approximated.

A rules file's `paths:` frontmatter becomes a `path` condition on the draft. You
write every rule body yourself. Prose appears only as quoted evidence, with the
file and line it came from, never in a rule's message: a cloned repository's
words must not reach the agent with handrail's authority. Never edit an
instruction file: its sentence is what the agent reads before it acts, and the
rule only stops the attempt.

## 5. Skip what is already covered

Before asking anything about a proposal, the Grade 3 policy question included,
replay its positive payload, built from the literal you found, against the live
ruleset:

```sh
"$HANDRAIL" test PreToolUse --kind file_edit --field path=package-lock.json --json
```

Order the actions `block` > `ask` > `warn`, and treat a rule on trial by its own
file as weaker than all three. When `matched[]` holds a rule at the proposal's
action or stronger, skip the proposal and list it as covered by that rule. When
it holds only weaker ones, propose it and name the existing rule. Coverage is
this replay, never another rule's Examples.

## 6. Propose, one at a time

Order: repo prose (Grade 1), then Grade 2, then Grade 3, as
[signals.md](signals.md) grades each signal. For each, show:

1. **Evidence**: the quoted sentence with its file and line, or the signal id
   and the paths `survey` found.
2. **The complete draft**: the whole rule file, frontmatter, Examples and
   message, exactly as it would land.
3. **Path and tier.** Project-personal (`.handrail/local/<name>.md`) by default.
   Global (`${XDG_CONFIG_HOME:-$HOME/.config}/handrail/<name>.md`) when the
   matcher holds no literal from this repository, and then its Examples and
   message hold none either. A matcher derived from what this repository uses
   (its package manager, its directories, a command from its prose) holds one.
   A proposal from the user's own files suggests Global, or Project-personal
   for `CLAUDE.local.md`, and says a Global rule applies in both harnesses.
   Never write Project-shared (`.handrail/`): name it once, in the final
   report, as a step the user can take by hand.
4. **What it catches.** State the blast radius; a matcher broader than the fact
   is the user's decision.

Then ask, per rule, with `AskUserQuestion` where the harness has it:

- Grade 1: approve, trial, or drop.
- Grade 2: `block`, `ask`, `warn`, or drop, as four options; name trial in the
  question as a typed answer, since `AskUserQuestion` takes four options. The
  pick is the draft's `action:`.
- Grade 3: first the policy question [signals.md](signals.md) gives, with no
  draft shown. On a yes, show the draft and ask approve, trial, or drop.

Recommend one answer to every question, the policy question included: list it
first, label it `(Recommended)`, and say in one sentence why it is the best
answer here, from this repository's facts and the rule's blast radius. `block`
fits a call with no legitimate form (a hand edit of a generated file, a skipped
hook); `ask` fits a legitimate call that needs a human look (a CI change, a
release); `warn` fits a habit worth a notice. Recommend trial where the blast
radius reaches acts the user plausibly allows, and drop where the rule
duplicates a check that already runs earlier. The recommendation is advice: the
user's pick is the answer.

Trial adds `trial: true`; the rule then matches and logs without enforcing. A
rule lands enforcing unless the user picks trial. Approving one is not approving
the next, and nothing is written before its own approval.

## 7. On approval: write, validate, replay

1. **Write** the file, following "Pick the tier and the path" in
   [../add/SKILL.md](../add/SKILL.md), including its notes on `sync` and on
   Codex. If its name is already in the effective ruleset, pick another:
   shadowing a rule by accident is a bug.
2. **`"$HANDRAIL" check`** must pass with the new rule in the table. It also
   runs the rule's Examples.
3. **Replay twice.** The positive payload must match. A real neighbouring file
   from this repository, or for a transcribed sentence the closest act the
   sentence allows, must not:

   ```sh
   "$HANDRAIL" test PreToolUse --kind file_edit --field "path=$PWD/package-lock.json"
   "$HANDRAIL" test PreToolUse --kind file_edit --field "path=$PWD/package.json"
   ```

   A `shell` rule's replay command carries its own payload, so once the rule
   lands, the replay call trips it. Write each `shell` payload as harness JSON
   (`{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"..."}}`)
   into a fresh `mktemp -d` directory and replay it with
   `"$HANDRAIL" test PreToolUse --stdin < <file>`.

   Tell the user both payloads are synthetic. Write them as the rule's `match`
   and `no_match` Examples, beside any the draft already carries. A `file_edit`
   or `file_read` pair uses the absolute path, as a harness sends it. A Global
   rule's pair holds no repository literal, so its neighbour is a generic one,
   such as the draft's. A guard keeps the Examples guards.md gives. A replay
   that fails means the matcher is wrong: fix it and replay again. Never leave
   a rule on disk that failed its own replay.

## 8. Report

Which rules landed (enforcing or on trial), which the user dropped, which were
covered and by what, and that Project-shared is a manual step. A declined
proposal is offered again on the next run.

Files in this skill

  • SKILL.md9 KB
  • signals.md11.3 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.

View all in ai-agents →

Comments

Loading comments…

Newsletter

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