A merged PR may require knowledge-base updates. Review the change and update library/ as needed.
Source PR
- PR: #298 - feat: injected-token metering, live KPI, real ROI trend, partial net
- Author: @thenotoriousllama
- Merged into:
main
- Files changed: 35
PR description
Was stacked on #297; after #297's squash-merge this branch was rebased onto main (original commits couldn't match the squash → conflicts). Now a plain PR against main, targeted tests re-verified post-rebase.
What this does
Implements the "prove memory works" loop (ISS-010, ISS-011, ISS-022): meter the tokens Honeycomb actually injects into harness sessions, surface them as a live KPI, make the ROI page move, and tell the user when memories are injected.
Piece A — injected-token metering
New append-only memory_injections ledger (src/daemon/storage/catalog/memory-injections.ts, heal-registered, lazy-CREATE on first write — no migration): one row per injection event (source: recall | recall_fast | prime, hits, tokens, session_id, project_id). Writer recordInjection (src/daemon/runtime/telemetry/injection-log.ts) is fail-soft/never-throws, skips zero-hit events, clamps counters. Two fire-and-forget call sites: the POST /recall handler (covers heavy + fast lanes; response byte-unchanged) and buildPrimeForScope (the prime digest already computed its token estimate — now it's persisted). Deliberately NOT a column on memory_access: that table is compacted (newest 32 rows per memory), so a cumulative SUM there would shrink over time, and prime injections have no per-memory access rows at all.
Piece B — honest KPI
/api/diagnostics/kpis gains injectedTokens (SUM over the ledger, short DIAG_TTL_MS cache — it's a live counter). estimatedSavings keeps working but its JSDoc now says what it really is: a corpus-mass proxy (Σ LENGTH(content)/4), not measured injections.
Piece C — real ROI trend + partial net
fetchRoiTrendView was a hardcoded EMPTY_ROI_TREND stub; it now folds the existing roi_metrics ledger (via readRoiMetrics) into daily-bucketed, zero-filled measured-savings / modeled-savings series (labels match hive's chart color heuristics). TS-side bucketing only — DeepLake returns NULL for SUM unde
Files touched
.claude-plugin/marketplace.json (+2/-2)
.claude-plugin/plugin.json (+1/-1)
CHANGELOG.md (+4/-0)
harnesses/claude-code/.claude-plugin/plugin.json (+1/-1)
harnesses/codex/package.json (+1/-1)
harnesses/openclaw/openclaw.plugin.json (+1/-1)
harnesses/openclaw/package.json (+1/-1)
package-lock.json (+2/-2)
package.json (+1/-1)
src/daemon/runtime/dashboard/api.ts (+176/-38)
src/daemon/runtime/memories/api.ts (+23/-0)
src/daemon/runtime/memories/prime.ts (+24/-0)
src/daemon/runtime/telemetry/injection-log.ts (+127/-0)
src/daemon/storage/catalog/index.ts (+12/-0)
src/daemon/storage/catalog/memory-injections.ts (+105/-0)
src/dashboard/contracts.ts (+40/-9)
src/hooks/binary.ts (+7/-1)
src/hooks/contracts.ts (+17/-1)
src/hooks/normalize.ts (+16/-4)
src/hooks/shared/contracts.ts (+10/-0)
src/hooks/shared/index.ts (+1/-0)
src/hooks/shared/recall-renderer.ts (+12/-8)
src/hooks/shared/user-prompt-recall.ts (+17/-1)
tests/daemon/runtime/dashboard/api-roi.test.ts (+91/-6)
tests/daemon/runtime/dashboard/injected-tokens-kpi.test.ts (+126/-0)
tests/daemon/runtime/dashboard/roi-trend.test.ts (+195/-0)
tests/daemon/runtime/memories/injection-metering.test.ts (+211/-0)
tests/daemon/runtime/memories/prime.test.ts (+9/-3)
tests/daemon/runtime/telemetry/injection-log.test.ts (+150/-0)
tests/daemon/storage/catalog/memory-injections.test.ts (+109/-0)
tests/hooks/binary.test.ts (+153/-0)
tests/hooks/normalize.test.ts (+111/-0)
tests/hooks/shared/recall-renderer-timeout.test.ts (+11/-10)
tests/hooks/shared/user-prompt-recall.test.ts (+113/-1)
tests/hooks/shims-channel.test.ts (+50/-0)
Auto-generated by .github/workflows/kb-issue-on-merge.yaml on merge.
A merged PR may require knowledge-base updates. Review the change and update
library/as needed.Source PR
mainPR description
What this does
Implements the "prove memory works" loop (ISS-010, ISS-011, ISS-022): meter the tokens Honeycomb actually injects into harness sessions, surface them as a live KPI, make the ROI page move, and tell the user when memories are injected.
Piece A — injected-token metering
New append-only
memory_injectionsledger (src/daemon/storage/catalog/memory-injections.ts, heal-registered, lazy-CREATE on first write — no migration): one row per injection event(source: recall | recall_fast | prime, hits, tokens, session_id, project_id). WriterrecordInjection(src/daemon/runtime/telemetry/injection-log.ts) is fail-soft/never-throws, skips zero-hit events, clamps counters. Two fire-and-forget call sites: thePOST /recallhandler (covers heavy + fast lanes; response byte-unchanged) andbuildPrimeForScope(the prime digest already computed its token estimate — now it's persisted). Deliberately NOT a column onmemory_access: that table is compacted (newest 32 rows per memory), so a cumulative SUM there would shrink over time, and prime injections have no per-memory access rows at all.Piece B — honest KPI
/api/diagnostics/kpisgainsinjectedTokens(SUM over the ledger, shortDIAG_TTL_MScache — it's a live counter).estimatedSavingskeeps working but its JSDoc now says what it really is: a corpus-mass proxy (Σ LENGTH(content)/4), not measured injections.Piece C — real ROI trend + partial net
fetchRoiTrendViewwas a hardcodedEMPTY_ROI_TRENDstub; it now folds the existingroi_metricsledger (viareadRoiMetrics) into daily-bucketed, zero-filledmeasured-savings/modeled-savingsseries (labels match hive's chart color heuristics). TS-side bucketing only — DeepLake returns NULL for SUM undeFiles touched
.claude-plugin/marketplace.json(+2/-2).claude-plugin/plugin.json(+1/-1)CHANGELOG.md(+4/-0)harnesses/claude-code/.claude-plugin/plugin.json(+1/-1)harnesses/codex/package.json(+1/-1)harnesses/openclaw/openclaw.plugin.json(+1/-1)harnesses/openclaw/package.json(+1/-1)package-lock.json(+2/-2)package.json(+1/-1)src/daemon/runtime/dashboard/api.ts(+176/-38)src/daemon/runtime/memories/api.ts(+23/-0)src/daemon/runtime/memories/prime.ts(+24/-0)src/daemon/runtime/telemetry/injection-log.ts(+127/-0)src/daemon/storage/catalog/index.ts(+12/-0)src/daemon/storage/catalog/memory-injections.ts(+105/-0)src/dashboard/contracts.ts(+40/-9)src/hooks/binary.ts(+7/-1)src/hooks/contracts.ts(+17/-1)src/hooks/normalize.ts(+16/-4)src/hooks/shared/contracts.ts(+10/-0)src/hooks/shared/index.ts(+1/-0)src/hooks/shared/recall-renderer.ts(+12/-8)src/hooks/shared/user-prompt-recall.ts(+17/-1)tests/daemon/runtime/dashboard/api-roi.test.ts(+91/-6)tests/daemon/runtime/dashboard/injected-tokens-kpi.test.ts(+126/-0)tests/daemon/runtime/dashboard/roi-trend.test.ts(+195/-0)tests/daemon/runtime/memories/injection-metering.test.ts(+211/-0)tests/daemon/runtime/memories/prime.test.ts(+9/-3)tests/daemon/runtime/telemetry/injection-log.test.ts(+150/-0)tests/daemon/storage/catalog/memory-injections.test.ts(+109/-0)tests/hooks/binary.test.ts(+153/-0)tests/hooks/normalize.test.ts(+111/-0)tests/hooks/shared/recall-renderer-timeout.test.ts(+11/-10)tests/hooks/shared/user-prompt-recall.test.ts(+113/-1)tests/hooks/shims-channel.test.ts(+50/-0)Auto-generated by
.github/workflows/kb-issue-on-merge.yamlon merge.