Skip to content

feat(claude-mod): run optional prompt before automatic compaction - #69

Merged
kunchenguid merged 7 commits into
kunchenguid:mainfrom
KaranSantra:feat/ca-pre-compact-prompt
Oct 9, 2026
Merged

kunchenguid merged 7 commits into
kunchenguid:mainfrom
KaranSantra:feat/ca-pre-compact-prompt

Conversation

@KaranSantra

@KaranSantra KaranSantra commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Intent

The ask, verbatim: "can you adjust compact-adviser auto to stow before runnign compactions?" and then "if you can this once this is done, send this as pr". On which account sends it: "yes use KaranSantra".

Context needed to read it: compact-adviser is a Claude Code plugin that, in auto mode, compacts the conversation by itself at a checkpoint its judge (TypeSafe Jev) scores as finished. Today it calls compaction directly. /stow is a skill meant to run before every compaction. Auto-compaction currently skips it. The PR goes to kunchenguid/compact-adviser from the KaranSantra fork.

Already decided, do not reopen: beforeCompactPrompt stays optional and empty by default, so other users without /stow keep today's compaction. A leading slash runs the user's own configured command. This follow-up only proves that path on the live harness and ignores a plugin turn that starts after the handoff was abandoned before it began.

What Changed

  • Adds a Claude Code–only beforeCompactPrompt setting (empty by default) that, in confirmed auto mode, runs one turn before automatic compaction; a leading / is invoked as a slash command (e.g. /stow) because Claude Code does not expand slash commands in plugin prompts.
  • Compacts only after that turn ends in a settled answer; intervening prompts/commands, interruptions, errors, timeouts, or a handoff abandoned before its turn starts leave context unchanged and defer the next judgment.
  • Documents the setting across the product contract and package READMEs, and covers the handoff path in unit tests and the Claude live e2e harness.

Risk Assessment

⚠️ Medium: The opt-in handoff is bounded, but its asynchronous turn attribution leaves concurrency uncertainty; no new actionable defect was substantiated in this pass.

Testing

Plugin-host suite passed (134). Stock live e2e twice stalled on a request-logging toast flake before the new cases. A disposable focused live harness then drove real Claude Code 2.1.295 under tmux: confirmed auto through the settings pane, set beforeCompactPrompt=/marker and observed marker-then-summary with "automatic compaction completed: 70,040 to 1,519 tokens", and confirmed a typed interrupt during a held before-compact prompt produced no further compaction.

  • Live validation: ✅ go - 2 of 4 scenarios driven live against the product
Scenario Result Live Evidence
Confirmed auto with beforeCompactPrompt=/marker runs the skill, then one automatic compaction ✅ pass live ~/.no-mistakes/evidence/01M4ER684MZ3BM0JW2Y0M0C9D3/before-compact-skill.screen.txt and before-compact-skill.events.json (marker then summary; compaction 70,040→1,519)
A typed user prompt while the before-compact turn is still pending abandons the handoff and does not compact ✅ pass live ~/.no-mistakes/evidence/01M4ER684MZ3BM0JW2Y0M0C9D3/before-compact-interrupted.screen.txt and before-compact-interrupted.events.json
A plugin before-compact turn that only starts after the handoff was abandoned before it began is not judged and does not re-arm compaction ⏸️ untested no Prior payload marked pass with live=false and only plugin-host unit evidence (npm --prefix packages/claude-mod test); that is not a live product drive. Reproducing the post-dispatch pre-start abando…
Empty beforeCompactPrompt (default) still compacts immediately in confirmed auto so users without /stow keep today's behavior ⏸️ untested no Prior payload marked pass with live=false and only plugin-host unit evidence (npm --prefix packages/claude-mod test empty-setting case); that is not a live product drive. The focused TUI harness onl…
Evidence: Focused live before-compact run log
Claude Code 2.1.295 (Claude Code); package ~/.no-mistakes/worktrees/cf51281e7382/01M4ER684MZ3BM0JW2Y0M0C9D3/packages/claude-mod; lab /private/var/folders/hn/_g5l2mlj5wd4rts8_791kjf00000gn/T/ca-before-compact-izLw9S
ok - automatic mode confirmed through the settings pane
ok - automatic mode runs the marker skill, then compacts once that turn ends
ok - a prompt typed while the before-compact turn is pending does not compact

3 focused live checks passed on Claude Code 2.1.295 (Claude Code).
{
  "version": "2.1.295 (Claude Code)",
  "passed": [
    "automatic mode confirmed through the settings pane",
    "automatic mode runs the marker skill, then compacts once that turn ends",
    "a prompt typed while the before-compact turn is pending does not compact"
  ],
  "jevRequests": 2,
  "summaries": 1,
  "liveEvents": [
    "marker",
    "summary"
  ]
}
Evidence: Focused live result JSON (marker then summary, one compaction)

passed: auto confirmed; marker skill then compact; interrupt does not compact. liveEvents: [marker, summary]. summaries: 1

{
  "version": "2.1.295 (Claude Code)",
  "passed": [
    "automatic mode confirmed through the settings pane",
    "automatic mode runs the marker skill, then compacts once that turn ends",
    "a prompt typed while the before-compact turn is pending does not compact"
  ],
  "jevRequests": 2,
  "summaries": 1,
  "liveEvents": [
    "marker",
    "summary"
  ]
}
Evidence: TUI screen after /marker then automatic compaction

❯ /marker → marker written → compact-adviser: automatic compaction completed: 70,040 to 1,519 tokens.

  Implementation notes for the parser module. Implementation notes for the parser module. Implementatio╭────────────────────────────────────
  Implementation notes for the parser module. Implementation notes for the parser module. Implementatio│ compact-adviser
  Implementation notes for the parser module. Implementation notes for the parser module. Implementatio│ compaction completed: 70,040 to 1,…
  Implementation notes for the parser module. Implementation notes for the parser module. Implementatio│ tokens.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementatio╰────────────────────────────────────
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module.

✻ Sautéed for 0s · done 6:17 PM

❯ /marker

⏺ marker written

✻ Worked for 0s · done 6:17 PM
  ⎿  Skills restored (marker)

⏺ compact-adviser: automatic compaction completed: 70,040 to 1,519 tokens.
                                                                                               Conversation compacted (ctrl+o for history)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯ 
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ⏸ manual mode on · ? for shortcuts · ← for agents
Evidence: Event order proving skill before compaction

{"freshEvents":["marker","summary"],"summaries":1,"marker":"ran\n"}

{
  "freshEvents": [
    "marker",
    "summary"
  ],
  "summaries": 1,
  "marker": "ran\n"
}
Evidence: TUI screen after interrupt during before-compact hold

Plugin before-compact prompt (MARKER_SKILL_E2E hold) answered; user typed "wait, one more thing"; no automatic compaction completed notice.

  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module. Implementation notes for the parser module. Implementation notes for the parser module.
  Implementation notes for the parser module.

✻ Crunched for 0s · done 6:17 PM

› Prompt from the compact-adviser plugin
❯ The compact-adviser plugin sent a message:
  Save the notes from this session. MARKER_SKILL_E2E hold

  This is how Claude Code surfaces a prompt a plugin submits between turns — it starts this turn in the user's place. Address the message
  above.

⏺ ok

✻ Sautéed for 12s · done 6:17 PM

❯ wait, one more thing

⏺ ok

✻ Sautéed for 0s · done 6:17 PM

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯ 
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ⏸ manual mode on · ? for shortcuts · ← for agents
Evidence: Interrupted path: summary count unchanged

{"summariesAtInterrupt":1,"summariesNow":1}

{
  "summariesAtInterrupt": 1,
  "summariesNow": 1,
  "liveEvents": [
    "marker",
    "summary"
  ]
}
Evidence: Claude mod plugin-host test log (before-compact suite)

> compact-adviser-claude-mod@0.1.12 test
> node scripts/plugin-test.mjs


tests/adviser.test.ts:
(pass) activation > is inert when CLAUDE_CODE_ENABLE_FUNCTION_HOOKS is unset [39.77ms]
(pass) activation > is inert when CLAUDE_CODE_ENABLE_FUNCTION_HOOKS is true [27.16ms]
(pass) activation > is inert when CLAUDE_CODE_ENABLE_FUNCTION_HOOKS is 0 [24.11ms]
(pass) activation > registers /compact-adviser and does not pin an ambient status line [22.96ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > takes no product action when it is "1" [28.23ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > takes no product action when it is "true" [22.57ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > takes no product action when it is "TRUE" [21.38ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > takes no product action when it is "yes" [20.05ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > takes no product action when it is "on" [21.37ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > takes no product action when it is " on " [24.11ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > stays enabled when it is "0" [18.90ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > stays enabled when it is "false" [21.28ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > stays enabled when it is "no" [21.80ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > stays enabled when it is "off" [19.59ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > stays enabled when it is "" [21.23ms]
(pass) the COMPACT_ADVISER_DISABLE kill switch > stays enabled when it is " " [18.90ms]
(pass) turn-end gates > the selected profile reaches the hook gate and response log [31.59ms]
(pass) turn-end gates > a qualifying settled checkpoint shows the hint once, without blocking the turn [30.14ms]
(pass) turn-end gates > optional request logging writes the TypeSafe body and never the key [28.13ms]
(pass) turn-end gates > each session writes its own request log file [33.02ms]
(pass) turn-end gates > a saved key in a settings.json read is absent from the TypeSafe body and request log [29.81ms]
(pass) turn-end gates > a silent no-qualify turn still logs the Jev response [25.13ms]
(pass) turn-end gates > a judgment failure logs the error kind without the key [29.85ms]
(pass) turn-end gates > the next turn clears the hint without restoring a status strip [23.64ms]
(pass) turn-end gates > below the constant minimum no transcript is read and TypeSafe is not called [24.04ms]
(pass) turn-end gates > the minimum is a token count: a 1M window at 4% still qualifies at 40k [26.56ms]
(pass) turn-end gates > no request without a key, known usage, or in off mode [24.98ms]
(pass) turn-end gates > legacy sharingConsent false in the plugin store is ignored [21.10ms]
(pass) turn-end gates > an empty key never makes a request [22.90ms]
(pass) turn-end gates > cwd .env supplies the key when the host env is empty [43.35ms]
(pass) turn-end gates > a host env key wins over cwd .env [26.72ms]
(pass) turn-end gates > a saved menu key is used when env is empty and wins over cwd .env [23.61ms]
(pass) turn-end gates > a host env key wins over a saved menu key [22.50ms]
(pass) turn-end gates > subagent, interrupted, errored, and empty-answer turns are not checkpoints [22.09ms]
(pass) turn-end gates > non-interactive sessions stay inert [22.66ms]
(pass) turn-end gates > a large static prompt alone is not useful history [20.58ms]
(pass) turn-end gates > a turn that starts before the judgment runs invalidates it [19.90ms]
(pass) turn-end gates > a turn that starts while TypeSafe answers discards the verdict [29.10ms]
(pass) turn-end gates > no repeat at the same checkpoint; a new checkpoint can hint immediately [42.70ms]
(pass) turn-end gates > the hint floor slides with context usage: a finished coordinating unit hints only once the window is fuller [25.68ms]
(pass) turn-end gates > a context budget lets the same judgment hint on a large window [33.24ms]
(pass) turn-end gates > usage follows an enabled auto-compact threshold and otherwise uses the window [36.67ms]
(pass) turn-end gates > unknown context usage keeps the strictest floor [22.48ms]
(pass) turn-end gates > uncertain or insufficient verdicts leave context alone [25.37ms]
(pass) turn-end gates > a rate limit failure backs off and reports once [28.26ms]
(pass) turn-end gates > a malformed failure backs off and reports once [30.99ms]
(pass) turn-end gates > a authentication failure backs off and reports once [28.29ms]
(pass) turn-end gates > a host refusal of nonessential traffic is named in the notice [20.62ms]
(pass) turn-end gates > a two-second TypeSafe timeout leaves context alone [266.65ms]
(pass) turn-end gates > the endpoint override accepts only a loopback fixture [59.53ms]
(pass) turn-end gates > a non-loopback endpoint override is ignored [36.31ms]
(pass) the TYPESAFE_BASE override > TYPESAFE_BASE="" sends the judgment to https://api.typesafe.ai/v1/systemone [22.68ms]
(pass) the TYPESAFE_BASE override > TYPESAFE_BASE="https://proxy.example.test/vendors/typesafe/" sends the judgment to https://proxy.example.test/vendors/typesafe/v1/systemone [25.40ms]
(pass) the TYPESAFE_BASE override > an invalid TYPESAFE_BASE asks nobody, gives no advice, and says why [39.95ms]
(pass) compaction cooldown > any external compaction resets counters; judging waits for 20k fresh tokens and 3 exchanges [33.36ms]
(pass) compaction cooldown > a precompute or a subagent's compaction does not reset the session [23.83ms]
(pass) automatic mode > compacts once at a confident checkpoint and resets the cooldown [27.15ms]
(pass) automatic mode > hint-level confidence also compacts in auto mode [27.52ms]
(pass) automatic mode > auto chosen in /config without the first-use confirmation never compacts [22.52ms]
(pass) automatic mode > incomplete judge coverage still compacts when the judgment qualifies [21.49ms]
(pass) automatic mode > a vetoed or failed compaction backs off for a minute without resetting counters [30.14ms]
(pass) before-compact prompt > submits the prompt, then compacts when that turn ends cleanly [31.28ms]
(pass) before-compact prompt > runs a leading slash as a command, not as model text [23.52ms]
(pass) before-compact prompt > a before-compact command that fails before a turn does not block the next checkpoint [28.53ms]
(pass) before-compact prompt > a before-compact prompt that fails before a turn does not block the next checkpoint [28.53ms]
(pass) before-compact prompt > a person slash command in between is not compacted as the before-compact turn [29.49ms]
(pass) before-compact prompt > a person prompt in between abandons the checkpoint and does not compact [24.92ms]
(pass) before-compact prompt > another plugin's prompt in between abandons the checkpoint [21.10ms]
(pass) before-compact prompt > a person's turn after an abandoned handoff is judged as its own checkpoint [26.43ms]
(pass) before-compact prompt > a person prompt queued during the turn keeps the checkpoint from arming [29.58ms]
(pass) before-compact prompt > another turn starting after the before-compact turn abandons it [20.61ms]
(pass) before-compact prompt > does not compact when a turn starts before the scheduled compaction [22.35ms]
(pass) before-compact prompt > does not compact when a manual compaction runs before the scheduled compaction [23.67ms]
(pass) before-compact prompt > does not compact when a person runs a command before the scheduled compaction [20.80ms]
(pass) before-compact prompt > an interrupted before-compact turn does not compact [22.90ms]
(pass) before-compact prompt > an empty setting still compacts immediately [27.18ms]
(pass) before-compact prompt > hint mode ignores the setting [19.70ms]
(pass) before-compact prompt > a timed-out before-compact prompt does not compact [23.94ms]
(pass) before-compact prompt > a plugin turn that starts after dispatch was abandoned is not judged [23.62ms]
(pass) before-compact prompt > a prompt during that late turn does not make its completion a checkpoint [20.81ms]
(pass) before-compact prompt > a before-compact turn still running at the timeout is not judged when it ends [23.47ms]
(pass) commands > threshold saves a whole token count and reports it; invalid input keeps the setting [25.87ms]
(pass) commands > a minimum at or above the model window saves with a warning, never clamped [16.64ms]
(pass) commands > a save confirmation survives the hot reload a saved row causes [20.09ms]
(pass) commands > a stale save confirmation is discarded [17.56ms]
(pass) commands > a refused save is reported as not saved [16.02ms]
(pass) commands > auto asks once; cancelling keeps the mode, confirming persists across sessions [21.73ms]
(pass) commands > hint and off save and say Claude Code's own compaction is unchanged [17.91ms]
(pass) commands > sharing commands are gone; install is the sharing consent [20.04ms]
(pass) commands > status reports readiness and the engine threshold, never the key [17.71ms]
(pass) commands > a budget below the compaction point relaxes the floor; one above it is ignored [27.25ms]
(pass) commands > status names this session's request log when logging is on [18.00ms]
(pass) commands > snooze suppresses advice for three exchanges; dismiss clears the hint [25.52ms]
(pass) commands > unknown arguments show the usage [22.12ms]
(pass) settings pane > opens focused and lists the Pi menu rows, arrow-keyed, with the values in effect [21.18ms]
(pass) settings pane > a row opens its own view; an option or Back returns to the list [33.00ms]
(pass) settings pane > choosing Automatic asks first; either answer returns to the list [22.52ms]
(pass) settings pane > reset, status, and close act through the same paths as the commands [24.13ms]
(pass) settings pane > the key row names the key in effect and its source, never the value [25.72ms]
(pass) settings pane > an environment key wins over a saved one, and the pane says so [21.15ms]
(pass) settings pane > a denied clear keeps the key view and the saved key [19.75ms]
(pass) settings pane > without any key the list says so and the view explains where one can come from [20.08ms]
(pass) settings pane > another plugin's pane is left to it [16.38ms]

tests/lib.test.ts:
(pass) settings > a budget is a whole number of tokens, and off clears it [1.17ms]
(pass) settings > the minimum is a positive safe whole decimal number of tokens [0.33ms]
(pass) settings > saved API key parsing trims, rejects empty, overlong, and control characters [0.15ms]
(pass) settings > configuration combines the host rows with stored acknowledgement and ignores legacy sharingConsent [0.50ms]
(pass) settings > unreadable rows or consent records are reported, never replaced by permissive values [0.32ms]
(pass) cwd .env key > last TYPESAFE_API_KEY assignment wins; comments and blanks are ignored [0.25ms]
(pass) cwd .env key > export and declare -x prefixes and one matching quote layer are stripped [0.16ms]
(pass) cwd .env key > env wins over saved over .env; empty env falls through [0.27ms]
(pass) session cooldowns > a fresh session has no cooldown; a malformed record restarts snoozed and compacted [0.20ms]
(pass) session cooldowns > after compaction: fresh usage, then 20k more tokens and 3 completed exchanges [0.19ms]
(pass) session cooldowns > a recent hint does not cooldown; snooze and capped exponential backoff still do [0.17ms]
(pass) session cooldowns > only session records older than the retention window are pruned [0.14ms]
(pass) judge input > bounded state excludes secrets and sensitive files and keeps coverage markers [0.76ms]
(pass) judge input > a settings.json read keeps non-secret options and drops the saved TypeSafe key [0.56ms]
(pass) judge input > shell redirection, tee, and sed -i feed the saved-artifact list [0.57ms]
(pass) judge input > tee and in-place sed after a bash reserved word still feed the saved artifacts [0.37ms]
(pass) judge input > a Bash command that writes nothing adds no artifact, and sensitive or failed writes stay out [0.17ms]
(pass) judge input > the saved-artifact bound still holds when the shell writes many files [0.37ms]
(pass) judge input > recent tail keeps the last 64 assistant messages when they fit the byte budget [0.49ms]
(pass) judge input > tool results in the 64-message window keep a head and tail and leave sibling budget [0.25ms]
(pass) judge input > a 64-message window still clips to the tail budget and the request cap [0.71ms]
(pass) judge input > a long transcript stays within the request cap and discloses truncation [0.43ms]
(pass) judge input > the checkpoint fingerprint text follows the latest ask and reply [0.84ms]
(pass) jev client > posts jev-latest with the bearer key in a header and the phase question [0.43ms]
(pass) jev client > errors map to kinds and never to a judgment [0.48ms]
(pass) jev client > strict validation rejects unoffered choices, bad sums, and a non-maximal choice [0.25ms]
(pass) jev client > judgment-failure notices explain the skip and which kinds can be temporary [0.49ms]
(pass) jev client > a missing second answer is rejected [0.08ms]
(pass) jev client > the composed score and the usage-dependent floor decide hint and auto the same way [0.35ms]
(pass) jev client > TypeSafe log lines record the gate decision without secrets [1.57ms]
(pass) jev client > TypeSafe request logs are per session and stay under ~/.claude [0.13ms]

 134 pass
 0 fail
Ran 134 tests across 2 files. [3.09s]
Evidence: Stock live-e2e failure at request-logging toast

> compact-adviser-claude-mod@0.1.12 test:e2e
> node scripts/live-e2e.mjs

ok - Claude Code 2.1.295 (Claude Code): without CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 there is no command
ok - the settings pane opens as one list, naming the .env key in effect, without a sharing toggle
[request logging] never saw "TypeSafe request logging on (all sessions)"
--- screen ---
                                                                             │                                                            ✕
 ▐▛███▛█   Claude Code v2.1.295                                              │Compact adviser  saved for all sessions
▝▜██████▀  Sonnet 4.5 · API Usage Billing                                    │
 ▝▝   ▝▝   ~/project                                                         │Mode                    Hints only (default)
                                                                             │Minimum context         40,000 tokens
▎ Debug mode enabled · logging to /private/var/folders/hn/_g5l2mlj5wd4rts8_79│Log TypeSafe requests   On
▎ 1kjf00000gn/T/compact-adviser-e2e-8miZYD/debug-on.log                      │TypeSafe API key        from .env
                                                                             │Reset minimum to 40,000
⏺ compact-adviser: options changed — reloaded (9 hooks:                      │Status
  session.start, prompt.submit, command.run, turn.start,                     │Close
  turn.complete, session.compact, config.describe, ui.close,                 │↑↓ move · Enter select · Esc close
  ui.render)                                                                 │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
                                                                             │
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯ 
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  ⏸ manual mode on · ? for shortcuts · ← for agents
- Outcome: ⚠️ 1 warning across 1 run (5m58s)

Follow-up notes

Empty beforeCompactPrompt keeps today's behaviour. A leading / goes through command.run, which runs any slash command the session already lists, including a user skill such as /stow, not only plugin-registered commands. Set the value to /stow to run that skill.

VISION.md line 10 says the product never chooses what to keep, and line 16 says the hint is never used to steer the agent. This does not cross them: the prompt is the user's own opt-in text, not the hint, and the host's compaction still decides what to keep.

The API does not document when $.command.run settles for a skill-backed command such as /stow. A long-running skill is covered by the timeout and the turn-id checks, and the live harness now observes that path, but there is no separate assertion of the promise's settle time.

npm run test:e2e on pinned Claude Code 2.1.275, with the session kill switch unset for the test child only: 13 live checks passed, including the marker skill running and then exactly one compaction, and a typed prompt while that turn was pending leaving the conversation uncompacted.

Hook validation spelling, measured: Claude Code 2.1.275 prints config.describe{key=compact-adviser.typesafeApiKey} unquoted; Claude Code 2.1.295 prints it quoted. Both forms stay.

Follow-ups, not fixed here: the stock live harness can miss the request-logging toast on 2.1.295 even when the setting saved; turn.start has no origin field, so a handoff abandoned after dispatch skips judging the next completed turn rather than matching an origin.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • ⚠️ packages/claude-mod/hooks/register.ts:904 - The required behavior is to “ignore a plugin turn that starts after the handoff was abandoned before it began.” This new branch identifies that turn through turn.start.origin, but accesses the field through a cast, and the new test supplies an origin explicitly rather than establishing that Claude Code supplies one. For a late /stow turn whose start event has no origin, the branch at 905 is skipped; after the retry expires, its completion reaches settle at 963 and can judge and dispatch /stow again. Verify provenance on the pinned live host and make the handoff identification work with the event it actually emits. The remedy needs a host-supported way to identify the turn, so it requires the author's decision if that provenance is unavailable.

🔧 Fix applied.
✅ Re-checked - no issues remain.

⚠️ **Test** - 1 warning
  • ⚠️ packages/claude-mod/scripts/live-e2e.mjs:428 - Stock npm run test:e2e on Claude Code 2.1.295 consistently fails at the request-logging pane step waiting for the toast "TypeSafe request logging on (all sessions)" even though the durable outcome is already visible (settings list shows "Log TypeSafe requests On" and options reload). Hot-reload after config.set appears to drop the toast before tmux capture can see it, so the harness never reaches the before-compact cases. Focused live driving that enables auto via the Mode row (durable signals only) and then exercises before-compact passed. CI pins 2.1.275; if that host still shows the toast the stock suite may stay green there. Optional harness hardening: accept pluginOptions().logRequests === true or the On label as success, not only the ephemeral toast.
  • Live validation: ✅ go - 2 of 4 scenarios driven live against the product
Scenario Result Live Evidence
Confirmed auto with beforeCompactPrompt=/marker runs the skill, then one automatic compaction ✅ pass live ~/.no-mistakes/evidence/01M4ER684MZ3BM0JW2Y0M0C9D3/before-compact-skill.screen.txt and before-compact-skill.events.json (marker then summary; compaction 70,040→1,519)
A typed user prompt while the before-compact turn is still pending abandons the handoff and does not compact ✅ pass live ~/.no-mistakes/evidence/01M4ER684MZ3BM0JW2Y0M0C9D3/before-compact-interrupted.screen.txt and before-compact-interrupted.events.json
A plugin before-compact turn that only starts after the handoff was abandoned before it began is not judged and does not re-arm compaction ⏸️ untested no Prior payload marked pass with live=false and only plugin-host unit evidence (npm --prefix packages/claude-mod test); that is not a live product drive. Reproducing the post-dispatch pre-start abando…
Empty beforeCompactPrompt (default) still compacts immediately in confirmed auto so users without /stow keep today's behavior ⏸️ untested no Prior payload marked pass with live=false and only plugin-host unit evidence (npm --prefix packages/claude-mod test empty-setting case); that is not a live product drive. The focused TUI harness onl…
  • npm --prefix packages/claude-mod test (134 pass, including full before-compact prompt suite)
  • npm --prefix packages/claude-mod run test:e2e twice (blocked at request-logging toast on Claude Code 2.1.295)
  • node .../focused-before-compact-live.mjs against real Claude Code 2.1.295 under tmux: enable auto via pane, before-compact /marker then compact, interrupted before-compact does not compact
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Empty beforeCompactPrompt keeps automatic compaction immediate. A non-empty
value, in confirmed auto mode, runs as one turn and compaction follows only
if that turn ends in a settled answer. A leading slash is a command, because
a plugin prompt does not expand slash commands and rejects text that starts
with /.
A dispatched handoff that is abandoned before turn.start now treats the
next plugin-origin turn as that submission and does not judge it. The
live harness also runs a skill-backed command before compaction, and
checks that a typed prompt while that turn is pending does not compact.

@kunchenguid kunchenguid left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Speaking as Kun's firstmate.

contract-class: opt-in — beforeCompactPrompt defaults to ""; empty / hint / off leave today's compaction path. Only confirmed auto + a non-empty user setting runs the handoff.

VISION (per-rule):

  • Timing question only: aligns (optional pre-step on the auto compact path; judge/hint unchanged).
  • Advice for the person, never the model: aligns (hint channel unchanged; before-compact is user-configured command/prompt, not advice fed to the model).
  • Measurement published: aligns (no judge/score change).
  • Cost of being wrong: aligns (timeout / interrupt / error / non-settled turn → no compact).
  • Accuracy entry price: aligns (judge untouched).
  • One policy, several hosts: aligns (Claude-only host config; shared questions/score/floor unchanged; contract states that).
  • What leaves the machine: aligns (no new telemetry; auto still needs explicit ack).
  • Scope: aligns (not a summarizer/context manager; /stow is the user's command).

Fork CI approved after diff review; no security risk. Green CI + CLEAN → squash-merging.

@kunchenguid
kunchenguid merged commit 28be11c into kunchenguid:main Oct 9, 2026
12 checks passed

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: this is merged. Thank you @KaranSantra — really appreciate you taking the time on this.

elkaix added a commit to elkaix/compact-adviser that referenced this pull request Oct 15, 2026
Adopt upstream's before-compact prompt (kunchenguid#69) and context budget (kunchenguid#65)
alongside this branch's re-ask gate and claude-mod hardening.

- compactNow keeps the branch's compaction semantics (counter, stale-turn
  drop, re-ask gate on failure) and serves both the direct and the
  before-compact paths.
- Budget helpers stay in the shared checkpoint.ts; budget "default" is no
  longer an alias for off, and a budget equal to the limit is ignored,
  as upstream decided.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants