Skip to content

Live Stream: Hard prune post-compression tool context - #5667

Closed
franksong2702 wants to merge 4 commits into
nesquena:masterfrom
franksong2702:franksong2702/4685-hard-pruning-budget-proof
Closed

franksong2702 wants to merge 4 commits into
nesquena:masterfrom
franksong2702:franksong2702/4685-hard-pruning-budget-proof

Conversation

@franksong2702

@franksong2702 franksong2702 commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Thinking Path

Contract Routing

Task type: runtime/compression/model-context invariant.

Touched areas:

  • api.streaming._prune_context_tool_results_after_compression()
  • model-facing session.context_messages after automatic compression
  • regression coverage in tests/test_auto_compression_card.py

Relevant public docs read:

  • AGENTS.md
  • CONTRIBUTING.md
  • docs/CONTRACTS.md
  • ARCHITECTURE.md
  • TESTING.md
  • docs/rfcs/README.md
  • docs/rfcs/webui-run-state-consistency-contract.md

State layer mutated: model context / context_messages only. Session.messages, visible transcript, Worklog/history rendering, and focused-continuation recovery metadata are intentionally not pruned.

Invariant proved: after a compression event, retained tool-result payloads in context_messages are bounded by the compressor tail budget/default WebUI hard cap while the visible transcript remains inspectable.

What Changed

  • Kept the existing agent-compressor pruning pass when available.
  • Added a deterministic WebUI hard cap for post-compression tool-result payloads in model-facing context:
    • defaults to 4096 rough tokens,
    • honors a smaller compressor tail_token_budget,
    • narrows further to half of threshold_tokens when that threshold is smaller,
    • favors recent tool output by walking tool rows from the tail backward.
  • Replaces over-budget role: tool content in context_messages with a compact summary that states the full output remains in the visible transcript/tool log.
  • Addressed review feedback on residual-budget accounting: once a tool row is over budget, even a note-only replacement consumes the residual budget so older tool rows cannot reuse it.
  • Addressed gate feedback on idempotence: existing [WebUI compressed-context budget: tool summaries are preserved on repeat prune passes while still counting toward the shared rough-token budget.
  • Addressed round-2 gate feedback on marker collisions: already-pruned detection now matches only WebUI's anchored generated summary-note shape, so raw oversized tool output that merely contains the marker literal is still pruned.
  • Clarified snippet summary wording so the note reports omitted-of-total chars when a snippet remains.
  • Added a regression that reproduces the HER-675 shape: a no-op compressor preserves a huge protected recent tool payload, WebUI prunes only context_messages, and Session.messages still contains the original full output.

Why It Matters

Compression should preserve the UI transcript without letting raw tool dumps re-enter the next model request. This closes the budget-proof gap left after replay guards: even if a recent tool result is protected by compressor tail policy, WebUI now enforces a final model-context-only bound before the next turn can inherit that payload.

Release-note wording: After automatic context compression, WebUI now hard-prunes oversized tool-result payloads from model-facing context while keeping the full visible transcript and tool log available.

Verification

  • ./scripts/test.sh tests/test_auto_compression_card.py::test_post_compression_context_prunes_tail_tool_results_with_active_compressor tests/test_auto_compression_card.py::test_post_compression_context_hard_prunes_protected_tail_tool_payload tests/test_auto_compression_card.py::test_post_compression_context_note_only_replacement_consumes_residual_budget tests/test_auto_compression_card.py::test_post_compression_context_hard_prune_is_idempotent tests/test_auto_compression_card.py::test_post_compression_context_raw_marker_collision_still_prunes -q -> 5 passed
  • ./scripts/test.sh tests/test_auto_compression_card.py -q -> 58 passed
  • .venv/bin/python -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • python3 scripts/ruff_lint.py --diff origin/master -> 2 changed Python files, 0 new findings on added/modified lines
  • git diff --check origin/master -> passed
  • Aligned the PR head to gate-certified 9157897cac630eca977339b2ea0dfcca35dbc6ec; git diff --stat 85501acf208874d5892c57d649f9681c29f3ac3c..HEAD produced no output, so this is tree-identical to the locally verified head.
  • Refreshed the gate-passed commit onto origin/master 26a829c952e72b58d74ea00b206845c6db33b39b; new PR head d88cb2ed611c2753bcc301f312584070bcab09ba passed the focused compression tests, the full touched test file, py_compile, diff-scoped ruff, and diff-check.

Risks / Follow-ups

  • The hard cap uses the same rough char-based budget shape as the live prompt estimate fallback, not provider-exact tokenization.
  • This PR bounds tool-result payloads only; it does not attempt a full prompt-level recompression policy for non-tool messages.
  • Track long-running context compression exhaustion and recovery UX #4685 should remain open for broader recovery/diagnostics work.

Model Used

OpenAI GPT-5 via Codex PR-aftercare agent. Notable tool use: local git, gh, PaperClip issue API readback, repo pytest runner, and diff-scoped ruff gate.

Refs #4685.

@greptile-apps

greptile-apps Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a WebUI-owned final hard-pruning pass over model-facing context_messages after automatic context compression, bounding post-compression tool-result payloads to a configurable token cap (defaulting to 4 096 rough tokens) while leaving Session.messages / the visible transcript untouched.

  • The core _hard_prune_post_compression_tool_results function walks tool rows tail-to-head, counts already-pruned summaries against the shared budget, and replaces over-budget raw payloads with a compact note; critically, the budget is exhausted (raw_tool_tokens = budget) after any replacement — including note-only — so older messages cannot silently reuse the same residual slice.
  • Idempotence is guaranteed via _POST_COMPRESSION_TOOL_RESULT_SUMMARY_FLAG: already-pruned messages are counted but never re-replaced, and the flag is carried forward through _restore_reasoning_metadata and stripped by _sanitize_messages_for_api.
  • _merge_display_messages_after_agent_result is updated in four places to prevent pruned-summary messages from leaking into the display transcript via backfill or the candidates loop.

Confidence Score: 5/5

Safe to merge. The change is scoped entirely to model-facing context_messages; the visible transcript and Session.messages are explicitly left untouched, and the flag-based idempotence guard prevents re-pruning.

The hard-prune logic is straightforward and deterministic: a single backward scan, a clear budget-exhaustion rule (raw_tool_tokens = budget fires for every replacement including note-only), and flag-gated idempotence. The restore/sanitize round-trip for the private flag is verified by a dedicated test. All four backfill paths in _merge_display_messages_after_agent_result now filter summary messages consistently, and the new regression suite covers the HER-675 shape, note-only residual accounting, collision safety, idempotence, and API-safe sanitization.

No files require special attention.

Important Files Changed

Filename Overview
api/streaming.py Adds ~107 lines implementing the hard-prune pass; integrates cleanly into the existing compressor-prune flow, flag propagation in metadata restore, and display-merge filtering. Budget exhaustion after note-only replacements is explicitly handled.
tests/test_auto_compression_card.py Adds 10 regression tests covering: protected-tail pruning with a no-op compressor, residual-budget accounting (note-only consumes remaining), idempotence, display-merge isolation, API-safe sanitization + flag restore, idless twin summaries, whitespace counting, and marker-collision safety.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Compression event detected] --> B[_prune_context_tool_results_after_compression]
    B --> C{compressor._prune_old_tool_results callable?}
    C -- yes --> D[Run compressor prune on deep copy]
    D --> E{pruned_count > 0?}
    E -- yes --> F[_deduplicate_context_messages]
    E -- no --> G[Keep context_messages as-is]
    F --> H[pruned_messages]
    G --> H
    C -- no / exception --> H
    H --> I[_hard_prune_post_compression_tool_results]
    I --> J[Walk tool rows tail → head]
    J --> K{msg has _webui_pruned_tool_result_summary flag?}
    K -- yes --> L[Count tokens toward budget, skip replacement]
    L --> J
    K -- no --> M{token_count <= remaining budget?}
    M -- yes --> N[Accumulate tokens, no replacement]
    N --> J
    M -- no --> O[Record replacement, set raw_tool_tokens = budget]
    O --> J
    J -- done --> P{replacements list empty?}
    P -- yes --> Q[Return pruned_messages unchanged]
    P -- no --> R[deep copy, replace content with summary note, set flag]
    R --> S[_deduplicate_context_messages]
    S --> T[context_messages updated - model-facing only]
    T --> U[Session.messages / visible transcript untouched]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Compression event detected] --> B[_prune_context_tool_results_after_compression]
    B --> C{compressor._prune_old_tool_results callable?}
    C -- yes --> D[Run compressor prune on deep copy]
    D --> E{pruned_count > 0?}
    E -- yes --> F[_deduplicate_context_messages]
    E -- no --> G[Keep context_messages as-is]
    F --> H[pruned_messages]
    G --> H
    C -- no / exception --> H
    H --> I[_hard_prune_post_compression_tool_results]
    I --> J[Walk tool rows tail → head]
    J --> K{msg has _webui_pruned_tool_result_summary flag?}
    K -- yes --> L[Count tokens toward budget, skip replacement]
    L --> J
    K -- no --> M{token_count <= remaining budget?}
    M -- yes --> N[Accumulate tokens, no replacement]
    N --> J
    M -- no --> O[Record replacement, set raw_tool_tokens = budget]
    O --> J
    J -- done --> P{replacements list empty?}
    P -- yes --> Q[Return pruned_messages unchanged]
    P -- no --> R[deep copy, replace content with summary note, set flag]
    R --> S[_deduplicate_context_messages]
    S --> T[context_messages updated - model-facing only]
    T --> U[Session.messages / visible transcript untouched]
Loading

Reviews (42): Last reviewed commit: "chore(streaming): remove stale tool summ..." | Re-trigger Greptile

Comment thread api/streaming.py Outdated
Comment thread tests/test_auto_compression_card.py
@franksong2702

Copy link
Copy Markdown
Contributor Author

Addressed Greptile’s residual-budget note in 4224b60ab: note-only over-budget replacements now consume the residual budget, so older tool rows cannot reuse it. Added test_post_compression_context_note_only_replacement_consumes_residual_budget; local focused/full touched-file checks are listed in the updated PR body.

@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from 4224b60 to 825ad2e Compare July 6, 2026 06:02
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

🔬 Gate certification — RED ⛔ (post-compression tool-result prune is NOT idempotent — repeated compression passes re-summarize already-pruned markers — 1 SILENT)

Certified head: sha:825ad2ea (rebased onto current master, git apply clean) · PR: #5667 · franksong2702 (T1), Live Stream: Hard prune post-compression tool context
Verdict: The core prune is sound and safe (no orphaned tool_call/tool_result pairs, deepcopy prevents live-mutation — Codex verified both), but Codex found — and proved by direct probe — that _hard_prune_post_compression_tool_results() is NOT idempotent: re-pruning an already-pruned context (which happens on repeated compression passes in a long tool-heavy session) rewrites the already-truncated markers.

What I ran (rebased worktree /tmp/wt-rebase-5667) — Codex + full suite (backend compression; no Fable surface)

Gate Result
Rebase onto current master ✅ git apply clean
Codex (reproduce) SHIP-WITH-FIXES — 1 SILENT (idempotence, probe-verified)
Full pytest suite ✅ 12198 passed, 0 failed
PR compression tests ✅ 56/56

Findings

✅ Core prune is safe (Codex-verified): _hard_prune_post_compression_tool_results operates ONLY on role=='tool' messages, truncating content to a token budget with a [WebUI compressed-context budget: omitted N chars] marker; uses copy.deepcopy(list(messages)) so it never mutates the live/persisted context; and it does NOT create orphaned tool_call↔tool_result pairs (Codex verified the send-path sanitization preserves paired assistant/tool IDs and this change drops no messages).
⛔ SILENT (Codex, probe-verified) — the prune is NOT idempotent (api/streaming.py:4290): re-running the prune over an already-pruned context (repeated compression passes in a long tool-heavy session) re-summarizes the already-truncated tool results. Codex's direct helper probe: after the first prune, a second prune over many pruned tool rows changed 39 rows, replacing the original markers with new "omitted 157 chars" markers → the marker text gets progressively rewritten/corrupted and token accounting drifts across compression cycles. Fix: detect an existing [WebUI compressed-context budget: marker before summarizing (line ~4298) and leave that tool content unchanged while still counting its rough tokens; add a regression asserting that pruning an already-pruned context twice returns identical contents.

Recommendation to the next agent / author

RED — gate-fail/changes-requested (1 SILENT): make _hard_prune_post_compression_tool_results() idempotent — skip re-summarizing any tool result whose content already contains the [WebUI compressed-context budget: marker (still account its rough tokens toward the budget), + a "prune twice = identical" regression. The core design is correct and safe (no orphaning, no live-mutation); the only gap is that repeated compression passes must not re-rewrite already-pruned markers. concept 4/5 (sound + safe prune, one idempotence gap). Author @franksong2702 (T1). crit=3, compression data-correctness. (Gate value: Codex proved non-idempotence with a direct double-prune probe — the suite's single-prune test can't catch a "prune the already-pruned" cycle that only happens across multiple compressions in a long tool-heavy session.)


Gate-certifier layer (warm-up → gate → release). I do not merge/tag/deploy. Core prune safe (tool-result-only, deepcopy no-mutation, no orphaned pairs — Codex verified) BUT not idempotent: re-pruning already-pruned tool rows rewrites the markers (Codex double-prune probe changed 39 rows). Fix: guard on existing [WebUI compressed-context budget: marker + count-only, + prune-twice-identical regression. Suite green 12198 + 56 compression tests (single-prune only). Backend (no Fable). Cert valid for sha:825ad2ea.

@nesquena-hermes nesquena-hermes added gate-fail Gate found blocking issue(s); fix-spec in comment; awaiting fix/re-push changes-requested Maintainer left detailed feedback requesting changes; PR is waiting on author to address labels Jul 6, 2026
@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from 825ad2e to 8d7b539 Compare July 6, 2026 06:24
@franksong2702

Copy link
Copy Markdown
Contributor Author

Addressed the gate idempotence finding in 8d7b53939: existing [WebUI compressed-context budget: tool summaries are now preserved on repeat prune passes while still counting their rough tokens against the shared budget. Added test_post_compression_context_hard_prune_is_idempotent asserting prune-twice contents stay identical, and updated the summary-note wording for snippet cases. Local checks are listed in the refreshed PR body (4 focused tests, 57-test file run, py_compile, diff-scoped ruff, diff-check).

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

🔬 Gate certification — RED ⛔ (round 2: idempotence fixed, but the already-pruned detection is a bare SUBSTRING check → a real oversized tool result containing the marker string bypasses pruning — 1 SILENT)

Certified head: sha:8d7b5393 (rebased onto current master, git apply clean) · PR: #5667 · franksong2702 (T1), Live Stream: Hard prune post-compression tool context
Verdict: Round-2 fixed my round-1 idempotence SILENT correctly (marker-skip + a genuine prune-twice-identical regression), but Codex found the skip-detection introduces a new SILENT: _is_compressed_context_tool_result_summary() is a bare substring check for [WebUI compressed-context budget:, so a genuine oversized tool result whose ORIGINAL content contains that literal string is mis-detected as already-pruned and skipped — leaving the oversized raw output in model-facing context (the exact bloat the prune exists to remove).

What I ran (rebased worktree /tmp/wt-rebase-5667b) — Codex + full suite (backend; no Fable surface)

Gate Result
Rebase onto current master ✅ git apply clean
Codex (re-verify idempotence) SHIP-WITH-FIXES — 1 SILENT (marker-collision, verified)
Full pytest suite ✅ 12199 passed, 0 failed
Compression tests (incl. new idempotence regression) ✅ 57/57

Findings

✅ Round-1 idempotence FIXED: extracted _POST_COMPRESSION_TOOL_RESULT_MARKER = "[WebUI compressed-context budget:"; the prune now skips rows already containing the marker (still counts their rough tokens); new test_post_compression_context_hard_prune_is_idempotent() asserts pruning twice yields identical content. The core prune remains safe (tool-result-only, deepcopy no-mutation, no orphaned pairs).
⛔ SILENT (Codex, api/streaming.py:4284) — marker-collision bypasses the prune: _is_compressed_context_tool_result_summary() uses a bare substring check (MARKER in text). A genuine oversized tool result whose original content happens to contain the literal [WebUI compressed-context budget: string is treated as already-pruned → skipped → the oversized raw output stays in model-facing context. Narrow (requires the tool output to contain that WebUI-internal string), but it's a real correctness hole — a tool that echoes/quotes the marker (e.g. a grep over the codebase, or a diagnostic dumping prior context) would evade pruning. Fix (Codex): don't detect via substring — mark generated replacements with private metadata (e.g. a _webui_pruned=True flag on the message dict) OR match the exact generated summary-note shape (anchored, not in); + a collision regression test (a raw result containing the marker string must still be pruned).

Recommendation to the next agent / author

RED — gate-fail/changes-requested (1 SILENT): replace the substring already-pruned check with a robust marker — set a private flag on the message dict when the prune generates a replacement (and check that flag), or match the exact anchored summary shape — so a real tool result that merely contains the marker string isn't mistaken for already-pruned; add a collision regression. The idempotence fix itself is correct; this is a hardening of HOW "already pruned" is detected (metadata > substring). concept 4/5 (idempotence done right; the detection mechanism needs metadata not substring). Author @franksong2702 (T1). crit=3, compression data-correctness. (Gate value: detecting "did I already transform this?" by substring-matching your own output marker is fragile — real content can collide with the marker; use out-of-band metadata. Codex flagged it right after the idempotence fix introduced the substring check.)


_Gate-certifier layer (warm-up → gate → release). I do not merge/tag/deploy. Round-1 idempotence fixed (marker-skip + prune-twice-identical regression) BUT is_compressed_context_tool_result_summary is a bare substring check → a real oversized tool result containing [WebUI compressed-context budget: bypasses pruning (SILENT, streaming.py:4284). Fix: private-metadata flag or anchored-shape match, not substring; + collision regression. Core prune still safe. Suite green 12199 + 57 compression (collision not covered). Backend (no Fable). Cert valid for sha:8d7b5393.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Addressed the round-2 marker-collision gate in 85501acf2: _is_compressed_context_tool_result_summary() no longer uses a bare substring check. It now recognizes only WebUI's anchored generated summary-note shape (note-only or snippet-plus-note), so raw oversized tool output that merely contains [WebUI compressed-context budget: still gets pruned. Added test_post_compression_context_raw_marker_collision_still_prunes, including a second-pass idempotence assertion. Local checks are in the refreshed PR body: 5 focused tests, 58-test file run, py_compile, diff-scoped ruff, and diff-check.

@nesquena-hermes nesquena-hermes added size:L Large PR (>10 files or >250 LOC) and removed changes-requested Maintainer left detailed feedback requesting changes; PR is waiting on author to address labels Jul 6, 2026
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

🔬 Gate certification — GREEN ✅ · CONVERGED (round 3 — idempotent + anchored marker detection, all findings closed)

Certified head: sha:9157897c (clean rebase, branch gate-rebase/5667-hard-prune-anchored-marker) · PR: #5667 · franksong2702 (T1), Live Stream: Hard prune post-compression tool context
Verdict: Round-3 CONVERGED. The 3-round arc is closed: r1 non-idempotence (fixed), r2 substring marker-collision (fixed r3 by anchored fullmatch detection). Codex SAFE, full suite green.

What I ran (rebased worktree /tmp/wt-rebase-5667c) — Codex + full suite (backend; no Fable surface)

Gate Result
Rebase onto current master ✅ git apply clean
Codex (re-verify collision + idempotence + core) SAFE TO SHIP — 0 findings
Full pytest suite ✅ 12200 passed, 0 failed
Compression tests (incl. collision + idempotence regressions) ✅ 58/58

Findings — all 3 rounds closed

✅ R2 marker-collision FIXED (anchored detection): _is_compressed_context_tool_result_summary now uses _POST_COMPRESSION_TOOL_RESULT_NOTE_RE.fullmatch(text) (regex full-match, NOT bare substring in) and handles the real trailing-note case via rpartition("\n\n") + fullmatch on the note. So a raw oversized tool result that merely CONTAINS the marker string is no longer mis-detected as already-pruned → it still gets pruned. New test_post_compression_context_raw_marker_collision_still_prunes (raw payload containing the marker + 600 lines must still prune) locks it.
✅ R1 idempotence RETAINED: a genuinely-pruned result (matching the exact note shape) is still detected → not re-summarized; test_post_compression_context_hard_prune_is_idempotent passes.
✅ Core prune still safe: tool-result-only truncation, copy.deepcopy no-mutation, no orphaned tool_call/tool_result pairs. Codex confirmed the fullmatch regex is anchored (no ReDoS), matches the real generated summary shape but not arbitrary mid-body content. Full suite green.

Recommendation to the next agent

Ready to merge — use branch gate-rebase/5667-hard-prune-anchored-marker (sha:9157897c), NOT the PR's stale head 85501acf. A properly-converged compression-context fix: prunes bloated post-compression tool-result payloads, idempotent across compression passes (anchored marker skip), and collision-safe (raw content containing the marker still prunes). Codex SAFE + full suite green + collision & idempotence regressions. concept 4/5 (3-round convergence). Author @franksong2702 (T1). crit=3, compression data-correctness.


Gate-certifier layer (warm-up → gate → release). I do not merge/tag/deploy. R2 collision fixed via NOTE_RE.fullmatch + rpartition('\n\n') note-match (not substring 'in'); r1 idempotence retained; core prune safe (tool-result-only, deepcopy, no orphaned pairs); Codex SAFE (anchored regex, no ReDoS) + full suite green (0 failed) + 58 compression tests incl collision & idempotence regressions. Backend (no Fable). 3-round convergence. Cert valid for sha:9157897c.

@nesquena-hermes nesquena-hermes added gate-pass Full gate passed (Codex+Opus+suite+browser); queued Tier 1 for release agent and removed gate-fail Gate found blocking issue(s); fix-spec in comment; awaiting fix/re-push labels Jul 6, 2026
franksong2702 added a commit to franksong2702/hermes-webui-fork that referenced this pull request Jul 6, 2026
…marker detection)

Clean rebase of franksong2702's nesquena#5667 (rebase-first).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from 85501ac to 9157897 Compare July 6, 2026 08:36
@franksong2702

Copy link
Copy Markdown
Contributor Author

Aligned the PR branch to the gate-certified 9157897cac630eca977339b2ea0dfcca35dbc6ec per the green gate comment. The local rebase skipped the duplicate patch stack onto origin/gate-rebase/5667-hard-prune-anchored-marker; git diff --stat 85501acf208874d5892c57d649f9681c29f3ac3c..HEAD produced no output, so this is tree-identical to the locally verified head while matching the certified commit. Checks have restarted on 9157897c.

franksong2702 added a commit to franksong2702/hermes-webui-fork that referenced this pull request Jul 6, 2026
…marker detection)

Clean rebase of franksong2702's nesquena#5667 (rebase-first).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from 9157897 to d88cb2e Compare July 6, 2026 16:49
@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the gate-passed PR onto the latest origin/master 26a829c952e72b58d74ea00b206845c6db33b39b after GitHub reported the branch behind base. New head is d88cb2ed611c2753bcc301f312584070bcab09ba; the rebase was clean. Local verification on the rebased head: 5 focused post-compression tests passed, tests/test_auto_compression_card.py passed 58/58, py_compile passed, diff-scoped ruff had no new findings, and git diff --check origin/master passed. Checks have restarted on d88cb2ed6.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

🔬 Gate re-affirm — GREEN ✅ (round-4 = rebase-only, code byte-identical to the round-3 converged cert)

Certified head: sha:305dfd4e (branch gate-rebase/5667-hard-prune-r4, rebased onto current master)

The r4 re-push is a pure rebase onto newer master (it picked up the merged #5673 docs) — I verified api/streaming.py AND tests/test_auto_compression_card.py are byte-identical to my round-3 GREEN cert on 9157897c (which had the full Codex-SAFE + suite gate: idempotent + anchored NOTE_RE.fullmatch marker detection, collision-safe). No code changed, so I re-affirm GREEN without a redundant Codex re-run.

Re-check Result
streaming.py + test byte-diff vs r3 cert ✅ IDENTICAL (r4 = rebase + merged docs only)
Full pytest suite (against today's master) ✅ 12200 passed, 0 failed (+ 58 compression tests)

Ready to merge — branch gate-rebase/5667-hard-prune-r4 (305dfd4e), NOT the PR head d88cb2ed. Converged compression-context fix (hard-prune post-compression tool results, idempotent + collision-safe), full gate clean across r3+r4. Credit @franksong2702. concept 4/5, crit=3.

franksong2702 added a commit to franksong2702/hermes-webui-fork that referenced this pull request Jul 6, 2026
…marker detection)

Clean rebase of franksong2702's nesquena#5667 (rebase-first).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from d88cb2e to 150f5eb Compare July 6, 2026 17:28
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

🔬 Gate re-affirm — GREEN ✅ (rebase-only re-push; code byte-identical to the round-3 converged cert) · head 150f5eb7

Verified api/streaming.py AND the test are byte-identical to my round-3 GREEN cert content (Codex-SAFE anchored-marker, idempotent, collision-safe). This is another pure rebase onto advancing master — no code change. GREEN re-affirmed; merge from the gate-rebase branch (gate-rebase/5667-hard-prune-r4, code-identical). No further re-gate needed on rebase-only re-pushes while the code stays identical.

@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from 150f5eb to f73c23f Compare July 6, 2026 18:35
franksong2702 added a commit to franksong2702/hermes-webui-fork that referenced this pull request Jul 6, 2026
…marker detection)

Clean rebase of franksong2702's nesquena#5667 (rebase-first).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the gate-passed PR onto latest origin/master 40ac0c6be2925abe411b6c8c4d8646258ac48ed8 after GitHub reported the branch behind base. New head is f73c23ffdd3ab90b1e531a20c2851ab6a37d452a; the rebase was clean.

Rebase-only safety check: api/streaming.py and tests/test_auto_compression_card.py are byte-identical to previous green PR head 150f5eb7ef3817ec6c798b4a464d5943b2b54ec0.

Local verification on the refreshed head:

  • python3 -m py_compile api/streaming.py
  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 → 58 passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master → no new violations on added/modified lines
  • git diff --check origin/master

Checks have restarted on f73c23ffd.

franksong2702 added a commit to franksong2702/hermes-webui-fork that referenced this pull request Jul 9, 2026
…marker detection)

Clean rebase of franksong2702's nesquena#5667 (rebase-first).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from 036a55f to 1b6ffcb Compare July 9, 2026 04:02
@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the review-feedback fix onto latest origin/master d4e80b45498a914ce67e6b976145804638a46caf after GitHub reported the branch behind base again. New head is 1b6ffcb145f58fe08837fdf249af4bde72aa6a8e; the rebase was clean.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 62 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile are running on 1b6ffcb1.

@franksong2702

Copy link
Copy Markdown
Contributor Author

CI read-back after the refreshed head 1b6ffcb145f58fe08837fdf249af4bde72aa6a8e: all GitHub Actions checks and Greptile Review are green.

The branch is current with base (0 behind / 2 ahead from local ref comparison). This remains ready for maintainer re-review of the review-feedback fix.

franksong2702 added a commit to franksong2702/hermes-webui-fork that referenced this pull request Jul 9, 2026
…marker detection)

Clean rebase of franksong2702's nesquena#5667 (rebase-first).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from 1b6ffcb to f1a4e5e Compare July 9, 2026 04:42
@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the review-feedback fix onto latest origin/master 0c49ed832f0925337981a3beaaeb70f09b2abab9 after GitHub reported the branch behind base again. New head is f1a4e5ef7d79b25900518a8f0b98acc8a389c3db; the rebase was clean.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 62 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile are running on f1a4e5ef.

franksong2702 added a commit to franksong2702/hermes-webui-fork that referenced this pull request Jul 9, 2026
…marker detection)

Clean rebase of franksong2702's nesquena#5667 (rebase-first).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@franksong2702
franksong2702 force-pushed the franksong2702/4685-hard-pruning-budget-proof branch from f1a4e5e to 1251221 Compare July 9, 2026 06:02
@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the review-feedback fix onto latest origin/master 216eb77adea21ea8e8817a569fc358b37594a116 after GitHub reported the branch behind base again. New head is 125122141366d20045023cf44598d28986546042; the rebase was clean.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 62 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on 125122141.

franksong2702 added a commit to franksong2702/hermes-webui-fork that referenced this pull request Jul 9, 2026
…marker detection)

Clean rebase of franksong2702's nesquena#5667 (rebase-first).

Co-authored-by: franksong2702 <franksong2702@users.noreply.github.com>
@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the review-feedback fix onto latest origin/master 66d3b6476db9d477f5eee27f063c235ba3cac1cc after GitHub reported the branch behind base again. New head is a4b0c6f00d4cba2b48ca7859f7bffbbc77d8b5e4; the rebase was clean.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 62 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on a4b0c6f00.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the review-feedback fix onto latest origin/master 531525269e4ee25eea72343f14cbb3dfa082ab23 after GitHub reported the branch behind base again. New head is 8b12cced231dd38f52ce10d530eb1bb1c80eae76; the rebase was clean.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 62 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on 8b12cced2.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Release-manager gate (overnight) — full suite 12,324/0 + dual advisor. This one is a genuine gate split I'm surfacing rather than deciding unilaterally:

Codex: SHIP ONLY WITH FIXES. _is_compressed_context_tool_result_summary() (streaming.py:4332) detects already-pruned rows by content shape (the note text) with no private metadata. Verified probes: (a) a real oversized tool output ending in the exact note shape is treated as already-pruned → stays 19KB in model context unpruned; (b) a legitimate visible tool row matching the shape is dropped in display merge (visible count 1→0). Fix: set a private _webui_pruned_tool_result_summary=True flag on generated replacements and gate all prune/dedupe/display guards on that flag, not the text shape.

Fable: GREEN — ship it (with the same metadata flag as a non-blocking fast-follow). Fable verified the identical shape-collision but rates it LOW-MED, adversarial-only: it requires a real tool output whose final paragraph exactly fullmatches WebUI's generated note format. Crucially: full outputs always remain in the visible transcript (deepcopy-proven), the newest tool result keeps a ≥2KB snippet (budget floors at 512, no zero-out), tool_call_id linkage preserved, idempotent across repeated compressions, non-compressed turns untouched — and pre-PR every oversized payload survived uncompressed, so the PR is strictly a net improvement even at the collision edge.

My recommendation: SHIP (lean Fable). The collision is narrow + adversarial, the full output is never lost (visible transcript), and it's a strict improvement over today's behavior where nothing gets pruned. The _webui_pruned metadata flag is the right durable hardening but is a reasonable fast-follow, not a ship-blocker — the downside it guards is "an adversarially-shaped payload isn't pruned," which is exactly today's baseline for ALL payloads. This is a context-compression path (feeds the model), so flagging for your call. Ship + fast-follow, or hold for the metadata flag first?

@franksong2702

Copy link
Copy Markdown
Contributor Author

Addressed the release-manager metadata-flag gate in 7a14f05c9dbe and refreshed onto latest origin/master 819949fd8cd0876fba3fa7b1db65462423ccac66.

What changed:

  • Generated hard-pruned tool-result replacements now carry private _webui_pruned_tool_result_summary=True metadata.
  • The prune/dedup/display guards now recognize WebUI-pruned summaries by that private flag, not by the generated note text shape.
  • _restore_reasoning_metadata carries the private flag forward after API-safe history round-trips, while _sanitize_messages_for_api still strips it from provider-facing payloads.
  • Added regressions for exact-note-shape raw payloads still pruning, unflagged note-shaped visible tool rows staying visible, flag idempotence, and API-safe stripping/restoration.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 65 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on 7a14f05c.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the metadata-flag fix onto latest origin/master 0d3ee7de9f9f865688531ea77b0a03ff1ef97e04 and addressed Greptile's cleanup note by removing the now-dead text-shape helper _is_compressed_context_tool_result_summary() / regex. Detection now stays exclusively on the private _webui_pruned_tool_result_summary message flag.

New head: 0c910e70fc7d7a6ce4a73c0b99b232bf40703efc

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 65 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD and git diff --check -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on 0c910e70.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the metadata-flag fix onto latest origin/master 91606a15e0dcc9e1f4ee97e3629e39834f03a0c3 after GitHub reported the PR behind base again. The rebase was clean; new head is f320a0be29efbfd7a4f9265e05a8daa2ea7a4096.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 65 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD and git diff --check -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions have restarted on f320a0be.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the metadata-flag fix onto latest origin/master 1e275d04ed699f3f84120847f1d4d192a5722970 after GitHub reported the PR behind base again. The rebase was clean; new head is 69d3adcd63c1def7cde35fde9dab5c4f872bd2ac.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 65 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD and git diff --check -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on 69d3adcd.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the metadata-flag fix onto latest origin/master 7a1a122e6eae6c28332f4d8bbdb2c777b2549456 after GitHub reported the PR behind base again. The rebase was clean; new head is 0aa525d078407d3271c64909c0ea596a2f87292c.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 65 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD and git diff --check -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on 0aa525d0.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the metadata-flag fix onto latest origin/master d6e5dc302d9e1b2c9c17c8f36895ca8c82f08ca6 after GitHub reported the PR behind base again. The rebase was clean; new head is a0c7d43c8bc2636775affea861e6d5de85e08bd1.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 65 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD and git diff --check -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on a0c7d43c.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the metadata-flag fix onto latest origin/master 2b457f164688318917889946831298366d6b49a5 after GitHub reported the PR behind base again. The rebase was clean; new head is f1c3d6768baedfc77a281157059e0743db02be13.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 65 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD and git diff --check -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on f1c3d676.

@franksong2702

Copy link
Copy Markdown
Contributor Author

Refreshed the metadata-flag fix onto latest origin/master 8cc2d6f8817f9949b9db0a7b86a7e2ccb155270f after GitHub reported the PR behind base again. The rebase was clean; new head is 1350182929a32f47ebf3591a0f9bffc321f7914c.

Local verification:

  • ./scripts/test.sh tests/test_auto_compression_card.py -q --timeout=60 -> 65 passed
  • python3 -m py_compile api/streaming.py tests/test_auto_compression_card.py -> passed
  • .venv/bin/python scripts/ruff_lint.py --diff origin/master -> no new violations on added/modified lines
  • git diff --check origin/master...HEAD and git diff --check -> passed

Diff remains scoped to api/streaming.py and tests/test_auto_compression_card.py; GitHub Actions and Greptile have restarted on 13501829.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Deep-review re-gate: SAFE TO SHIP ✅ (gate-pass)

Ran the full authoritative gate (Codex reproduce + 992-test compression/streaming regression suite) on live head 1350182929. The prior gate-fail/changes-requested labels are stale — cleared by this run.

Pairing-safety (the critical concern) verified:

  • api/streaming.py:4334 replaces tool content only on a deep copy — never removes or reorders messages. Pairing probes preserved every assistant tool_call ↔ tool-result ID through both the prune and API sanitization (no orphaned tool_result).

  • api/streaming.py:8980 assigns only s.context_messages (model context), gated by a confirmed compression event. Shared-object probes confirmed s.messages (the visible/persisted transcript) retains full tool output.

  • The private summary metadata is provider-safe, restart-persistent, idempotent across multiple compressions, and filtered from display merges.

  • Codex: no regression risk, core flows intact.

  • Tests: 65 own + 992 regression (compress/context/prune/tool_result/streaming) green.

Certifying gate-pass. Backend streaming-context change (no visible UI). Thanks @franksong2702.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Shipped in exp-v0.52.14 — thanks @franksong2702! Deep-review re-gate verified pairing-safety + context-only + idempotence by reproduction (Codex), 65 own + 992 regression tests green, deployed to experimental. Converged from the earlier bounce. 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changes-requested Maintainer left detailed feedback requesting changes; PR is waiting on author to address gate-pass Full gate passed (Codex+Opus+suite+browser); queued Tier 1 for release agent size:L Large PR (>10 files or >250 LOC)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants