The pipeline had never been run against an actual repository — every test in
the suite is either a prose-grep over SKILL.md or a unit test that asserts key
presence. Running it against scratch repos (private remote, and public `open/`
remote with and without a gitignored STATE.md) found two defects living under
a 42/42-green suite.
1. dirty_files truncated the first path. gitOk() trims every command's output,
but `git status --porcelain` puts the worktree status in column 2, so a
modified-but-unstaged file is " M path". The trim ate the leading space and
the fixed slice(3) then ate the first character: app.js was reported as
pp.js. Only the first line is affected, which is why it survived — no test
asserted dirty_files VALUES, only that the key existed. Porcelain now goes
through a non-trimming gitOkRaw().
2. The commit message claimed a STATE.md update it did not contain. The
message was hardcoded to "oppdater STATE.md" regardless of what was staged.
On every `open/` repo STATE.md is gitignored, so the handoff commit carries
only the --also paths. Git history is the regime's long-term log; it was
systematically wrong about its own contents.
Also promotes the leak condition from advisory to hard gate. A public remote
whose STATE.md is not yet gitignored is the state a FRESH open/ repo starts
in, and should_commit_state was true there — the ritual only mentioned
leak_warning, then committed. It now lands in errors[] (step 2 stops on a
non-empty errors[]), should_commit_state is false, and --commit refuses to
stage STATE.md. Explicit --also paths are still honoured: the gate protects
STATE.md, not the commit as a whole.
And corrects SKILL.md's justification for the single-line rule. It claimed a
wrapped rationale= replaces the board's next step with garbage; board.sh in
repo-mailbox 0.20.3 tracks a comment to its closer, so that no longer follows.
The rule stands, restated with the risk that is still real: a rationale
containing the closer sequence ends its own comment early.
Tests 42 -> 48, all six written failing first.
Still unverified: that /graceful-handoff loads as a user command (#26251), and
that a cross-plugin Skill invocation of repo-mailbox:route passes from a
sub-scoped skill. Both need the catalog ref bumped so the version is installed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RvLY4FwbzY157oVqwnkHD8
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
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.
Two invariants are unchanged: push is Forgejo only (never GitHub), and push
stays user-triggered (the skill is disable-model-invocation: true). The pipeline
script 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.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BcNX2QdgmXyLd2Bt6Z25GU
BREAKING: replace the NEXT-SESSION artifact + 3 hooks with a STATE.md-centric,
skill-only design. /graceful-handoff now overwrites the nearest STATE.md (with the
mandatory 👉 NESTE block) instead of writing a separate handover file.
- Remove hooks/ entirely: Stop auto-trigger (operator choice), SessionStart loader
(redundant with global session-start.sh), statusLine hint (dead — user settings win).
- Invert the pipeline: the model writes STATE.md (only it has the context for 👉 NESTE);
handoff-pipeline.mjs becomes a slim deterministic helper (--plan / --commit / --dry-run).
- Remote-aware policy: STATE.md tracked on private remotes, local-only (gitignored) on
public/open mirrors. Authoritative signal: git check-ignore STATE.md.
- SKILL.md rewritten as the Session-Slutt ritual; dropped the Sonnet model pin.
- Docs (README, CLAUDE.md, CHANGELOG), plugin.json 2.1.0→3.0.0, .gitignore cleanup.
- Tests rewritten for --plan/--commit; no-`git add -A` regression preserved. 30/30 green.
Release-cut (tag v3.0.0 + catalog ref bump) pending — separate gated action.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SiKr4c6GAzQH5n6E6f5NA
Stop hook fallback antok 200K-vindu. På Opus 4.7 (faktisk 1M) kunne
auto-handoff fyre 5–7x for tidlig — estimert 70% når reell bruk var
~14%. Erstatter enkel fallback med 4-stegs resolution-kjede:
1. payload.context_window.used_percentage (autoritativ)
2. payload.context_window.context_window_size + transcript-estimat
3. MODEL_WINDOWS[payload.model.id] + estimat
4. FALLBACK_WINDOW=1_000_000 + estimat (2026-default)
additionalContext-meldinger inkluderer nå [kilde: <source>] for innsyn.
Brief som kilde-artefakt i docs/brief-context-window-detection.md.
6 nye tester (57 totalt). Ingen regresjoner.
Step 6 of v2.0 plan. SessionStart hook fires on source: resume or
source: compact, walks up to 3 levels searching for
NEXT-SESSION-*.local.md, injects content via additionalContext, and
archives the file (rename to *.archived.local.md) to prevent stale-load
in later sessions. 9 tests cover sources, multi-level search,
topic-slug variants, archive filtering, malformed payload.
Step 4 of v2.0 plan. statusLine hook reads context_window.used_percentage
from stdin payload and prints display-only hint at 60% / 70%. NEVER runs
git (research/03 — statusLine scripts can be cancelled mid-flight, unsafe
for side effects). 9 tests cover thresholds, null payload, malformed JSON.
Includes hook-helper.mjs copied from llm-security as test infrastructure.
Step 1 of v2.0 plan. Hard cut from commands/ to skills/ per Anthropic
recommendation for new plugins. Frontmatter sets disable-model-invocation:
true and pins model: claude-sonnet-4-6. Docs (README, CLAUDE.md, root
README) deferred to Step 9 per plan.