Skip to content

feat(agent-harness): synchronize durable snapshots and legacy history - #5701

Open
iscekic wants to merge 2 commits into
shared-agent-harness-3bb0-s14from
shared-agent-harness-3bb0-s15
Open

feat(agent-harness): synchronize durable snapshots and legacy history#5701
iscekic wants to merge 2 commits into
shared-agent-harness-3bb0-s14from
shared-agent-harness-3bb0-s15

Conversation

@iscekic

@iscekic iscekic commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

No new behavior — this change prepares chat synchronization without enabling it for users.


createSynchronization checks current QuickChatAuthority before and after a bounded import batch, then returns snapshots, history, or exclusive event replay. Snapshots keep settings, active and queued runs, unresolved interactions, device actions, and the event cursor together. The existing cursor_expired result still requires a replacement snapshot; production transport and alarm integration remain outside this level.

Files
  • services/agent-harness/src/sync.ts — Source; added (A), 44 lines. Wraps snapshot, paged history, and event replay with authorization and request-time import. Preserves snapshot transactions and replay limits; exposes the synchronization store and drain operations.

createLegacyCoordinator introduces LegacyAdapter and HistoryProgressSchema for scope-checked imports and text projection, with fresh authorization before and after delivery. Imports atomically deduplicate text by universally unique identifier (UUID); completed harness text creates projection work with permanent keys in the same transaction. Projection batches share one 30-second deadline, retain 60-second retries, and fence late replies; callers must use the returned store for atomic writes.

Files
  • services/agent-harness/src/legacy.ts — Source; added (A), 269 lines. Normalizes legacy messages as non-executable text, rejects conflicting UUID content, and validates scope and generation before returning durable receipts. Limits drains to 50 deliveries and projects only completed, nonempty text. Prearms recovery before primary waits and checks acknowledgment identity and revision. Bounds raw authorization before local binding, so timed-out replies cannot recreate deleted state. Allows smaller positive timeout overrides without requiring adapter cancellation; retries retain permanent keys.

HistoryProgress adds explicit pending or drained status through drainLegacyHistoryWithProgress and createQuickChatRuntime.hasPending. The primary query checks authority and all unacknowledged legacy rows, including leased or locked rows, so an empty claim batch cannot prove completion. drainLegacyHistory keeps its existing delivery-only signature; completion describes the final primary snapshot, not a guarantee against later commits.

Files
  • packages/db/src/quick-chat-runtime.ts — Source; modified (M), 18 changed lines. Adds a primary-snapshot backlog query and exports it through the runtime; invalid authority raises QuickChatAuthorityError.
  • apps/web/src/lib/agent-harness/history.ts — Source; modified (M), 17 changed lines. Adds the scoped progress wrapper without changing the existing request or cron delivery contracts.
  • apps/web/src/lib/agent-harness/history.test.ts — Test; modified (M), 191 changed lines. Adds PostgreSQL backlog and progress coverage, including lease and lock behavior.

SchedulerAdapter.drainLegacy and SchedulerStateSchema.initialHistory gate inference on validated drained progress, using persisted continuation, 30-second attempts, and one-second retries. buildHistory gains legacyThrough, applies the frozen boundary before the 200-row limit, and excludes later queued harness input. Older executable checkpoints retain their admission boundary; callers without an adapter stay compatible, but removing an adapter cannot bypass persisted pending work.

Files
  • services/agent-harness/src/scheduler.ts — Source; modified (M), 159 changed lines. Persists pending/ready checkpoints and retry wake times, fences stale drains, and preserves Stop without starting model reservations during import. Permanent authorization failures fail the selected run.
  • services/agent-harness/src/model-step.ts — Source; modified (M), 42 changed lines. Replaces paged history scans with a sequence-bounded query before timestamp ordering and trimming. Later imports cannot displace frozen history after restart; legacy text remains untrusted data.
  • services/agent-harness/src/sync.test.ts — Test; added (A), 1,506 lines. Adds SQLite coverage for snapshots, replay, authority, durable import, projections, frozen history, Stop, restart, and timeout recovery.

Tests: 2 files — history.test.ts updated; sync.test.ts added, with 43 SQLite synchronization cases and seven timeout/recovery cases.
Generated: 0 files changed.


Verification

No manual tests ran because this level does not activate production integration or start a product stack. Full backend, browser, iOS, and Android verification remains pending at the completed stack tip.

Visual Changes

Visual Changes: N/A

Reviewer Notes

  • Scope: shared-agent-harness-3bb0-s14...shared-agent-harness-3bb0-s15 only; two commits cover the backlog prerequisite and synchronization coordination.
  • Repository: Kilo-Org/cloud. Worktree: /Users/igor/Projects/.worktrees/shared-agent-harness-3bb0.
  • Inventory: 6 Source files, 2 Test files, and 0 Generated files; 2,219 insertions and 27 deletions.
  • Recorded repair evidence: the focused SQLite suite, oxlint, oxfmt, and whitespace checks passed all four scoped commands.
  • The SQLite tests use injected primary faults; they do not prove PostgreSQL transactions.
  • PostgreSQL lease and lock tests have not run locally and must run in continuous integration (CI).
  • Repository-wide type/build checks, live behavior, and current-head CI remain unverified.

Human steps

No setup is required to merge this level; it adds no secret, configuration value, or database migration.

  • after merge — Before activation, provision the Worker and scoped server configuration.

Notes

E2E: bot-e2e — full backend, browser, iOS, and Android verification remains pending at the completed stack tip.

Stacked PRs — merge bottom to top. Each level shows only its own diff.

Runtime verification (E2E, user advocacy, simplify) runs on the tip PR over every level.
Every level keeps its own checks, its own bot review, and its own threads; each one is answered on its own PR.
Each level is its own deliverable: it builds and passes its own checks alone.
A finding on a level is repaired on that level, then carried upward with stack.sh forward.

  1. shared-agent-harness-3bb0chore(agent-harness): register workspaces and enforce CI boundaries #5632
  2. shared-agent-harness-3bb0-s2feat(agent-harness): define portable domain and snapshots #5637
  3. shared-agent-harness-3bb0-s3feat(agent-harness): define commands tools and permission policy #5639
  4. shared-agent-harness-3bb0-s4feat(agent-harness): share client state and cursor recovery #5643
  5. shared-agent-harness-3bb0-s5feat(agent-harness): persist command intents and execution receipts #5647
  6. shared-agent-harness-3bb0-s6feat(db): add harness ingress grants and retirement fences #5655
  7. shared-agent-harness-3bb0-s7feat(agent-harness): deliver legacy history and project durable text #5659
  8. shared-agent-harness-3bb0-s8feat(agent-harness): authorize durable grants and registered clients #5662
  9. shared-agent-harness-3bb0-s9feat(agent-harness): fence retirement and retry payload cleanup #5667
  10. shared-agent-harness-3bb0-s10feat(agent-harness): persist authoritative state in SQLite #5675
  11. shared-agent-harness-3bb0-s11feat(agent-harness): admit durable runs and revisioned commands #5678
  12. shared-agent-harness-3bb0-s12feat(agent-harness): recover queued runs and stream checkpointed steps #5688
  13. shared-agent-harness-3bb0-s13feat(agent-harness): resolve interactions and dispatch tools sequentially #5693
  14. shared-agent-harness-3bb0-s14feat(agent-harness): fence designated client tool execution #5697
  15. shared-agent-harness-3bb0-s15feat(agent-harness): synchronize durable snapshots and legacy history #5701 ← this PR
  16. shared-agent-harness-3bb0-s16feat(agent-harness): reuse authorized invitations with durable replay #5704
  17. shared-agent-harness-3bb0-s17feat(integrations): bound repository transport for harness reads #5710
  18. shared-agent-harness-3bb0-s18feat(integrations): expose bounded authorized repository reads #5714
  19. shared-agent-harness-3bb0-s19feat(agent-harness): expose named authorized resource reads #5718 (tip)

async function drainLegacy(signal: AbortSignal = AbortSignal.timeout(30_000)) {
try {
signal.throwIfAborted();
const authority = await authorize('drain');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Drain authorization is not bound by the drain deadline

authorize('drain') (and importLegacy's authorize('import')) omit timeoutMs, so withTimeout never wraps the raw request. signal.throwIfAborted() runs only before the first authorize and after adapter.drain. A hung authorize ignores the AbortSignal; when it later resolves, bindExistingConversation still runs and can recreate deleted local state. drainProjections already passes remaining() into authorize for this reason. Pass the remaining drain deadline into these authorize calls the same way.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

const initial = schedulerRecord(db, selected.id).data.initialHistory;
if (initial?.status === 'ready') return true;
const drainLegacy = adapter.drainLegacy;
if (!drainLegacy) return initial === null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Missing drainLegacy adapter busy-loops the Durable Object alarm

Once initialHistory is pending, this return skips the lease/backoff write that sets retryAt to now() + 1_000. maintainAlarm then setAlarm(ingress.retryAt). After that timestamp, retryAt is in the past, so each alarm re-arms immediately and never persists a new delay. Stuck-pending is intended when the adapter is removed; a hot alarm loop is not. Persist a future retryAt (or clamp a past deadline) before returning.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
services/agent-harness/src/legacy.ts 177 Drain authorization is not bound by the drain deadline
services/agent-harness/src/scheduler.ts 508 Missing drainLegacy adapter busy-loops the Durable Object alarm
Files Reviewed (8 files)
  • services/agent-harness/src/sync.ts - 0 issues
  • services/agent-harness/src/legacy.ts - 1 issue
  • services/agent-harness/src/scheduler.ts - 1 issue
  • services/agent-harness/src/model-step.ts - 0 issues
  • packages/db/src/quick-chat-runtime.ts - 0 issues
  • apps/web/src/lib/agent-harness/history.ts - 0 issues
  • apps/web/src/lib/agent-harness/history.test.ts - 0 issues
  • services/agent-harness/src/sync.test.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by grok-4.6 · Input: 307.8K · Output: 27.6K · Cached: 797.4K

Review guidance: REVIEW.md from base branch shared-agent-harness-3bb0-s14

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