Skip to content

feat(jev): the context engine — budget-greedy state assembly with paper-content taste cards - #76

Merged
aarontrowbridge merged 2 commits into
mainfrom
feat/context-engine
Oct 8, 2026
Merged

aarontrowbridge merged 2 commits into
mainfrom
feat/context-engine

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

Implements #75.

What

The corpus had outgrown the Jev state (production receipts show the median digest call at 4055 of 4096 bytes) while the papers' own words never reached the model. This PR replaces the fixed-15 counts with one mechanism: a budget-greedy, decay-ranked state assembly — directives whole, then per-paper taste cards (your why + the paper's abstract + its conclusions/outlook) and terms fill the configured budget by recency-weighted priority. Small corpus → everything rides; growing corpus → recent work claims proportionally more of the window.

Surface

  • paper_content.py: tolerant section extraction (Conclusion(s)/Summary/Outlook/Discussion/Perspectives, numbered or not) over the library ladder (.txt → PDF extraction → arXiv API abstract floor). Fail-open everywhere; digest-time reads are local-only.
  • arjev backfill: paced, idempotent fetch of every corpus paper's PDF + extracted text; arjev fetch now emits the .txt at fetch time.
  • Candidate-content arm: jev-first re-scores the top finalists with their fetched conclusions (two-pass, bounded, cached in the state dir — the PDF library stays keep-only); lexical-first enriches the pools before their single call.
  • arjev calibrate --replay-arms: the live A/B (fixed-15 · greedy@4KB · greedy@8KB × content on/off) over labeled candidates, Brier per arm with honest n; arm receipts land in a separate file, never the production join.
  • Config/checklist/inspect slots: state_policy, taste_budget_bytes, candidate_content. README rewritten around the context-management story (generic capacity math + the measured production block).

Invariants kept

  • Defaults preserve current behavior exactly (fixed-15, no candidate content) — every enlargement is opt-in until calibrate --replay-arms shows a measured win (the vendor documents context rot; evidence decides).
  • Fingerprint determinism, staged-gate audit, fail-open-on-every-seam, frozen receipt schema — all unchanged and tested.
  • New dep: pypdf (pure-Python, PDF text extraction at the fetch/backfill seams).

Verification

  • Full suite: 207 passed (34 new tests across test_paper_content.py + test_context_engine.py); ruff check clean.
  • Live smoke: budget-greedy digest over the real 105-note vault + 64-PDF library + live 3-feed union — exit 0, 5 picks, fingerprint stable, isolated state dir.
  • Calibration labels are still thin (precision@5 n=80, Brier n=0), so the arms may need a few weeks of keeps before any default flip — the feature ships with arms ready either way.

…er-content taste cards

The corpus outgrew the fixed-15 state (production median: 4055 of 4096 bytes),
while the papers' own words never reached the model — bodies were compressed
to token frequencies. One mechanism replaces the fixed counts (issue #75):

- budget-greedy assembly: directives whole, then taste cards (why + abstract +
  conclusions excerpts, decay-weighted) and terms fill the taste budget
  greedily — small corpus fits everything, growing corpus gives recent work
  proportionally more of the window
- paper_content: tolerant section extraction over the library ladder (.txt →
  PDF → API abstract floor), candidate conclusions cached in the state dir
  (the library stays keep-only), finalist re-score under the content arm
- arjev backfill: paced corpus PDF+text backfill, idempotent
- calibrate: receipt dedup (the enriched call supersedes) + --replay-arms,
  the A/B that gates every default flip (context rot is real; evidence
  decides) — defaults unchanged until a measured win
…at scale, honest numbers

Two criticals: a transport error in a candidate fetch crashed the digest
(caught in candidate_conclusions now — a dead proxy degrades to None, never
kills a digest); and the >4KB arms kept the 4096 hard cap, collapsing every
arm to the same backstopped state — the A/B would have measured nothing
(state_arms scales hard_cap with the budget; a 12KB arm joins the matrix).

Importants: the arms test is hermetic again (cache pre-populated, warm-up
paced and shared); fetch_pdf's exists-path pays extraction once so backfill
converges on pre-feature libraries; README numbers now survive contact with
the code (12KB ≈ 12% of the 32k-token window, ~$0.08/day, the 12KB arm real).

Minors: a failed finalist re-score no longer supersedes the journaled
receipt; slash-form ids slug to flat cache filenames with the real id inside
the JSON; the mode line says finalists vs candidates per ranking mode; the
paced_conclusions seam unifies politeness across digest and replay-arms;
corpus-content and library write-back disclosures land in the README.
@aarontrowbridge
aarontrowbridge marked this pull request as ready for review October 8, 2026 10:37
@aarontrowbridge
aarontrowbridge merged commit 6df17b4 into main Oct 8, 2026
1 check passed
@aarontrowbridge
aarontrowbridge deleted the feat/context-engine branch October 8, 2026 10:37
aarontrowbridge added a commit that referenced this pull request Oct 8, 2026
The context engine (#75, PR #76) is v0.4-shaped work: pyproject + __init__
to 0.4.0, README install pin and Status section updated, and the release
note in the established docs/release-notes format (defaults unchanged —
every new arm is opt-in until calibrate --replay-arms shows a win).

Closes #77
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.

1 participant