Skip to content

feat(context): add limit + compact query params to GET /context#2

Merged
todie merged 2 commits intomainfrom
feat/context-limit-compact
Apr 7, 2026
Merged

feat(context): add limit + compact query params to GET /context#2
todie merged 2 commits intomainfrom
feat/context-limit-compact

Conversation

@todie
Copy link
Copy Markdown
Owner

@todie todie commented Apr 7, 2026

Stacked on top of #1. Review #1 first — the merge base for this PR is optimize/session-start-tokens, not main.

Summary

PR #1 worked around the ~8 KB /context payload client-side with awk. This PR moves the knobs into the server, so the rendering choice lives next to the data and works correctly for non-shell clients too.

New query parameters on GET /context:

param effect
limit=N Cap the observations section at N bullets. Zero or absent falls back to MaxContextResults (20). Sessions (5) and prompts (10) are unchanged.
compact=1 Render observation bullets as - [type] **title**, dropping the : <300 chars of body> preview. Parsed with strconv.ParseBool so 1, true, yes etc. all work.

Backward compatibility

  • FormatContext(project, scope) stays as a thin wrapper over the new FormatContextWithOptions(project, scope, opts) with a zero-value ContextOptions, so all ~15 existing callers and test fixtures are untouched.
  • A GET /context request with no new params produces byte-for-byte identical output to the pre-change binary — verified empirically, see benchmark.

Hook update

plugin/claude-code/scripts/session-start.sh now passes ?limit=${ENGRAM_CONTEXT_LIMIT}&compact=1 to the server. The awk post-processor from #1 stays in place as a belt-and-suspenders fallback for users whose binary is older than their plugin — on a new server it's a near no-op; on an old server it still compacts things client-side.

Tests

  • store: new TestFormatContextWithOptions covers:
    • default equivalence with FormatContext
    • Compact: true dropping previews
    • Limit: N capping observations
    • Limit + Compact composition
    • Limit <= 0 falling back to MaxContextResults
  • server e2e: existing /context test now also hits ?limit=1&compact=1 and asserts the compact payload is strictly smaller and contains no `: ` segments.
  • Full suite: go test ./... → 748 passed in 10 packages.

Benchmark (ctodie project, 200+ observations, loopback)

binary request payload latency
old /context 7949 B ~1.0 ms
new /context (default params) 7949 B ~0.9 ms
new /context?limit=8&compact=1 728 B ~0.8 ms

Byte-for-byte identity on the default-params case confirms backward compat. The -91% on the compact path is the real win.

End-to-end SessionStart hook injection

payload
original (before #1) ~9800 B
after #1 (shell awk only) ~1900 B (−81%)
after #1 + #2 (server-side) ~1151 B (−88%)

PR #1 alone already solved the token cost for users who can't upgrade their binary; this PR makes the fast path the default and keeps the shell fallback for mixed-version deployments.

Test plan

  • go test ./... — 748 passed
  • Build engram from this branch, run against real ctodie DB, verify default params return byte-for-byte match with old binary
  • Verify ?limit=8&compact=1 returns 728 B on same DB
  • End-to-end SessionStart hook: 1151 B output, well-formed, tools list present
  • Old binary silently ignores new params and returns full context (shell awk fallback then compacts it) — mixed-version deployment works
  • Reviewer to sanity-check on a fresh project

Files changed (5)

  • internal/store/store.go — add ContextOptions, FormatContextWithOptions; FormatContext becomes a wrapper
  • internal/store/store_test.go — new TestFormatContextWithOptions
  • internal/server/server.go — parse limit + compact query params
  • internal/server/server_e2e_test.go — extend existing /context test
  • plugin/claude-code/scripts/session-start.sh — pass new params; keep awk fallback

🤖 Generated with Claude Code

@todie todie changed the base branch from optimize/session-start-tokens to main April 7, 2026 05:52
todie and others added 2 commits April 7, 2026 01:53
Stacked on top of #1 (perf: shrink SessionStart hook injection).

The /context endpoint always rendered every recent observation with up to
300 chars of raw content inlined per bullet, returning ~8 KB on any busy
project. PR #1 worked around this client-side with awk; this commit moves
the knobs into the server so the rendering choice lives next to the data.

New query parameters
--------------------
  limit=N    Cap the observations section at N bullets. Zero or absent
             falls back to the store-wide MaxContextResults default.
             Sessions and prompts are not affected (still 5 and 10).

  compact=1  Render observation bullets as `- [type] **title**` only,
             dropping the `: <300 chars of body>` preview. Parsed with
             strconv.ParseBool so `1`, `true`, `yes`, etc. all work.

Backward compatibility
----------------------
FormatContext(project, scope) stays as a thin wrapper over the new
FormatContextWithOptions(project, scope, opts) method with a zero-value
ContextOptions, so all ~15 existing callers and test fixtures are
untouched. A GET /context request with no new params produces byte-for-
byte identical output to the pre-change binary (verified empirically,
see benchmark below).

Hook update
-----------
plugin/claude-code/scripts/session-start.sh now passes
`?limit=${ENGRAM_CONTEXT_LIMIT}&compact=1` to the server. The awk
post-processor from #1 stays in place as a belt-and-suspenders fallback
for users whose binary is older than their plugin — on a new server it's
a near no-op; on an old server it still compacts things client-side.

Tests
-----
- internal/store: new TestFormatContextWithOptions covers default
  equivalence with FormatContext, Compact dropping previews, Limit
  capping observations, Limit+Compact composition, and Limit<=0 falling
  back to the default.
- internal/server: e2e test now hits /context with and without
  &limit=1&compact=1 and asserts the compact payload is strictly smaller
  and contains no `: <body>` segments.

Full suite: 748 passed in 10 packages.

Benchmark (ctodie project, 200+ observations, loopback)
-------------------------------------------------------
  old binary,  /context                        7949 B   ~1.0 ms
  new binary,  /context (default params)       7949 B   ~0.9 ms  ← identical bytes
  new binary,  /context?limit=8&compact=1       728 B   ~0.8 ms  ← −91%

End-to-end SessionStart hook injection
--------------------------------------
  original (before #1):                       ~9800 B
  after #1 (shell awk only):                  ~1900 B   −81%
  after #1 + #2 (server-side):                ~1151 B   −88%

PR #1 alone already solved the token cost for users who can't upgrade
their binary; this PR makes the fast path the default and keeps the
shell fallback for mixed-version deployments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Go's strconv.ParseBool only accepts 1/0/t/f/T/F/true/false/True/False/
TRUE/FALSE. The original comment claimed 'yes' was also supported; it
isn't — compact=yes silently falls back to Compact=false. Verified
against the live binary: /context?limit=8&compact=yes returned 8 full
(non-compact) observations.

Drive-by doc fix; no behaviour change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@todie todie force-pushed the feat/context-limit-compact branch from 49dc372 to aca3e22 Compare April 7, 2026 05:53
@todie todie merged commit d22cb5c into main Apr 7, 2026
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