fix(engine): bound every-beat history surfaces — tail-read + recap/recent_narration byte caps (SYN-08) - #852
Conversation
…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
|
Warning Review limit reached
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
Comment |
There was a problem hiding this comment.
💡 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".
| # 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) |
There was a problem hiding this comment.
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 👍 / 👎.
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_recapreproduced 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 slackrows, 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).
_is_combat_bookkeeping) untouched.3.
_scene_recent_narration— bounded read + DEFAULT-OFF content cap (F14-17)WORLDOS_RECENT_NARRATION_MAX_CHARS(legacyCLAWDND_*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)
recaprecent_narrationread (rows parsed)recent_narrationbytes (default)recent_narrationbytes (opt-in cap=400)Token-mass REDUCTION only;
scene_contextnever grows.Invariants
save_campaign, noupdated_atbump, noappend_logadded 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.clawdnd-*id orCLAWDND_*env changed; new knob uses theWORLDOS_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).test_store+test_recap+test_beat_roundtrip).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_relationshipstop-24 cap) — the other SYN-08 member — is a separate derivation cap onscene_context.durableand 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