Skip to content

FormatGate.hook.ts: upgrades leave the pre-2026-07-12 blocking copy active, reintroducing forced re-emits #1564

Description

@vichong

Summary

install/hooks/FormatGate.hook.ts shipped its observation-only fix on 2026-07-12, but an already-installed host keeps the previously-installed blocking copy at ~/.claude/hooks/FormatGate.hook.ts. Nothing in the upgrade path replaces the active hook, so the exact failure the fix was written to prevent (forced full re-emit, duplicated response) persists indefinitely on upgraded installs.

Evidence (LifeOS 7.1.1, Claude Code 2.1.217, WSL2 / Ubuntu)

File mtime size behaviour
~/.claude/hooks/FormatGate.hook.ts (active, registered via StopGates) 2026-07-12 23:10 6993 B return { decision: "block", reason: blockReason(violation) }
~/.claude/skills/LifeOS/install/hooks/FormatGate.hook.ts 2026-07-14 18:34 7295 B appendObs({... decision: "flag" ...}); return null
danielmiessler/LifeOS@main:LifeOS/install/hooks/FormatGate.hook.ts 7295 B byte-identical to the install copy

StopGates.hook.ts was byte-identical to upstream on the same host, so this is a single stale file rather than a wholesale un-synced hooks directory.

From LIFEOS/MEMORY/OBSERVABILITY/format-gate.jsonl on that host: 36 decision:"block" records (20 too-many-emdashes, 16 no-banner), each one a forced re-emit of a full response for a cosmetic format issue. That is the symptom described in the upstream header comment:

OBSERVATION-ONLY (2026-07-12): this gate does NOT block. ... That re-emit prints the response twice, which is the duplicated-output failure [the principal] escalated on hard (blocked ~10x no-banner / ~14x em-dash in a single session before this change).

Secondary issue: the block reason contradicts the banner regex

While the stale copy is active, its guidance is also self-defeating. BANNER = /════\s*LifeOS/ requires the literal word LifeOS, but blockReason() quotes back:

Re-emit the response in the one LifeOS format — ════ LifeOS banner as the first line ...

A model reading that reasonably emits a bare ════════... rule, which fails the regex again. Observed three consecutive identical blocks from this before the regex was read directly. Upstream deleted blockReason() along with the blocking path, so this resolves itself once the sync issue is fixed — worth noting only because it makes the stale state much more expensive than it looks.

Suggested fix

Have the upgrade path (or Doctor --reconcile) diff active ~/.claude/hooks/*.ts against skills/LifeOS/install/hooks/*.ts and report or replace drifted files. Related in kind to #1510, where shipped hooks and registered hooks disagreed.

Workaround

cp ~/.claude/skills/LifeOS/install/hooks/FormatGate.hook.ts ~/.claude/hooks/FormatGate.hook.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions