Sibling Claude session 2026-05-30 found that lastTurnEntries treats
harness-injected skill bodies as spurious turn boundaries, breaking both
enforce-memory-coverage (can't find user's coverage line) AND
enforce-normative-content-rules::detectLegitSkillActive (can't find the
Skill tool_use that lives in the assistant message BEFORE the body).
Refinement applied here: this session inspected 29 isMeta:true entries
across the live transcript (8f4ba767-...jsonl) via a debug helper and
found isMeta:true is ALSO used for "Continue from where you left off"
auto-resume, Stop hook feedback strings, and <local-command-caveat>
wrappers — those are real user-equivalent boundaries that must remain
visible. Sibling's blanket "skip isMeta" proposal would have broken them.
Discriminator: skip ONLY when isMeta === true AND typeof sourceToolUseID
=== 'string' (tool-spawned content). Skill bodies have the linking field;
the other isMeta sources do not. The sourceToolUseID field is harness-
controlled and not writable by controller from inside a tool call —
cannot be spoofed.
Behaviour after fix:
* Skill body injection → skipped → walk continues back to find user's
real prompt (with coverage line).
* The assistant message containing the Skill tool_use is now inside the
turn → detectLegitSkillActive finds it → normative writes pass when
invoked under an active claude-md-management skill.
* "Continue from where you left off." → still treated as turn boundary.
* Stop hook feedback strings → still treated as turn boundary.
TDD:
* 3 new tests in tools/enforce-hook-helpers.test.mjs under the
"lastTurnEntries / lastUserPromptText / lastAssistantText / turnToolUses"
describe block:
- lastTurnEntries skips skill body injections (isMeta + sourceToolUseID)
- lastTurnEntries does NOT skip "Continue from where you left off"
(isMeta but no sourceToolUseID)
- turnToolUses includes Skill tool_use spawned in same turn as the
injected skill body
* 2/3 RED→GREEN (the "Continue" negative test passed on baseline already
since its string content satisfies the existing string-content branch).
Scope:
* Fixes 2 of the 5 structural quirks documented in the Stream H
completion log (enforce-memory-coverage gap, enforce-normative-
content-rules detectLegitSkillActive gap).
* Does NOT fix: enforce-read-path-deny LEGIT_SKILLS exemption gap
(separate hook, no lastTurnEntries dependency); TDD-gate cross-actor
blindness (different mechanism — actor session boundaries);
detectFullTestRun regex narrowness (command-pattern matching).
Regression: vitest tools 1788/1788 GREEN (was 1785; +3 new tests).
Plan: docs/superpowers/plans/2026-05-30-lastturnentries-skill-body-skip.md
findOverride/findOverrideAttempt/loadOverrideVocab become permanent stubs returning null/null/empty.
Non-deleted hooks (verify-before-push, tdd-gate, memory-coverage, branch-switch) still import these
symbols and need them to compile; runtime always reports 'no override'.
Adapted 15 existing tests in enforce-hook-helpers.test.mjs and 7 in enforce-semgrep-security.test.mjs
that asserted old vocab behaviour; all now assert stub behaviour (null/empty).
1824/1824 vitest tools GREEN.
Stream G of router-gate v4 deployment.
Code-quality review of Task B (Phase 4) flagged two minor fixes:
- Export CHAIN_OUTCOME_BUCKETS for external consumers (test + future cuts)
no longer hard-code bucket names.
- Replace fs.readFileSync via duplicate `import fs from 'fs'` with the
already-imported named `readFileSync` in helpers test.
+1 regression test on the export.
The verify-before-push hook now skips the regression gate when EVERY
staged/unpushed file is a .md document (memory, docs, specs, plans,
SKILL.md). Code-touching pushes remain fully gated as before; mixed
pushes (even one non-md file) keep the full gate.
Closes the recurring loop where Claude invokes the "ремонт инфраструктуры"
override on every docs-only push — regression adds no value when the
change set has no executable code.
New helpers (tools/enforce-hook-helpers.mjs):
- isDocsOnlyPath(p): true iff path ends with .md (case-insensitive)
- isDocsOnlyChange(paths): true iff non-empty AND every entry docs-only
- listChangedFiles(kind): git diff --cached (commit) / @{u}..HEAD (push)
Empty result = unknown -> caller MUST fall through to normal gate.
decide() in enforce-verify-before-push.mjs accepts a new changedPaths
arg and short-circuits {block: false} when isDocsOnlyChange === true.
Empty/undefined -> falls through (conservative).
TDD: 13 new tests across enforce-hook-helpers.test.mjs + enforce-verify-
before-push.test.mjs, all GREEN. Tools-only canonical regression 965/965.
Brain-retro #5 candidate C, hole 7: the 'ремонт инфраструктуры' phrase
suppressed ALL rule keys with no constraint. Now requires a 'ремонт: <what>'
line in the same prompt documenting the target.
enforce-override-vocab.json: added 'requires_justification: "ремонт:"' to
the entry.
enforce-hook-helpers.mjs findOverride(): honors requires_justification — when
set, the user prompt must contain '<prefix> <non-empty-text>' or the override
is rejected.
Previous segment-split approach still mis-detected because naive && split
also splits INSIDE quoted commit messages. A git commit with a body like
'... npx vitest run ...' produced a segment starting with vitest after split.
New approach: find FIRST real command (after skipping cd / env-prefix),
classify based on that. Anything after it is arguments / chained commands,
which don't change the kind. Hard guard rejects first-real ∈ {git, scp, ssh,
curl, cat, echo, grep, cp, mv, ...}.
Found live: my own commit message from the previous fix ('handles compound
commands like cd ... && npx vitest run') caused the verify-pass sentinel to
overwrite as fail. Test for this case in helpers.test.mjs.
Previous guard ("any \b(git|cat|echo)\s/ → null") was too aggressive: it
blocked legitimate compound test commands like `cd ... && npx vitest run`
or `npx vitest run && echo done`.
New approach: split on shell separators, examine each segment after stripping
env-prefix and `cd` prefix. A command is a test run iff some segment STARTS
with a recognised test-invocation token. Correctly handles both directions:
- false-positive guard (commit message containing 'vitest run' → null)
- false-negative fix (compound 'cd ... && vitest run' → vitest-full)
Live-caught by my own TDD-gate: prod-edit blocked, wrote tests first, RED
verified, then GREEN. 59/59 unit tests pass.