Skip to content

feat(runtime): reconstruct replayable agent graph timeline - #1549

Merged
likun666661 merged 4 commits into
agent/1341-reconcile-graph-schedulefrom
agent/1529-graph-trace
Jul 28, 2026
Merged

likun666661 merged 4 commits into
agent/1341-reconcile-graph-schedulefrom
agent/1529-graph-trace

Conversation

@likun666661

@likun666661 likun666661 commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

  • add a transactionally consistent SQLite control-plane snapshot for Graph schedule updates, operator provisions, intent claims/current admissions, and supervisor wakes/attempts
  • reconstruct a typed, chronological Graph timeline by joining that metadata with root/child AgentRuns and immutable RuntimeEvents at query time
  • expose deterministic same-millisecond ordering, validated cursor pagination, explicit omission counts, and AgentGraphCoordinator.getTimeline()
  • keep immutable schedule/runtime history in the pageable stream while returning mutable admission/wake state separately in page-level currentState
  • return reference-only record facets and identities; schedule instructions, reasons, tool arguments/results, and child message payloads are not copied into the trace

Why this shape

This is the first implementation slice of #1529. It deliberately uses a query-time join over existing authorities instead of introducing a second trace authority or changing scheduler/runtime semantics.

The existing SQLite schema stores the current admission and wake state but not every overwritten transition, and reconcile passes are not durable facts yet. Mutable admission/wake snapshots therefore live outside the pageable historical stream so their state changes cannot invalidate an issued cursor. The response also reports the missing transition history through coverage.limitations:

  • admission_transition_history_not_persisted
  • supervisor_wake_transition_history_not_persisted
  • reconcile_history_not_persisted

That makes the v1 trace useful and honest while leaving an append-only transition ledger and Desktop timeline UI as follow-up slices.

Result

A completed Graph run can now reconstruct:

root turn → schedule commit → operator/child Session → intent → durable AgentRun activation → runtime records → child terminal → supervisor wake/attempt → later root turn → selected result → schedule finish

The cursor is based on durable event identity/time rather than the page index, so it remains valid when an earlier event is discovered later. sequence is documented as deterministic reconstruction order, not a cross-ledger commit sequence.

activation_started comes from the claimed child AgentRun's durable createdAt, including the live window before its first RuntimeEvent. Terminal activation remains RuntimeEvent-authoritative.

Validation

  • npm run typecheck
  • npm run build --workspace @maka/core
  • npm run build --workspace @maka/storage
  • npm run build --workspace @maka/runtime
  • node --test packages/runtime/dist/__tests__/agent-graph-timeline.test.js packages/runtime/dist/__tests__/stream-graph-projection.test.js packages/runtime/dist/__tests__/stream-graph-coordinator.test.js
  • focused Biome format/lint checks

The full runtime suite completed with 2 unrelated local macOS/Homebrew sandbox failures out of 2,551 tests: the sandbox could not load ripgrep's /opt/homebrew/opt/pcre2 dylib, and an executable-root assertion expected /usr/local. The new timeline tests pass.

Part of #1529.

中文说明

本 PR 做了什么

  • 在 SQLite 控制面增加一次事务内读取的 Graph metadata snapshot,覆盖 schedule、operator provision、intent claim/当前 admission、supervisor wake/attempt。
  • 查询时再与 root/child AgentRun 和 immutable RuntimeEvent 做 join,生成类型化、按时间排序、可分页的 Graph timeline。
  • 同毫秒事件使用确定性的全序;cursor 基于 durable event identity/time,而不是不稳定的数组下标。
  • pageable events 只承载 immutable schedule/runtime history;mutable admission/wake 状态独立放在 page-level currentState,状态变化不会让旧 cursor 失效。
  • trace 只返回 identity、关联和 record facet,不复制 instruction、reason、tool args/result 或 child message 内容。

为什么不是新建一套 trace authority

这是 #1529 的第一个实现切片。首版直接复用现有权威 stores,不增加新的 scheduler/runtime 语义,也不引入第二套事实源。

当前 SQLite 表只保留 admission/wake 的最新状态,不保留所有被覆盖的中间 transition;reconcile pass 目前也没有 durable ledger。因此 mutable current state 不进入分页历史流,同时 API 通过 coverage.limitations 明确报告这些边界,而不是假装已经拥有完整历史。

activation_started 直接来自 claim 所关联 child AgentRun 的 durable createdAt,即便还没有第一条 RuntimeEvent 也可见;terminal 仍以 RuntimeEvent 为权威。后续可以单独增加 append-only transition ledger,再接 Desktop timeline UI。

@likun666661

Copy link
Copy Markdown
Member Author

The overall direction looks right: reconstructing this as a read projection over the existing SQLite control plane and Runtime ledgers is preferable to introducing another trace authority. I found two blockers before this can satisfy the stable/replayable timeline contract.

Blocker 1: mutable snapshot events invalidate pagination cursors

intent_admission_snapshot and supervisor_wake_state_snapshot derive their event identity/time from the current mutable state, including state/status and updatedAt:

  • packages/runtime/src/agent-graph-timeline.ts:389-400
  • packages/runtime/src/agent-graph-timeline.ts:531-542

Cursor resume then requires the exact old eventId + eventTime to still exist (agent-graph-timeline.ts:575-584). If page 1 ends on an admission snapshot while it is claimed, and the admission becomes executing before page 2, the old snapshot event disappears and the cursor fails with Agent graph timeline cursor is stale or invalid.

I reproduced this with a minimal two-state admission fixture. The existing test covers an earlier immutable event being discovered, but not a current snapshot changing between page reads.

This conflicts with the stable-cursor acceptance criterion. Making only the entity ID stable is not sufficient because the mutable snapshot can also move in chronological order. Pagination needs to bind to a frozen reconstruction/snapshot version, or mutable current-state snapshots should live outside the pageable historical stream. Persisting append-only transitions would also solve this later.

Blocker 2: an existing activation is invisible until its first committed RuntimeEvent

readCommittedAgentGraphProjection() enumerates the child AgentRuns, but its returned projection contains only records/state derived from committed RuntimeEvents. buildAgentGraphTimeline() then emits activation_started only by iterating projection.state.activations (agent-graph-timeline.ts:406-480).

As a result, a valid claimed/executing child AgentRun that already exists but has not committed its first RuntimeEvent produces only:

operator_provisioned
intent_claimed
intent_admission_snapshot

There is no activation_started, even though the AgentRun authority says the activation exists. This is a real live-execution window between durable Run creation and the first RuntimeEvent, and it also hides an activation that fails before producing a committed RuntimeEvent.

The timeline should match each claim's targetSessionId/targetRunId against the actual child AgentRun and emit activation_started from the Run's durable createdAt. Terminal status can still remain RuntimeEvent-authoritative to preserve the existing stop-race semantics.

I do not consider the lack of a cross-store atomic transaction itself a blocker for this query-time projection; the cursor/snapshot contract is the part that needs to remain coherent across pages.

@likun666661
likun666661 marked this pull request as ready for review July 28, 2026 06:12
@likun666661
likun666661 merged commit 17ee6c3 into agent/1341-reconcile-graph-schedule Jul 28, 2026
likun666661 added a commit that referenced this pull request Jul 28, 2026
* feat(runtime): reconcile graph supervisor schedules

* fix(runtime): close graph stop admission races

* fix(runtime): gate graph admission inside pending turns

* fix(runtime): stop pending turns with cached backend

* feat(runtime): bootstrap dynamic graph operators (#1474)

* feat(runtime): bootstrap dynamic graph operators

* fix(storage): close graph provision crash gaps

* feat(runtime): host-manage agent graph invocations (#1480)

* feat(runtime): host-manage agent graph invocations

* fix(runtime): fence graph host lifecycle

* feat(runtime): add agent graph client read model (#1483)

* feat(runtime): add agent graph client read model

* fix(runtime): materialize bounded graph client views

* fix(runtime): fence graph projection updates

* fix(runtime): isolate graph client projection

* fix(runtime): repair dirty graph projections

* feat(desktop): add playable Graph Mode (#1513)

* feat(desktop): add playable graph mode

* fix(desktop): resume graph supervisor at quiescence

* fix(desktop): make graph wakes durably retryable

* fix(desktop): converge graph wake lifecycle

* fix(desktop): settle parked graph wakes

* feat(runtime): reconstruct replayable agent graph timeline (#1549)

* feat(runtime): reconstruct agent graph timeline

* docs: explain agent graph architecture (#1550)

* fix(runtime): stabilize graph timeline cursors

* docs: clarify graph timeline current state

* style: format graph integration merge
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