Skip to content
Development
Skill

/night-market-debugging-playbook

Triage night-market failures by symptom (hooks, CI, tests). Use when a check fails unexpectedly. Do not use for routine gates; use night-market-operations.

From plugin
claude-night-market
337200 skills59 agents162 commands1 MCP
Install
$ npx -y skills add athola/claude-night-market --skill night-market-debugging-playbook --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/night-market-debugging-playbook

Context preview

The summary Claude sees to decide when to auto-load this skill.

Triage night-market failures by symptom (hooks, CI, tests). Use when a check fails unexpectedly. Do not use for routine gates; use night-market-operations.

SKILL.md

night-market-debugging-playbook.SKILL.md
name: night-market-debugging-playbook
description: Triage night-market failures by symptom (hooks, CI, tests). Use when a check fails unexpectedly. Do not use for routine gates; use night-market-operations.

Night Market Debugging Playbook

Match the symptom to a row, run the one discriminating command, apply the known fix. Every row below is a failure this repo has already paid for, with the commit hash that settled it. Do not re-derive a diagnosis that archaeology already produced.

Vocabulary

Terms used throughout, defined once:

  • **Hook**: a script Claude Code runs on events (PreToolUse,

PostToolUse, Stop, SessionStart). Registered in a plugin's `hooks/hooks.json` with a `command` and a `timeout`.

  • **Hook budget**: that registered `timeout` in seconds. The harness

kills the hook when it expires, before any output is honored.

  • **Host interpreter**: hooks run as `python3 ...` under the machine's

system Python (floor: 3.9), NOT the repo's uv-managed 3.12 venv. Third-party packages a plugin declares are not guaranteed present.

  • **skrills**: optional Rust binary for skill validation and analysis.

Every Makefile target that uses it has a Python fallback.

  • **Import chain**: everything a `import x` transitively pulls in,

including the plugin's `__init__.py`.

Symptom index

| # | Symptom | Likely cause | Story | |---|---------|--------------|-------| | 1 | PreToolUse hook error / ModuleNotFoundError on every `git commit` | Unguarded third-party import in a plugin `__init__.py` reachable from a hook | 45dd77ef, 9bfc0a7a | | 2 | `python39-compat` is the only failing CI check | A 3.10+/3.11+ construct (`datetime.UTC`, bare `X \| Y` union) entered a hook import chain | 18c9340d, PR #511 | | 3 | Hook exits 0 but never does anything | Hook reads `CLAUDE_TOOL_*` env vars instead of stdin JSON | CHANGELOG 1.9.14 | | 4 | `capabilities-sync` CI fails | plugin.json registrations drifted from the book reference | capabilities-sync.yml | | 5 | Root `pytest` raises ImportPathMismatchError | Plugin tests collected from repo root instead of per plugin | conftest.py, pyproject norecursedirs | | 6 | `slop-check` fails on a PR | Slop score over 3.0 in a `docs/` or `book/src/` markdown file | slop-check.yml | | 7 | Stop hook produces no verdict at all | Inner subprocess timeout >= registered hook budget | 268cff89 | | 8 | CI broken on a GitHub action or tool pin | Stale or nonexistent pinned version | f81d89a5, 25bf5a9d | | 9 | Scanner reports nothing on input you know is bad | Swallowed exception (except-and-continue) drops files silently | 666171c3, b6de71cf | | 10 | `skrills: not found` | Missing optional binary (a Python fallback exists) | Makefile validate-skills |

Triage runbooks

1. ModuleNotFoundError from a hook on every commit

First command (substitute the hook path from the error message):

echo '{}' | python3 plugins/gauntlet/hooks/precommit_gate.py; echo "exit=$?"

What the result means: a traceback names the module whose import chain pulls in a package the host interpreter lacks. Exit 0 with no output means the hook is import-safe and the problem is elsewhere (check the hook registration in `hooks/hooks.json`).

Fix: guard the import at module level or defer it into the function that needs it. The gauntlet incident: `precommit_gate.py` imported `gauntlet.knowledge_store`, whose `__init__.py` eagerly imported modules doing bare `import yaml` and `import anthropic`. Guarded in 45dd77ef (#518), deferred in 9bfc0a7a. Add a regression test that blocks the package via a `sys.meta_path` blocker and re-imports the hook (pattern in `plugins/gauntlet/tests/unit/test_challenges.py`).

2. python39-compat is the only failing check

The repo is Python 3.12, but hook scripts and their transitive imports must stay importable under Python 3.9 (`.github/workflows/ python39-compat.yml`). First command:

uv run ruff check --select UP007 --target-version py39 plugins/<plugin>/hooks/
rg -n 'datetime\.UTC|from datetime import UTC' plugins/<plugin>/

What the result means: UP007 hits are bare `X | Y` union annotations that raise TypeError at import time on 3.9. The `rg` hits are the `datetime.UTC` alias (3.11+), which UP007 does not catch. Either one in a hook import chain breaks every hook at once: on PR #511 a single `datetime.UTC` in `leyline.quota_tracker` produced three cascade failures (18c9340d).

Fix: use `from datetime import timezone` with `timezone.utc`, and `typing.Union`/`Optional` or a `from __future__ import annotations` line for unions. To mirror CI's Gate 2 locally (verified 2026-07-02):

uv venv --python 3.9 /tmp/hook39
VIRTUAL_ENV=/tmp/hook39 uv pip install pytest pyyaml
cd plugins/abstract
/tmp/hook39/bin/python -m pytest tests/hooks --override-ini="addopts="

The `addopts` override strips per-plugin coverage flags that need packages the bare venv lacks. See also the linter trap below: ruff will fight this fix.

3. Hook exits 0 but never does anything

First command:

rg -l 'CLAUDE_TOOL_' plugins/*/hooks/
rg -ln 'read_hook_payload' plugins/*/hooks/

What the result means: Claude Code never sets `CLAUDE_TOOL_*` environment variables. The payload arrives as JSON on stdin. A hook reading only env vars is a silent no-op: it exits 0, CI is green, and nothing downstream ever happens. This starved the `[Learning]` discussion digests for two months (last digest 2026-04-25) before anyone noticed (CHANGELOG 1.9.14).

Fix: read stdin first via the canonical reader `plugins/abstract/hooks/shared/hook_io.py` (`read_hook_payload`, stdin-first with env-var fallback for the test harness). Then verify the hook actually fires: pipe a realistic payload in and check for the side effect rather than the exit code alone.

4. capabilities-sync CI fails

First command:

bash scripts/capabilities-sync-check.sh

What the result means: the script diffs every plugin's `.claude-plugin/plugin.json` registrations agai

Read more
Ships withclaude-night-market

A plugin marketplace for Claude Code. Install only the plugins you need to run git workflows, code review, spec-driven development, and autonomous agents from inside your Claude Code session.

Get the whole plugin

Other skills on claude-night-market.