| name | self-review |
| description | Use when finishing spec.md before writing test-definitions.md, or when the review gate asks for a spec review — self-reviews the just-authored spec inline and earns its Tier 1 review stamp. Your own inline pass; do not spawn a sub-agent. |
Self-Review
Review the artifact you just authored, then earn its review stamp so the next
step is unblocked. This is Tier 1 — a cheap, inline floor. You review your
own work; no sub-agent is spawned. The independent check is Tier 2 (the fork
review at each phase exit), not this.
Stakes set depth. Tier 2 may never run — review as if your stamp is the last
word before code gets built on this spec, because often it is. Cheap floor means
fast, not shallow.
Earn the stamp
The line below runs the stamp-earning step at render time. It binds a
review:<scope> stamp to the active ticket's spec.md at its current
content and appends it to skill-invocations.log under the project namespace root, where the
per-asset gate reads it back. Invoking this skill is what writes the stamp —
hand-editing the log is the gameable floor this tier deliberately accepts.
!PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}" && CLAUDE_PROJECT_DIR="$PROJECT_DIR" bun "$PROJECT_DIR/.safeword/hooks/write-review-stamp.ts" spec
If no [skill-invocation-log] ... ✓ line appears above, run this fallback before stopping:
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2> /dev/null || pwd)}"
CLAUDE_PROJECT_DIR="$PROJECT_DIR" bun "$PROJECT_DIR/.safeword/hooks/write-review-stamp.ts" spec
The review stamp is content-bound and uses the normalized runtime identity.
If the automatic line and fallback both print [skill-invocation-log] FAILED, or still do not print ✓: STOP.
The stamp was not written and the gate will keep blocking. Most likely the bash
injection was denied, no in_progress ticket was found, or Bun could not run the
installed helper — report it to the user and resolve before retrying.
Review the spec (do this now, with the stamp written)
The stamp records that a review was invoked; the actual scrutiny is yours. Read
the active ticket's spec.md and check, against personas.md and the ticket's
scope / out_of_scope frontmatter:
- Every JTBD resolves to a real persona and reads as a genuine job (
When I…, I want…, so I can…), not a restated feature.
- Each JTBD carries ≥1 numbered Rule (or legacy Acceptance Criterion)
stating an observable, product-level invariant — not an implementation detail.
- The criteria cover the ticket's scope and stop at its
out_of_scope line —
no silent scope creep, no orphan capability.
- Nothing leaks implementation (file names, function names, libraries) into
spec-level prose.
If the review surfaces a fix, edit spec.md and re-invoke $safeword:self-review — the
content-bound stamp goes stale on any edit, so the gate correctly re-blocks
until the corrected spec is re-reviewed. That is the point: a review that
changes the artifact must be re-earned.
Skip valve
If the artifact is genuinely trivial to review (boilerplate, a docs-only
change), log a skip with a reason instead of a review — it clears the same gate
and records why:
bun .safeword/hooks/write-review-stamp.ts spec --skip "<why this spec needs no review>"
Skip is the explicit --skip flag, quoted as one argument — free text after the
artifact is rejected, and an empty reason does not clear the gate.