graceful-handoff/CHANGELOG.md
Kjell Tore Guttormsen 96a22474bd feat(graceful-handoff): sync the ritual with the global session-end mechanism (v3.2.0)
SKILL.md had drifted from the global CLAUDE.md on two counts, both verified by
grep before the rewrite: the STATE.md template predated the mandatory `board:`
line and the `route:`/`route-last:` lines, and the closing line still demanded
three fields where six are now required (Innboks, Modell neste okt,
Oppstartskommando were missing).

- New step 3 routes the next session via `repo-mailbox:route` BEFORE the Write.
  It cannot run after the commit: the emitted lines live inside STATE.md, so
  routing afterwards would dirty a file that was just committed. One invocation
  feeds both the three comment lines and the closing line's model fields.
- `repo-mailbox` stays a soft dependency — documented fallback if it is absent
  or the cross-plugin Skill invocation is blocked. `route.sh`'s path is
  deliberately not hardcoded (plugin cache, versioned, drifts).
- The single-line constraint on the three comments is now in prose: `board.sh`
  reads the first non-blank, non-heading, non-`<!--` line under the heading as
  the repo's next step, so a wrapped `rationale=` corrupts the board.
- Closing line 3 -> 6 fields. The Innboks field reports what the session did
  rather than re-querying the mailbox — inbox handling belongs first in a
  session, and "no inbox injected" must never be reported as "empty".
- STATE format consolidated to ONE copy. Repo CLAUDE.md restated it with the
  same defect; it now points at SKILL.md step 4 as the authority, following the
  model-rubric precedent (two copies drift, prose cannot be tested).
- allowed-tools gains `Skill`. `plugin.json` description left unchanged on
  purpose — editing it would require the manual marketplace.json edit that
  release-plugin.mjs does not perform.

Tests 30 -> 42, all green. They are prose greps: drift guards, not proof the
ritual runs. Verifying that means a manual /graceful-handoff against a scratch
repo.

Release (tag + catalog ref bump) is operator-gated and NOT done here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013V59bNbWa5x2oTH2NMBJy4
2026-08-09 21:23:39 +02:00

139 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Changelog
All notable changes to graceful-handoff are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [3.2.0] - 2026-08-09
### Changed
- **The ritual is back in sync with the global "Session Slutt" mechanism.** SKILL.md had drifted: its STATE.md template predated the mandatory `board:` line and the `route:` / `route-last:` lines, and its closing line still demanded three fields where the global CLAUDE.md now requires six. Both are fixed, and the ordering matters — see below.
- **New step 3: route the next session *before* STATE.md is written.** The ritual now invokes the `repo-mailbox:route` skill between `--plan` and the Write. It cannot run after the commit: the emitted lines live *inside* STATE.md, so routing afterwards would dirty a file that was just committed. One invocation feeds two consumers — the three comment lines in the template, and the `Modell neste økt` / `Oppstartskommando` fields in the closing line. Deriving the model by hand in the closing line is exactly what the lookup exists to prevent.
- **`repo-mailbox` is a soft dependency.** The plugin is separately installable, so step 3 documents a fallback: if `repo-mailbox` is absent (or the cross-plugin `Skill` invocation is blocked), the ritual continues, fills `next-cost` by judgement, and says so explicitly rather than passing it off as a rubric lookup. `route.sh`'s path is deliberately **not** hardcoded — it sits in the plugin cache behind a version number that drifts.
- **The template now carries the single-line constraint in prose, not just by example.** `board.sh` reads the first line under the `👉 NESTE` heading that is non-blank, non-heading and does not *start* with `<!--`, and shows it as that repo's next step across every repo — so a `rationale=` wrapped across two lines replaces the repo's next step on the board with garbage.
- **Closing line: 3 fields → 6.** Added `Innboks`, `Modell neste økt` and `Oppstartskommando`. The inbox field **reports what the session did** rather than re-querying the mailbox: inbox handling belongs first in a session, and a fresh read at handoff time surfaces messages too late to act on. A new light step 7 makes the distinction explicit, including that "no inbox was injected" must never be reported as "empty ✅".
- **STATE.md format consolidated to one copy.** The repo's own `CLAUDE.md` restated the format — with the same missing lines, the identical defect in a second location. It now points at SKILL.md step 4 as the authority instead, following the precedent set when the model rubric was removed from the global CLAUDE.md: two copies drift, and prose in a doc file cannot be tested.
- `allowed-tools` gains `Skill` (needed to invoke `repo-mailbox:route`). `Bash` stays sub-scoped; `curl`/`wget` remain unapproved.
- **`plugin.json`'s `description` is deliberately unchanged** — it is still accurate, and editing it would require the manual `marketplace.json` description edit that `release-plugin.mjs` does not perform.
### Tests
- 30 → 42. Eleven new guards in `skill-structure.test.mjs` (all three comment lines present, closed status-token set, single-line warning, `repo-mailbox:route` referenced, route step ordered *before* the Write, documented fallback, all six closing-line fields, the "three items" phrasing gone, the inbox honesty rule, and repo `CLAUDE.md` deferring to SKILL.md) plus the version/CHANGELOG pins in `plugin-manifest.test.mjs`.
- **These are prose greps — drift guards, not correctness proofs.** None of them executes the ritual. Verifying it means running `/graceful-handoff` manually against a scratch repo.
### Changed (documentation, released with this version)
- **README brought in line with the org repo standard** (`repo-standard` gate, 0 ERROR). Documentation only — no plugin code, version, or behaviour changed. H1 is now the repo name; the opening line is verbatim the forge description (so description == catalog == README is machine-checkable); `## Install` is a top-level heading on the first screen and carries the CLI command `claude plugin install graceful-handoff@ktg-plugin-marketplace` next to `marketplace add`; `## Non-goals` and `## Changelog` sections added.
- **Dropped the static `tests-30` badge.** This forge has no CI runner, so the badge asserted a run nothing verifies. The Testing section now states the command and says plainly that nothing runs it automatically. Note for the catalog: the tests axis no longer has a badge backing it.
- **Fixed two dead `../../README.md` references** inherited from the monorepo (README lines 7 and the marketplace footer) — both pointed outside a standalone repo and 404'd.
- **Dropped the `hooks-0` badge** (6 → 5 badges, clearing the `repo-standard` BADGE-COUNT warning). A badge asserting an absence is not something a reader acts on, and "No hooks" is already stated in prose where it carries its actual argument. The `skill-1` badge was deliberately kept: it is the only axis the catalog's version gate can still verify for this plugin.
## [3.1.0] - 2026-06-24
### Changed
- **Removed the hardcoded push window — push is now unconditional, Forgejo only.** The regime-wide push-window restriction (mantor 08:0017:00, fre 08:0016:00) was lifted permanently, so SKILL.md no longer gates the push on a weekday/time window — it would otherwise park a push in the middle of working hours. The two other invariants are unchanged: **push is Forgejo only, never GitHub**, and **push stays user-triggered** (the skill is `disable-model-invocation: true` and never auto-pushes outside a user-invoked handoff). `handoff-pipeline.mjs` had no window logic — only its header comment was corrected. If a future repo needs a window again, reintroduce it as per-repo config, never as a hardcoded default.
## [3.0.0] - 2026-06-23
### BREAKING
- **Full integration with the STATE.md continuity system.** The plugin no longer writes its own `NEXT-SESSION-PROMPT.local.md` artifact. `/graceful-handoff` now overwrites the **nearest STATE.md** (cwd → repo root) with a complete state-of-play in the mandatory format — title + subtitle + a `## 👉 NESTE — START HER` block on top, then fixed sections and short history. STATE.md is the one continuity layer; local handover inventions (`NEXT-SESSION`, `HANDOVER.md`, `ROADMAP.md`) are gone.
- **Inverted architecture: the model writes, the script assists.** Only the session model has the context to fill the `👉 NESTE` block meaningfully — a deterministic `git log` snapshot never could. `scripts/handoff-pipeline.mjs` is now a slim, deterministic STATE helper: `--plan` (resolve nearest STATE.md + classify remote + git facts, read-only), `--commit` (stage **only** STATE.md when tracked, plus explicit `--also` paths — never `git add -A`), `--dry-run`. It no longer renders artifacts.
- **All three hooks removed.** The Stop auto-trigger, the statusLine hint, and the SessionStart auto-loader are deleted (`hooks/` is gone):
- *SessionStart loader* was redundant — the global `~/.claude/hooks/session-start.sh` already injects the nearest STATE.md.
- *statusLine* was dead code — a user's `~/.claude/settings.json` statusLine always wins over a plugin's, and it already shows context %.
- *Stop auto-trigger* would overwrite a good, model-written STATE.md with a poor snapshot. Removed in favour of a purely manual regime.
- **Removed the `model: claude-sonnet-4-6` pin.** Writing a self-standing STATE.md is human-facing synthesis → the skill now inherits the session model (Opus-class quality).
### Changed
- **Remote-aware STATE policy.** STATE.md must never reach a public mirror. The helper classifies the `origin` remote (`github.com` or the `open/` Forgejo namespace → public → STATE local-only/gitignored; otherwise → private → STATE tracked). The authoritative commit signal is `git check-ignore STATE.md`; `remote_class` only drives a `leak_warning` when the two disagree.
- SKILL.md is now the operationalised "Session Slutt" ritual: reach a natural stopping point → `--plan` → write STATE.md → `--commit` → push only inside the window → fixed closing line.
- Tests rewritten around `--plan`/`--commit`; the no-`git add -A` regression guard is preserved. Hook tests removed.
### Migration from v2.x
No automatic migration. Reinstall to drop the hooks. `/graceful-handoff` now writes STATE.md instead of a NEXT-SESSION artifact; any stale `NEXT-SESSION-*.local.md` files can be deleted. STATE.md auto-loads at session start via the global hook — the plugin no longer ships its own loader.
## [2.1.0] - 2026-05-01
### Fixed
- **Modell-bevisst kontekstvindu i Stop hook (`stop-context-monitor.mjs`).** Tidligere fallback antok 200K-vindu. På Opus 4.7-sesjoner (faktisk 1M-vindu) kunne auto-handoff fyre 57x for tidlig — estimert 70% når reell bruk var ~14%. Erstatter `windowSize = payload?.context_window?.context_window_size || 200_000` med en 4-stegs resolution-kjede (`resolveContextSource()`):
1. `payload.context_window.used_percentage` (autoritativ, modell-agnostisk — kilde: `direct`)
2. `payload.context_window.context_window_size` + transcript-estimat (kilde: `payload-size`)
3. `MODEL_WINDOWS[payload.model.id]` + estimat (Opus 4.7=1M, Sonnet 4.6=200K, Haiku=200K — kilde: `model-map`)
4. `FALLBACK_WINDOW = 1_000_000` + estimat (oppdatert 2026-default — kilde: `default-1m`)
### Changed
- `additionalContext`-meldinger fra Stop hook inkluderer nå `[kilde: <source>]` for innsyn i hvilken resolution-path som ble brukt (`direct`/`payload-size`/`model-map`/`default-1m`).
- Inline-kommentar (linje 1422) og README/CLAUDE.md-seksjoner om auto-trigger-mekanikk oppdatert til å beskrive ny kjede.
### Tests
- 6 nye tester i `tests/hooks/stop-context-monitor.test.mjs` dekker hver path: `used_percentage` foretrukket, `used_percentage` trigger med tom transcript, model-map for Opus 4.7 (1M) og Haiku (200K), default 1M-fallback med tomt payload, og `null used_percentage` faller gjennom til size-pathen. Total: 56 tester (50 + 6).
### Open
- Stop-hook payload-schema er ikke offisielt dokumentert. Det er ikke bekreftet at Stop-payload faktisk inneholder `used_percentage` eller `model.id` (statusLine-payload gjør). Hvis ingen av feltene leveres, faller resolveren til `default-1m`. Smoke-test ved første Opus 4.7-sesjon vil avgjøre hvilken kilde som blir primær.
## [2.0.0] - 2026-05-01
### BREAKING
- **Hard cut from `commands/` to `skills/`.** The plugin now ships a single SKILL.md at `skills/graceful-handoff/SKILL.md` with `disable-model-invocation: true` and `model: claude-sonnet-4-6`. The legacy `commands/graceful-handoff.md` is deleted. User-invocation `/graceful-handoff` works as before.
- **Architecture rewrite.** The 6-phase prose workflow is replaced by a deterministic Node script `scripts/handoff-pipeline.mjs` that returns structured JSON. SKILL.md is now a thin orchestration wrapper. Tests run directly against the pipeline without LLM involvement.
- Removed `auto_discover: true` from `plugin.json` (not in documented schema; silently ignored anyway per research/05).
### Added
- **Auto-trigger via Stop hook (`hooks/scripts/stop-context-monitor.mjs`).** Estimates context usage from transcript size; at estimated ≥70%, auto-writes the artifact and creates a commit. Push remains user-triggered (separates reversible from irreversible). Lock file at `<transcript_dir>/.handoff-lock-<session_id>` prevents repeat firing within a session.
- **Context hint via statusLine (`hooks/scripts/statusline-monitor.mjs`).** Reads `context_window.used_percentage` from payload; prints a hint at 60% and an urgent reminder at 70%. Display-only — never runs git (unsafe per research/03).
- **Auto-load via SessionStart hook (`hooks/scripts/session-start-load-handoff.mjs`).** On `source: resume` or `source: compact`, finds `NEXT-SESSION-*.local.md` (cwd + 3 levels up), injects content via `additionalContext`, archives the file (`*.archived.local.md`) to prevent stale-load.
- Commit-message confirmation gate: pipeline prints message to stderr, reads `y/n` from stdin (interactive). `--auto` flag bypasses for hook-driven invocations.
- New flags: `--no-push` (commit but don't push), `--auto` (non-interactive auto-Y), `--non-interactive`.
- Pipeline robustness: detached HEAD detection, no-upstream detection, idempotency check (60s cooldown on clean tree), pre-commit hook respect.
- 36 unit tests across 5 test files (skill-structure, pipeline, statusline-monitor, stop-context-monitor, session-start-load-handoff).
### Changed
- **Pipeline staging discipline (CRITICAL).** Pipeline now stages ONLY the handoff artifact (and REMEMBER.md/TODO.md if present). Previously used `git add -A` which scoops up unrelated work-in-progress. The new behavior is enforced by a regression test.
- `allowed-tools` is now Bash sub-scoped (`Bash(git:*) Bash(node:*) Bash(jq:*) ...`) instead of an open `Bash`. Note: per research/02, this is pre-approval (not restriction) — to actually block tools, project-level deny rules are needed.
- Plugin model is pinned to `claude-sonnet-4-6` (was: inherit from session). Frees Opus 4.7 budget for the next session that the user is actually entering.
### Known limitations
- statusLine placement in `hooks/hooks.json` is an open assumption (research/03 confirmed statusLine config exists, but exact placement vs `settings.json` is unverified). Smoke-test required.
- Token estimation in Stop hook uses `chars/3.5` heuristic — may drift ±10% from Claude's internal counting. The 70% threshold is conservatively set.
- `disable-model-invocation: true` has open issue [#26251](https://github.com/anthropics/claude-code/issues/26251); manual smoke-test recommended before relying on it.
- Auto-execute does not push: irreversible operations remain user-triggered.
### Migration from v1.0.0
There is no automatic migration. v2.0.0 is a breaking change.
1. Reinstall the plugin to pick up `skills/` and remove `commands/`.
2. The `/graceful-handoff` slash command works identically from the user's perspective.
3. The new auto-trigger features activate automatically when the plugin's hooks are loaded.
## [1.0.0] - 2026-04-19
### Added
- Initial release with single command `/graceful-handoff`
- 6-fase deklarativ workflow: detect → classify → write artifact → update REMEMBER/TODO → commit+push → print copy-paste-prompt
- Tre handoff-typer: `multi-sesjon` (ultraplan-prosjekt), `plugin-arbeid` (marketplace-plugin), `enkelt-oppgave` (fallback)
- Default filnavn `NEXT-SESSION-PROMPT.local.md`; slug-override via første posisjons-argument
- Flag: `--no-commit` (skip git), `--dry-run` (ingen skriving, ingen git-ops)
- Auto-generert Conventional Commits-melding fra `git diff --stat`
- Respekterer pre-commit hooks (secrets, pathguard) — bypasser aldri
- Tidsbudsjett < 60 sekunder; ingen Agent-delegering, ingen WebSearch
- 7-seksjons-template for NEXT-SESSION-artefakt (matcher eksisterende konvensjon i llm-security/config-audit)
### Notes
- Auto-discover plugin — `marketplace.json`-oppføring ikke nødvendig
- MIT-lisens