Stop shipping megabytes of skill prompts in every snapshot refresh

The whole app turned to molasses because snapshot.load() shipped the
entire skill_runtime_snapshots table - unbounded, compiled_prompt
included - over IPC on every refresh. A real profile carried 142 rows
totaling 144MB (1.1MB each from the pre-cap CEO compile-all era), so
every coalesced refresh serialized ~144MB of JSON. The forced-humanizer
rework made it worse by adding a row per run for every agent.

- Drop skillRuntimeSnapshots, skillRevisions, skillTests, and
  skillBindings from the renderer snapshot: no renderer surface reads
  them; skill internals are main-process state. Only the lightweight
  skillDefinitions catalogue still ships.
- Project provider_requests without raw_response_json (4.1MB across the
  newest 100 rows; RunsPage only reads responseText)
- Migration 051 purges the bug-era compiled prompts from existing DBs
- resolveRuntimeSnapshot is now idempotent per run: a run re-queued by
  startup recovery reuses its frozen snapshot instead of dying on the
  uniqueness guard and burning real retry budget
- Regression test pins the contract: a 2MB database row must not add
  bytes to the snapshot (payload asserted under 400KB)
This commit is contained in:
ZacharyZhang-NY committed 2026-07-08 23:20:56 -04:00
1 parent 4ab20e679c
commit 1f89ea7089
10 files changed
+117 -34

No files matched your search

+5 -1
View File
@@ -10,6 +10,11 @@ export const PROVIDER_REQUEST_STATE = {
export type ProviderRequestState = typeof PROVIDER_REQUEST_STATE[keyof typeof PROVIDER_REQUEST_STATE];
/**
* Snapshot projection of a provider request. The raw provider response body
* stays in the database (task_runs diagnostics read it there) — shipping it
* to the renderer ballooned every snapshot by megabytes for nothing.
*/
export interface ProviderRequestRecord {
id: string;
orgId: string;
@@ -19,7 +24,6 @@ export interface ProviderRequestRecord {
model: string;
promptSha256: string;
responseText: string | null;
rawResponseJson: string | null;
state: ProviderRequestState;
errorMessage: string | null;
inputCharCount: number;
+4 -8
View File
@@ -34,11 +34,7 @@ import type {
ProviderCatalogEntry,
ProviderRecord,
ScheduledPostRecord,
SkillBindingRecord,
SkillDefinitionRecord,
SkillRevisionRecord,
SkillRuntimeSnapshotRecord,
SkillTestRecord,
SprintRecord,
TaskRecord,
TaskRunRecord,
@@ -103,11 +99,11 @@ export interface AppSnapshot {
providerRequests: ProviderRequestRecord[];
providerRateLimits: ProviderRateLimitStateRecord[];
providerRateLimitObservations: ProviderRateLimitObservationRecord[];
// The snapshot is the renderer's working set and rides IPC on every
// refresh. Skill internals (revisions, bindings, per-run runtime
// snapshots with compiled prompts) are main-process concerns and stay out
// of it; only the lightweight catalogue listing ships.
skillDefinitions: SkillDefinitionRecord[];
skillRevisions: SkillRevisionRecord[];
skillBindings: SkillBindingRecord[];
skillRuntimeSnapshots: SkillRuntimeSnapshotRecord[];
skillTests: SkillTestRecord[];
socialAccounts: SocialAccountRecord[];
socialAccountGrants: SocialAccountGrantRecord[];
browserSessionContainers: BrowserSessionContainerRecord[];