code-yeongyu / code-yeongyu/senpi

[Bug]: eval summary clamping triggers assistant_rewritten with claude-sdk-oauth

Open
#1,472 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
429
Forks
98
Avg merge
5h 3m
Merged PRs (30d)
526

Description

## What happened?

With the built-in `eval` tool and `claude-sdk-oauth`, `eval.prepareArguments` clamps a summary longer than 80 characters by mutating the assistant tool-call arguments.

`AssistantCommitBoundary` hashes the provider-final assistant before that mutation and the committed assistant after it. The two summaries differ, so the turn is classified `assistant_rewritten` and continuity is invalidated. The next turn then forks or cold-flattens the session.

Live confirmation on an interactive root session (senpi 2026.9.6, `claude-opus-5`): I sent an 81-character summary, the session stored 80 characters ending in `...`, a `claude-sdk-oauth-binding` entry recorded `{"invalidated":true,"reason":"assistant_rewritten"}`, and the following turn reported `{"kind":"flatten","reason":"assistant_rewritten","deltaMessages":193,"payloadBytes":1209471}` with 562,740 cache-write tokens in that single request.

In one earlier long session, 11/11 `assistant_rewritten` events matched the same pattern: original summaries were 81-103 characters and every committed summary was exactly 80.

This is distinct from #691, which normalized volatile `thinking.startedAt` / `thinking.endedAt`. Here the differing field is `content[*].arguments.summary`.

## Steps to reproduce

Deterministic and offline, against the installed modules:

1. Import `AssistantCommitBoundary` from `.../claude-sdk-oauth/session-commit-boundary.js` and `clampEvalSummary` from `.../senpi-codemode/src/tool/eval-request.ts`.
2. Build a resident assistant message whose content is one `eval` tool call with this 81-character summary:

`01234567890123456789012345678901234567890123456789012345678901234567890123456789X`

3. Call `captureProviderFinal(key, message)`.
4. Apply the clamp: `message.content[0].arguments.summary = clampEvalSummary(message.content[0].arguments.summary)`.
5. Call `commit(key, message, modelId)`.

Observed: `rewritten`. With a summary of 80 characters or fewer the same sequence returns `clean`.

Live variant: in an interactive `claude-sdk-oauth` session, have the model call `eval` once with the same 81-character summary, send one more message, then read the session JSONL.

Relevant paths:

- `packages/senpi-codemode/src/tool/eval-request.ts` (`clampEvalSummary`)
- `packages/senpi-codemode/src/tool/eval-tool.ts` (`prepareArguments`)
- `packages/coding-agent/src/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.ts` (`assistantContentHash`)

## Expected behavior

Argument normalization should not invalidate Claude SDK session continuity. Either canonicalize the summary before `captureProviderFinal`, or apply the same normalization inside `assistantContentHash`. The reproduction above should return `clean`, and the next turn should stay incremental when nothing semantic changed.

## Version

- `senpi`: 2026.9.6
- `omo-ai`: 5.0.0-0.beta.45
- Provider: `claude-sdk-oauth`
- Models observed: `claude-opus-5`, `claude-fable-5-1`
- OS: Windows 11 x64

Contributor guide

Open the contributing guide

Research direction

Start with packages/senpi-codemode/src/tool/eval-request.ts and eval-tool.ts, then inspect assistantContentHash and the captureProviderFinal/commit flow in packages/coding-agent/src/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.ts. Run the deterministic reproduction with an 81-character summary and verify it returns clean and that the next turn remains incremental when only normalization differs.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.