Skip to content

fix(engine): bound every-beat history surfaces — tail-read + recap/recent_narration byte caps (SYN-08) - #852

Merged
100yenadmin merged 1 commit into
mainfrom
fix/syn08-bounded-history-surfaces
Jun 14, 2026
Merged

fix(engine): bound every-beat history surfaces — tail-read + recap/recent_narration byte caps (SYN-08)#852
100yenadmin merged 1 commit into
mainfrom
fix/syn08-bounded-history-surfaces

Conversation

@100yenadmin

Copy link
Copy Markdown
Member

What & why

The per-beat lean spine re-parses the FULL append-only campaign history every beat and emits count-not-byte-capped payloads, so token mass creeps invisibly as campaigns age (the audit projected 30-60KB/beat at 30+ sessions; session_recap reproduced 48,631B live every cold open). This PR bounds the window on the named every-beat surfaces without dropping recency — the story content the gates read stays intact.

Cluster SYN-08 (issue #805), findings F07-5 + F07-11 + F14-16 + F14-17.
Source: docs/audits/ENGINE-AUDIT-2026-06-11.md (Part C per-finding specs).

Changes (additive, read-only)

1. store.read_log_all(tail=N) — bounded tail-read (F07-11)

Newest-first file walk that stops once it has N x slack rows, then returns the chronological [-N] window — instead of opening + pydantic-validating EVERY line of EVERY session file 2-4x/beat (strictly linear in campaign size).

  • tail=None (default) = full walk, byte-identical to before — no behavior change for any existing caller.
  • tail<=0 = degenerate empty window.

2. recap.format_recap — byte cap, not just count cap (F07-5, F14-16)

Per-entry ~400-char sentence-boundary soft cap + ~6KB total budget (defaulted params), trimming OLDEST-first so the newest beats survive (recency = the story memory the gates read).

3. _scene_recent_narration — bounded read + DEFAULT-OFF content cap (F14-17)

  • The read now uses the bounded tail (lossless window bound, always on) — returns the same last-N player-facing beats.
  • The content byte-cap is DEFAULT-OFF, opt-in via WORLDOS_RECENT_NARRATION_MAX_CHARS (legacy CLAWDND_* honored via _env). Per F14-17's story-first posture: bounding the window is lossless, but byte-capping the content drops story — ride a long-campaign duo A/B before changing the default.

Measured before/after (seeded campaign, 749 log rows / 30 sessions, ~1.4KB DM-prose beats)

Surface OLD NEW Delta
recap 8,653 B 2,617 B -69%
recent_narration read (rows parsed) 749 rows 525 rows bounded (constant vs campaign size)
recent_narration bytes (default) 5,876 B 5,876 B byte-identical (window bound is lossless)
recent_narration bytes (opt-in cap=400) 5,876 B 1,852 B -68%

Token-mass REDUCTION only; scene_context never grows.

Invariants

  • Sole-writer / read-only: no save_campaign, no updated_at bump, no append_log added to any read path (the G3 #1 blocker: is_live_view freeze/lockout from multi-campaign divergence (3/5 personas) #640 / live-pointer-flip class). Verified by grep + the existing read-only tests.
  • Additive-by-default: new params/env default to today's behavior; old snapshots round-trip (no model change).
  • Wire contracts frozen: no clawdnd-* id or CLAWDND_* env changed; new knob uses the WORLDOS_ prefix with legacy fallback.

Tests

TDD (failing first). New: 6 read_log_all(tail=…) equivalence/short-circuit/read-only tests (test_store.py), 4 recap byte-budget/per-entry-cap/byte-identical tests (test_recap.py), 3 recent_narration default-off/opt-in tests (test_beat_roundtrip.py).

  • Touched suites: 66 passed (test_store + test_recap + test_beat_roundtrip).
  • Full engine suite: 2172 passed.
  • Tier-0 qa/fast_gate.sh: PASS (188 passed).

Scope note

This PR fixes the bounded tail-read + byte-cap half of SYN-08 (F07-5/F07-11/F14-16/F14-17) as scoped. F13-6 (durable npc_relationships top-24 cap) — the other SYN-08 member — is a separate derivation cap on scene_context.durable and is left for a follow-up. SYN-09 (echo-back returns: log_event/remember/forget/update_character) is a sibling cluster, not in this change.

Closes #805

…cent_narration byte caps (SYN-08)

The per-beat lean spine re-parsed the FULL append-only campaign history every
beat and emitted count-not-byte-capped payloads, so token mass crept invisibly
as campaigns aged (audit projected 30-60KB/beat at 30+ sessions). This bounds the
WINDOW without dropping recency — story content the gates read is preserved.

- store.read_log_all(tail=N): bounded newest-first walk that short-circuits the
  whole-history re-parse (F07-11). tail=None stays the full walk, byte-identical.
  On a 749-row seeded campaign the every-beat recent_narration read drops from
  749 -> 525 rows parsed; the bound is constant regardless of campaign size.
- recap.format_recap: per-entry ~400-char sentence-boundary soft cap + ~6KB total
  budget (defaulted), trimming OLDEST-first so the newest beats survive (F07-5,
  F14-16). NO LLM summarization — trims, never paraphrases. Short beats stay
  byte-identical. Seeded recap: 8,653B -> 2,617B (69% smaller).
- _scene_recent_narration: reads via the bounded tail (lossless window bound,
  always on); the content byte-cap is DEFAULT-OFF, opt-in via
  WORLDOS_RECENT_NARRATION_MAX_CHARS (legacy CLAWDND_* honored) per F14-17's
  story-first posture (ride a long-campaign duo A/B before changing the default).
  With the opt-in cap=400 the seeded tail drops 5,876B -> 1,852B (68% smaller).

Read-only invariant preserved (no save_campaign / updated_at bump). Additive:
new params/env default to today's behavior; old snapshots round-trip. Preserves
the #847 combat-filter in recap.py.

Closes #805
Refs F07-5, F07-11, F13-6, F14-16, F14-17, SYN-08
Source: docs/audits/ENGINE-AUDIT-2026-06-11.md
@100yenadmin 100yenadmin added this to the v1.0.5 milestone Jun 14, 2026
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@100yenadmin, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 22 minutes and 6 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 101c99a4-6130-4f79-8785-45d4f69f6113

📥 Commits

Reviewing files that changed from the base of the PR and between 6b09160 and 72a3635.

📒 Files selected for processing (6)
  • servers/engine/recap.py
  • servers/engine/server.py
  • servers/engine/store.py
  • servers/engine/tests/test_beat_roundtrip.py
  • servers/engine/tests/test_recap.py
  • servers/engine/tests/test_store.py

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72a363501e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread servers/engine/server.py
# raw tail is heavily interleaved with bookkeeping. read_log_all(tail=…) returns
# exactly the last K RAW rows the full walk would, so this stays lossless.
raw_tail = limit * _RECENT_RAW_SLACK
entries = read_log_all(c.id, getattr(c, "session_ids", None), tail=raw_tail)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve player-facing tail through bookkeeping bursts

When more than limit * _RECENT_RAW_SLACK raw non-facing rows sit after or between recent narration/dialogue entries (for example the default lean tail of 8 followed by >64 combat/roll/system log rows during a long combat), this bounded call returns only that raw suffix; the filter below then cannot see the older player-facing beats, so scene_context(..., recent_narration=8) returns fewer or no story beats where the previous full read returned the last 8. The extra slack inside read_log_all affects how many rows are parsed before stopping, but it still returns only raw_tail rows to this filter, so the window is not actually lossless in bookkeeping-heavy sessions.

Useful? React with 👍 / 👎.

@100yenadmin
100yenadmin merged commit 7718546 into main Jun 14, 2026
16 checks passed
@100yenadmin
100yenadmin deleted the fix/syn08-bounded-history-surfaces branch June 14, 2026 08:16
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.

[engine-audit][P2-cluster] Token-mass bounded surfaces

1 participant