Skip to content
Back to skills

Ship A Feature

ASecurity

Ship a NEW capability to vigiles so it actually reaches a user — a new export, public function, subpath, CLI flag, or hook-vocabulary word. Use when about to add, or having just added, a feature / export / public API to this repo. Runs the executable checks a green `npm run check` does NOT make — reachable from a public door, API-surface diff READ, a public doc home, the experimental_ decision, a gate on a frozen tree. NOT for bug fixes, refactors, doc-only or test-only edits.

  • 15 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 20, 2026
ai-agentsshellnodetestinggitapi

Works with

  • cli
  • api

Security analysis

A100/100

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

Scanned September 20, 2026

npx -y skills add zernie/vigiles --skill ship-a-feature --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ship A Feature?

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

Security grade badge for Ship A Feature
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/zernie-ship-a-feature/badge)](https://www.skillsdirectory.com/skills/zernie-ship-a-feature)

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: ship-a-feature
description: Ship a NEW capability to vigiles so it actually reaches a user — a new export, public function, subpath, CLI flag, or hook-vocabulary word. Use when about to add, or having just added, a feature / export / public API to this repo. Runs the executable checks a green `npm run check` does NOT make — reachable from a public door, API-surface diff READ, a public doc home, the experimental_ decision, a gate on a frozen tree. NOT for bug fixes, refactors, doc-only or test-only edits.
argument-hint: <the exported symbol, e.g. "alternateSpellings">
---

# Ship a feature (so it ships, not half-ships)

The nine-row definition-of-done already exists — `cohesive-feature-delivery` in
`CLAUDE.md`. On 2026-09-02 `equivalentDisasters` went out tested, gated and
committed, and failed **four** of those rows unnoticed: unreachable from every
public door, a neighbour's JSDoc silently stolen, no doc, no stability mark.
Prose in a 2000-line file does not fire. This does. **Every check below names
the failure it was written for; nothing here is theory.**

## 1. Before writing — three decisions only you can make

| Decision                                                   | Criterion                                                                                                                                                                                                                |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Which door?** (`package.json` `exports` subpath)         | Free + deterministic → `.` (`src/test.ts`). Bills a model → `./eval` (`paid_` prefix). Hook vocabulary → `./hook`. A symbol not re-exported from a door's barrel **does not exist** to `require("vigiles")` (failure 1). |
| **Stable or `experimental_`?**                             | STABILITY.md: exported + not stable ⇒ `@experimental` tag AND `experimental_` name. One day old with one consumer is not stable. ESLint `local/experimental-name` holds tag→name; it cannot write the tag (failure 4).   |
| **Where does the WHY live publicly?** (`document-the-why`) | A `docs/*.md` section beside the claim it changes — for `equivalentDisasters`, next to "blocks 7/7" in `docs/compiled-hooks.md`. Not the README (brevity), not this file (contributor tier).                             |

Write the answers into the PR body. The script below refuses to pass the
stability row until you state it.

## 2. After writing — one command, five checks

```sh
npm run build
node .claude/skills/ship-a-feature/scripts/ship-check.mjs <symbol> [--stable "<why it is stable>"]
```

| Check          | What it executes                                                                                   | Measured failure it exists for                                                                       |
| -------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **REACHABLE**  | `import()`s every `exports` subpath from the built `dist/`; the symbol must be a key on ≥ 1        | `equivalentDisasters` in `src/guardrail-check.ts`, never in `src/test.ts` → `undefined` (1)          |
| **SURFACE**    | regenerates `api-surface/` (`api:report`), then READS `git diff`: any `+… (undocumented)` fails    | inserted between `verifyGuardrail`'s JSDoc and its declaration; the diff said so, nobody read it (2) |
| **DOCUMENTED** | `\b<symbol>\b` must occur in `docs/**/*.md` or `README.md`                                         | no public home until asked (3)                                                                       |
| **FINDABLE**   | two pages must name it — or one names it and another LINKS to that section's anchor                | documented once, inside a guide about a DIFFERENT feature, with no inbound link (5)                  |
| **MARKED**     | declaration's JSDoc has `@experimental`, or the name is prefixed, or you passed `--stable "<why>"` | exported one day old, no tag, no prefix; the lint rule had nothing to hold (4)                       |

**DOCUMENTED and FINDABLE are different questions.** The first asks whether the
capability is written down anywhere. The second asks whether a reader who does
not already know which file to open can get to it. `experimental_equivalentDisasters`
(since renamed `experimental_alternateSpellings`) passed the first and failed the second: it was explained once, under its own
heading, inside the guide for a different feature, and nothing linked there — so
the testing guide, where someone asking "does my guard actually block?" looks,
named it zero times. A pointer satisfies FINDABLE; you do not have to repeat the
explanation.

`✗` means fix and re-run; the script prints the fix beside each finding. A `✓ MARKED
stable` line carries your reason — paste it into the PR body.

## 3. The gate — on a FROZEN tree, in the foreground, with its real exit code

```sh
node .claude/skills/ship-a-feature/scripts/ship-check.mjs --gate     # = npm run check, frozen
```

It hashes the tree before and after and fails if anything changed while the
gate ran. Three measured ways a gate lies, all closed by running it this way:

- **Editing during the run** (5): `npm run check` linted a half-written file and
  failed on errors that belonged to no state. Do not touch the tree until it exits.
- **Waiting with `until ! pgrep -f 'check.mjs'`** (6): the waiting shell's own
  command line matches the pattern, so it never ends. Twenty minutes reported as
  "still running". Run gates in the foreground; if you must background one, `wait`
  the PID you spawned. Never `pgrep -f` a string that appears in your own command.
- **`npx vitest run … | tail -8`** (7): the pipeline's status is `tail`'s. Exit 0
  with three tests failing. No pipe after a gate — redirect to a file and read it.

## Already mechanized — call it, do not restate it

| Property                                   | Owner (do not duplicate)                                    |
| ------------------------------------------ | ----------------------------------------------------------- |
| tag `@experimental` ⇒ `experimental_` name | ESLint `local/experimental-name` (in `npm run lint`)        |
| `./eval` symbols carry `paid_`             | `npm run exports:check` (in `npm run check`)                |
| surface drift vs committed snapshot        | `npm run api:check` (in `npm run check`)                    |
| docs' `ts` blocks import real exports      | `npm run docs:check` — only once a doc EXISTS (3)           |
| every CLI verb mentioned in `docs/`        | `src/doc-command-coverage.ts` — verbs only, not API symbols |
| the whole command list                     | `npm run check` (`node scripts/check.mjs --list` prints it) |

## 4. Done means

The five ✓ lines, a `✓ GATE` line, and the PR body naming door · stability
decision · doc section. A feature nobody can `import` is not a feature, and one
nobody can navigate to is not documented.

Files in this skill

  • SKILL.md7.2 KB
  • scripts/ship-check.mjs15.2 KB
  • ship-a-feature.harness.mjs14.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.