fix(session-handoff): PowerShell hook body char count [v0.3.1]

`(& git log -1 --format='%b')` in PowerShell collapses multi-line subprocess
output into string[]. The threshold check used `$body.Length` which on a
string[] returns the line count, not char count — so the body>200 condition
was effectively comparing "more than 200 lines", which is much harder to
meet. Files-count saves it in practice for big commits, but small-file
big-message commits were under-detected.

Fix: join the array back into a single string with `-join "`n"` before
measuring length. Verified via stdin-pipe smoke against current HEAD:
body chars now report 1255 (vs 29 before — the line count).

POSIX `.sh` variant unaffected — `$()` collapses output and `${#var}` is
char count.

Bump 0.3.0 → 0.3.1 PATCH (bugfix, no behavior contract change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 23:41:26 +03:00
parent cc6c321b57
commit d089df7e9f
4 changed files with 17 additions and 24 deletions

View File

@@ -1,5 +1,5 @@
# Task Board
_Updated: 2026-05-24 (session-handoff: 3 baseline 🟢, posttooluse-hook + existing-projects-upgrade → 🔴.)_
_Updated: 2026-05-24 (session-handoff: 4 закрыто 🟢 (install/template-extend/hermes/posttooluse-hook); existing-projects-upgrade → 🔴.)_
<!--
Canonical layout. One block per task. Per-task deep context lives in
@@ -1449,7 +1449,7 @@ Findings → follow-up tasks через tasks_create в claude-skills (slug `ses
---
## [session-handoff-posttooluse-hook] — Автоматизировать substantive-commit detection в session-handoff через PostToolUse hook (settings.json), вместо поведенческой памяти agent'а.
## 🟢 [session-handoff-posttooluse-hook] — Автоматизировать substantive-commit detection в session-handoff через PostToolUse hook (settings.json), вместо поведенческой памяти agent'а.
**Reason:** в текущем SKILL.md (v0.2.0) substantive-commit detection — поведенческое: agent сам после `git commit` помнит проверить эвристику `prefix-exclude AND (body>200 OR files>3) AND (первый non-trivial всегда)`. Менее robust, чем harness-уровневый hook. Один пропущенный «вспомнить» на середине сессии — handoff не обновляется, next агент работает с устаревшим состоянием.
@@ -1459,23 +1459,12 @@ Findings → follow-up tasks через tasks_create в claude-skills (slug `ses
**NB:** это harness-оптимизация поверх скила, не часть самого session-handoff. Скил остаётся работоспособным и без hook'а — просто менее автоматизирован. Bump SKILL.md MINOR не требуется (skill behavior не меняется).
**Status:** ready
**Where I stopped:** (not started)
**Next action:** 1. Открыть update-config flow (или вручную ~/.claude/settings.json).
2. Добавить PostToolUse hook:
- matcher: `Bash`
- command pattern: содержит `git commit` (исключая `git commit --amend`? — design-вопрос)
- action: вызов helper-скрипта, который читает последний commit (`git log -1 --format`), парсит prefix/body/files, решает substantive vs trivial, и при substantive — invoke session-handoff write-mode на agent-стороне (через notification или прямое stdout-сообщение в agent).
3. Helper-скрипт — bash/powershell, лежит в ~/projects/claude-skills/skills/session-handoff/hooks/ (если решим, что hooks — часть скила и distribuируются вместе с install.sh).
4. Smoke test: сделать substantive commit в тестовом проекте → handoff должен обновиться без user-запроса. Сделать trivial `chore:` commit → handoff не трогать.
5. Документировать в SKILL.md секции When to use — «substantive-commit trigger автоматизирован через PostToolUse hook (см. hooks/)» — но только после успешного smoke. До этого момента — оговорка в SKILL.md остаётся «agent-side эвристика».
**Design-вопросы для реализатора:**
- Хук распределяется через install.sh скила (тогда нужно расширять install.sh) или конфигурируется через update-config вручную user'ом? Recommendation: install.sh добавляет hook автоматически, чтобы скил работал «из коробки».
- `git commit --amend` — триггерить или нет? (recommend: НЕ триггерить — амэнд обычно правка предыдущей сессии).
- Cherry-pick / rebase — каскад из множества коммитов, не должен спамить handoff write'ами. Recommend: триггер только на оригинальный commit-event (HEAD@{1} != HEAD предыдущий).
**Branch:** n/a
**Status:** done
**Where I stopped:** done
**Closed by:** Shipped `skills/session-handoff/hooks/` (3 файла: `commit-detector.ps1`, `commit-detector.sh`, `README.md`). Hook reads PostToolUse JSON via stdin, парсит `git log -1`, на substantive emits `hookSpecificOutput.additionalContext` через JSON stdout → Claude Code surface'ит system reminder. **install.sh НЕ мутирует settings.json** — opt-in через README hook-config snippets (cross-platform Windows/Linux/macOS). SKILL.md body When-to-use получил mention "**Optional**: hook см. hooks/README.md". Bumps: 0.2.1 → 0.3.0 (initial hook capability) → 0.3.1 (PATCH PS bugfix: `$body = git log %b` возвращает Object[], `.Length` давало line count не char count — fix via `-join "`n"`). Stdin-pipe smoke (6 scenarios) ✓: substantive HEAD emits JSON (`body 1255 chars`, 6 files), --amend / not-git-commit / failed-commit / empty-stdin / malformed-JSON — silent skip. **Deferred** (partial close): live-hook e2e smoke (enable в settings.json + commit → see additionalContext) — отдельной сессией, чтобы не interfere с current commits + meta-feedback loop. Rebase/cherry-pick batch deduplication — defer to follow-up если actually annoys.
**Branch:** master
<!-- created-by: OpeItcLoc03@DESKTOP-NSEF0UK / from: OpeItcLoc03/workshop / 2026-05-24T18:31:07.605Z -->
<!-- closed-by: vitya@DESKTOP-NSEF0UK / 2026-05-24 / partial: stdin smoke ✓; live-hook e2e deferred; rebase noise dedup deferred -->
---

View File

@@ -30,9 +30,10 @@
- [x] Update SKILL.md body — When-to-use mentions hook as opt-in alternative
- [x] Bump SKILL.md 0.2.1 → 0.3.0 (MINOR — new capability)
- [x] Reinstall via scripts/install.ps1
- [ ] stdin-pipe smoke (substantive HEAD + trivial HEAD)
- [ ] STATUS.md → 🟢 (partial: stdin smoke ✓, live-hook deferred)
- [ ] commit
- [x] stdin-pipe smoke (6 scenarios): substantive HEAD ✓ emits JSON; --amend / ls / empty / malformed / failed-commit ✓ silent skip
- [x] discover + fix PS bug: `git log %b` string[], `.Length` was line count; `-join "`n"` fix; PATCH bump 0.3.0 → 0.3.1
- [x] STATUS.md → 🟢 (partial: stdin smoke ✓, live-hook deferred to separate session)
- [ ] commit (next)
## Notes
Live-hook enable + e2e validation = separate task / separate session. Adding hook to settings.json in this active session would fire on every git commit done here, including the closure commit itself — meta-feedback loop best avoided.

View File

@@ -1,6 +1,6 @@
---
name: session-handoff
version: 0.3.0
version: 0.3.1
description: "Sliding handoff between CC sessions via .tasks/NEXT_SESSION.md. Read on session start: orient agent, ask user before action. Write on session-end phrase or substantive commit. Session-end phrases: «завершаем сессию», «сворачиваемся», «закругляемся», «wrap up session», «end session», «we're done for now». Trigger-line in CLAUDE.md: `session handoff: read on start, write on end`. Skip task-zone phrases: «закрываем эту таску», «pause», «отбой», «разбегаемся»."
---

View File

@@ -43,7 +43,9 @@ if ($inside -ne 'true') { exit 0 }
# Parse last commit
$subject = (& git -C $cwd log -1 --format='%s').Trim()
$body = (& git -C $cwd log -1 --format='%b')
# PowerShell collapses multi-line subprocess output into string[] — join back so
# .Length below is char count, not line count.
$body = ((& git -C $cwd log -1 --format='%b') -join "`n")
$files = ((& git -C $cwd diff-tree --no-commit-id --name-only -r HEAD) | Measure-Object).Count
# Trivial-prefix check (Conventional Commits prefix before optional scope + colon)
@@ -52,10 +54,11 @@ $trivial = @('meta','docs','style','chore')
if ($trivial -contains $prefix) { exit 0 }
if ($subject -match 'fix\s+typo') { exit 0 }
# Threshold check
# Threshold check (chars for body, count for files)
$bodyLen = if ($body) { $body.Length } else { 0 }
if ($bodyLen -le 200 -and $files -le 3) { exit 0 }
# Substantive — emit JSON
$msg = "Substantive commit detected on " + $cwd + ": ``" + $subject + "`` (" + $files + " files changed, body " + $bodyLen + " chars). Consider invoking session-handoff write-mode to update .tasks/NEXT_SESSION.md."