Commit Graph

2 Commits

Author SHA1 Message Date
teamchong c1d111a083 feat(transform): per-bucket char attribution telemetry (Task #18 Phase 1)
Adds the data collection half of the adaptive chars-per-token plan
without touching the gate. Every isCompressionProfitable call site
now credits its chars to a named bucket on TransformInfo, and those
counts flow through toTrackEvent into the persisted JSONL.

transform.ts:
- New BucketName union: 'static_slab' | 'reminder' |
  'tool_result_{structured,log,prose}' | 'history'
- bumpBucket() helper lazily allocates info.bucketChars
- toolResultBucket() routes by classifyContent shape so the post-
  compaction text we credit matches the shape the gate evaluated
- Bump sites: static slab (combinedRaw), reminder (reminderRaw),
  tool_result single-string (innerRaw), tool_result multi-block
  per TextBlock (innerTextRaw), history (collapsedChars)
- Surfaces historyTextChars separately so history's no-collapse
  rejections still contribute a denominator

tracker.ts:
- TrackEvent gains bucket_chars?: Partial<Record<BucketName, number>>
  and history_text_chars?: number
- toTrackEvent copies them when populated, drops empty bucket maps
  so happy-path events stay small

tests/tracker.test.ts:
- Asserts snake_case field names and nested shape land correctly
- Asserts absent buckets and pass-through events stay clean

No gate logic changed. CHARS_PER_TOKEN / SLAB_CHARS_PER_TOKEN /
HISTORY_CHARS_PER_TOKEN still drive isCompressionProfitable. Phase 2
will fit per-bucket slopes from the captured events and wire a
bucket-aware override.

docs/ADAPTIVE_CPT_PLAN.md: full plan + empirical basis (slab-only
regression on 87 events recovered cpt≈1.50 vs the baked 2.5
constant — 40% on non-slab compressions ship today).

Verified: tsc --noEmit clean; 290/290 tests pass (+2 new);
build emits dist/node.js.
2026-05-20 21:21:34 -04:00
teamchong b49764e64c docs: add TRANSFORM_INFO.md (compression strategy reference)
A single canonical document explaining how pixelpipe compresses Claude
Code requests end-to-end. Covers, in order:

  1. Why this proxy exists
  2. The static / dynamic split (DYNAMIC_BLOCK_TAGS, KNOWN_STATIC_TAGS)
  3. What the final request looks like (placement='user' layout)
  4. The cache_control budget (the one invariant that matters: ttl='1h')
  5. Per-message compressions (reminders + tool_results, no cache_control)
  6. Determinism and fingerprints (systemSha8, claudeMdSha8, firstUserSha8)
  7. The unknown-tag canary
  8. The savings math (effectiveCost / baselineCost from src/dashboard.ts)
  9. What deliberately did NOT get built
 10. Wiring map

Intended as the file a new contributor reads after CLAUDE.md but before
touching src/core/transform.ts. HANDOFF.md is session-state and rotates;
README.md is install/run; CLAUDE.md is rules; this is the architecture
reference that explains WHY the code looks the way it does.

Authored by the doc-writer teammate, KNOWN_STATIC_TAGS section
post-edited to reflect that e219b37 landed (not "future commit"). The
"source of truth" line in section 8 points at src/dashboard.ts directly
now that the Python reference has been removed (be372a0).

334 lines. No code changes; gates not re-run since this is doc-only.
2026-05-18 17:57:22 -04:00