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
139 lines
15 KiB
Markdown
139 lines
15 KiB
Markdown
# 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 (man–tor 08:00–17:00, fre 08:00–16: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 5–7x 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 14–22) 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
|