Troubleshooting - nguyenhx2/agent-harness-bootstrap GitHub Wiki
English · 日本語
Every entry here is a failure that actually happened, not a hypothetical. Find the symptom, run the first move, then read the section.
| Symptom | First move | Section |
|---|---|---|
| A hook blocks nothing | Check the flavour, pipe a payload by hand | below |
| Every call is refused | Check the interpreter the hook needs | below |
| A dispatch is refused | Read the ledger line the message cites | below |
| A board edit is refused | task.py move |
below |
| A link points at nothing | harness-check.py |
below |
| Cursor or Codex allows everything | port.py --self-test |
below |
| The viewer looks stale | harness-graph.py |
below |
| Scaffold reports CONFLICT or exits 1 | Reconcile, or fill the variable | below |
Check the flavour first. A Windows harness ships .ps1 hooks. If settings.json registers
.sh commands, every lookup misses and everything is allowed. The flavour and the registration
both derive from one OS flag, so a mismatch means the harness was hand-edited.
harness-view assess . # reports a hook that ships but is not registeredThen test the hook directly. Hooks read JSON on stdin and answer with an exit code:
echo '{"cwd":"'"$PWD"'","tool_name":"Bash","tool_input":{"command":"cat .env"}}' \
| bash .claude/hooks/protect-secrets.sh; echo "exit=$?"2 blocked, 0 allowed. In PowerShell check $LASTEXITCODE, never $?: that is a boolean saying
the script ran, not what it decided. On Windows, bash on PATH may be the WSL launcher rather than
Git Bash; the two resolve paths differently, so test with the one your settings call.
On a harness older than v1.20.1, upgrade (/harness-update). Four live holes were closed there:
git -c k=v commit and other git global options walked past the commit and destructive-command
guards, the Windows PowerShell 5.1 payload carried a byte-order mark that switched the verification
ledger off, a guard that timed out let its command run, and protect-secrets did not see the
PowerShell tool.
The blocking hooks carry onFailure: "block" and a 30 s timeout: a guard that cannot run refuses
rather than allows. If every Bash call is blocked, the hook itself is failing. Run it by hand as
above. The usual cause is that the interpreter the registration names (bash, pwsh or
powershell) is not on PATH, or the hook is too slow for its timeout.
The hook reads the verification ledger (.claude/state/verify-ledger.jsonl) and refuses:
- a review or suite run on content that already passed
- a seat dispatched twice on the same content with no result recorded
- a third review round, or an identical brief
- a dispatch past the task's budget (
dispatch_budget, default 24) - a write-capable seat that names neither a registered
TASK-NNNnorTIER: directorTIER: standard
The message cites the ledger line. Use the recorded result, or change something and run the gate
once. Only you can override it, as .claude/hooks/README.md says.
guard-board blocks an edit to a task row of docs/tasks/master-plan.md. A task's state lives in
the file's status:, its folder and the board row, and python .claude/scripts/task.py move TASK-NNN <state> changes all three and reads the row back. The Phases table and the prose around the board
can still be edited.
python .claude/scripts/harness-check.pyIt checks that every registered hook script exists, every seat a command or table names exists, the
tier table agrees with the seats, every cited rule exists, and every skill a seat lists is
installed. Each finding names file:line and the fix.
A real bug: the deny rule matched .env.example, the one file every block message tells the agent
to read. It is fixed, and the eval covers reading the example, seeding a local one and running with
one loaded. In your own harness, the deny list should name value-bearing files, not every
filename containing env. Check .claude/settings.json and the protect-secrets hook.
python harness-bootstrap/scripts/port.py --self-testIt checks both hook flavours, that the adapter offers every interpreter name its platform may use,
and that it denies a call it cannot evaluate. The adapter once baked in powershell, which
exists only on Windows; on macOS and Linux the binary is pwsh, so every call raised a traceback and
the guard never ran.
On Codex, also check that the project is trusted and its hooks were reviewed through /hooks;
until then the ported hooks are on disk and inert. See
docs/tools/codex.md.
The graph-stale hook regenerates .claude/state/harness-graph.json after an edit under .claude/,
and /harness-update and /harness-toggle regenerate it themselves, because script-driven writes
do not fire editor hooks. Two maps are on demand: /code-graph refreshes the module-boundary map,
and /docs-graph prints orphan requirement IDs. To force a rebuild:
python .claude/scripts/harness-graph.py # or: harness-view scan .python scripts/check_numbers.pyIt compares every figure in the documents, the deck, the clips and the figures with what the scripts print, and exits non-zero on a contradiction. A clip once claimed "26 / 26" long after the eval reached 107, hidden from the checker by the spaces around the slash. If a page and a script disagree, trust the script and open an issue.
-
CONFLICTis not an error. The file exists and differs from what would be installed. Resolve each one by hand: keep yours, adapt it, take the new one, or register the difference as a task. The scaffolder never overwrites or deletes what you wrote. -
Exit 1 on a variable means an unresolved
{{VAR}}. A placeholder in a rule file would match nothing and fail silently, so the run writes nothing instead. Fill it invars.jsonand re-run. -
Exit 1 on the roster or links means an invalid flag set, a second seat holding
Agent, a control installed but not wired, or a linkharness-check.pycannot resolve. The message names the file and line.
Open an issue with the output of
harness-view assess . and the failing command. The assess output names the node, which is usually
enough to locate the problem.