Skip to content

fix(cron): warn on context-window approach + expose session size (#3693)#4572

Merged
houko merged 3 commits into
mainfrom
fix/cron-context-guard
May 4, 2026
Merged

fix(cron): warn on context-window approach + expose session size (#3693)#4572
houko merged 3 commits into
mainfrom
fix/cron-context-guard

Conversation

@houko

@houko houko commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add tracing::warn! when a Persistent cron session passes cron_session_warn_fraction (default 0.8) of its effective token budget — first signal of context pressure is now a structured log line, not a hard provider 400.
  • Enrich GET /api/cron/jobs/{id} and GET /api/cron/jobs/{id}/status with session_message_count and session_token_count so dashboards / operators can graph growth trends.
  • New docs/architecture/cron-session-sizing.md documents the existing prune knobs (cron_session_max_tokens, cron_session_max_messages), the new warn knobs, the API observability fields, and a "pick a strategy" matrix.

Why

cron_session_max_tokens / cron_session_max_messages (since #2989) already prune the persistent (agent, "cron") session before each fire. The umbrella issue #3693 flagged that operators still had no way to see growth coming: pruning prevents the 400, but only after the cap has been reached, and there is nothing to graph in the meantime. This PR fills that gap.

New KernelConfig fields

Field Type Default Effect
cron_session_warn_fraction Option<f64> Some(0.8) Fraction of the effective token budget at which to emit tracing::warn!. Out-of-range / non-finite values silently disable the warn.
cron_session_warn_total_tokens Option<u64> Some(200_000) Fallback ceiling used by cron_session_warn_fraction when cron_session_max_tokens is unset. Set to None to require an explicit cap.

Both have #[serde(default = ...)] so older configs keep working.

New API response fields

On GET /api/cron/jobs/{id} and GET /api/cron/jobs/{id}/status:

Field Type Meaning
session_message_count u64 Messages currently in the persistent (agent, "cron") session.
session_token_count u64 Estimated tokens for those messages (system prompt and tools excluded — same accounting as the prune path).

Both default to 0 when the cron session does not exist yet (job has never fired, or only session_mode = "new" fires). Strictly additive; existing clients are unaffected.

Out of scope (follow-up under #3693)

Gentle summarize-and-trim compaction (replace the dropped tail with a short LLM-generated summary instead of pure drop-from-front). That requires a synthetic LLM round-trip per fire and is a larger change. Tracked under the same umbrella issue.

Implementation notes

  • Pure-function resolve_cron_warn_threshold(max_tokens, warn_fallback, fraction) covers all the boundary conditions (NaN / Inf / negative / zero / >1.0 / both budgets unset). Unit-tested in crates/librefang-kernel/src/kernel/tests.rs.
  • The prune block now only writes the session back to disk when at least one drain happened (was: write on every fire that entered the branch). Avoids touching SQLite on warn-only fires.
  • Estimation reuses librefang_runtime::compactor::estimate_token_count (CJK-aware char-weighted heuristic — same as the prune loop). No new tokenizer dependency.

Verification

  • cargo is forbidden from this worktree (multi-worktree contention rule from CLAUDE.md); CI runs the full workspace build, clippy, and librefang-api integration tests including the two new tests.
  • New tests:
    • resolve_cron_warn_threshold_* (8 unit tests in kernel/tests.rs) — covers NaN / Inf / out-of-range fraction / zero budget / partial-token ceiling.
    • cron_job_get_response_has_session_size_fields — boots a real kernel via MockKernelBuilder, registers a cron job, seeds three messages into the cron session, asserts both fields present and non-zero on /api/cron/jobs/{id} and /api/cron/jobs/{id}/status.
    • cron_job_get_response_session_fields_default_zero_when_no_session — asserts both fields are 0 (not absent) when no cron session exists yet.
  • OpenAPI drift hook (pre-push) re-generated as needed; if CI surfaces any spec churn it will be in the auto-bump PR loop the repo already runs.

Refs #3693

…PI (#3693)

cron_session_max_tokens / cron_session_max_messages already prune
Persistent cron sessions before each fire (since #2989). This PR
closes the operator-visibility gap that the umbrella issue #3693
flagged:

- tracing::warn! when the post-prune session estimate exceeds
  cron_session_warn_fraction (new KernelConfig field, default 0.8)
  of the provider's context window. First indication of pressure
  is now a structured log line, not a hard provider 400.
- Cron job status / detail responses now include
  session_message_count and session_token_count so dashboard /
  operators can graph growth trends.
- docs/architecture/cron-session-sizing.md documents both pruning
  caps + the new warn fraction + the API observability fields.

Two new KernelConfig fields:
  cron_session_warn_fraction: Option<f64>          (default Some(0.8))
  cron_session_warn_total_tokens: Option<u64>      (default Some(200_000))
The warn-fraction is applied against cron_session_max_tokens when
set, otherwise against cron_session_warn_total_tokens as a fallback
ceiling so jobs that have not opted into pruning still get warnings.

Two new fields on the cron job detail / status JSON response:
  session_message_count: u64
  session_token_count:   u64
Both default to 0 when no Persistent cron session exists yet.
Strictly additive — existing clients keep working.

Side effect of the warn wiring: the prune block now only writes
the session back to disk when messages were actually drained
(was: write on every fire that entered the prune branch). This
avoids touching SQLite on warn-only fires.

Gentle summarize-and-trim compaction (issue gap #4) is tracked
separately under #3693 — it requires a synthetic LLM round-trip
and is a larger change.

Closes part of #3693
@github-actions github-actions Bot added area/docs Documentation and guides area/kernel Core kernel (scheduling, RBAC, workflows) labels May 4, 2026
@github-actions github-actions Bot added the size/L 250-999 lines changed label May 4, 2026
@houko houko closed this May 4, 2026
@houko houko reopened this May 4, 2026
@houko
houko enabled auto-merge (squash) May 4, 2026 12:47
@github-actions github-actions Bot added the has-conflicts PR has merge conflicts that need resolution label May 4, 2026
@houko
houko merged commit 584846d into main May 4, 2026
14 of 15 checks passed
@houko
houko deleted the fix/cron-context-guard branch May 4, 2026 13:03
@github-actions github-actions Bot added ready-for-review PR is ready for maintainer review and removed has-conflicts PR has merge conflicts that need resolution labels May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation and guides area/kernel Core kernel (scheduling, RBAC, workflows) ready-for-review PR is ready for maintainer review size/L 250-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant