Skip to content

fix(workflows): preserve stage duration across durable resume - #1751

Merged
lavaman131 merged 4 commits into
mainfrom
fix/1713-preserve-stage-duration
Jul 13, 2026
Merged

fix(workflows): preserve stage duration across durable resume#1751
lavaman131 merged 4 commits into
mainfrom
fix/1713-preserve-stage-duration

Conversation

@lavaman131

@lavaman131 lavaman131 commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

Preserve a durable LM stage's pause-adjusted elapsed duration across a mid-stage /workflow resume. Active session checkpoints now refresh timing even when the session file is unchanged, and a resumed process rebases the live timer from the persisted baseline so user-visible and lifecycle durations cover both process segments.

Changes

  • Persist startedAt and accumulated durationMs on every changed active-stage session checkpoint.
  • Include timing in the session-checkpoint identity so repeated checkpoints for the same session remain durable and idempotent.
  • Hydrate the latest active timing through the durable backend interface, scoped backends, file storage, and DBOS envelopes.
  • Pass the durable accumulated duration into resumed stages and tasks, then rebase their live start time without producing negative durations.
  • Preserve existing pause accounting by checkpointing pause-adjusted elapsed time and only subtracting pauses incurred in the resumed process segment.
  • Keep completed-stage output lookup authoritative, preventing timing-only checkpoints from replaying completed stages or side effects.
  • Document durable active-stage timing behavior and add an Unreleased changelog entry.

Acceptance criteria coverage

  • Repeated active checkpoints: controllable-clock tests verify later checkpoints update duration even with an unchanged sessionFile, while identical timing snapshots deduplicate.
  • Process boundary: a file-backed integration test writes with one backend instance, resumes and completes through a fresh instance, and replays through a third instance.
  • Terminal reporting: the integration test verifies stored, lifecycle-reported, and durable completed-stage duration includes pre-resume and post-resume segments.
  • Pause accounting: tests cover active pauses, post-resume pauses, nonnegative rebasing, and no double counting.
  • Both durable backends: file-backed process-boundary round trips and fresh DBOS hydration preserve the latest timing fields; repeated DBOS hydration remains idempotent.
  • Replay idempotency: completed replay uses the durable output without invoking the live stage again; existing tool/UI identity remains unchanged.

Validation

  • AGENT=1 bun test test/unit/*durable*.test.ts test/unit/store.test.ts test/unit/node-card.test.ts test/unit/run-detail-render.test.ts test/unit/status-list-render.test.ts test/unit/executor-lifecycle-persistence-1.test.ts test/unit/executor-lifecycle-persistence-2.test.ts — 252 passed
  • bun run test:unit — passed in commit and pre-push hooks
  • bun run typecheck — passed
  • bun run lint — passed
  • bun run check:file-length — passed
  • git diff --check — passed

Fixes #1713

Verified proof

Verified from the rebased PR worktree against the current base; no tmux proof was used because controllable-clock and fresh-backend process-boundary tests provide deterministic timing evidence.

  • Base SHA: 28388b7b6e8a5466d9d36c719ee999ac0fa13d26
  • Head SHA: 2d6ed612ec9c588b66413f73def11e50a56d2373
  • Focused durable resume suite (run twice): 69 passed, 0 failed across 3 files on each run
  • Broader durable/lifecycle suite: 254 passed, 0 failed across 21 files
  • Full unit suite: 3,283 passed, 0 failed across 377 files
  • bun run typecheck: passed
  • bun run lint: passed
  • bun run check:file-length: passed (2,062 tracked files checked)
  • git diff --check: passed
  • Commit and pre-push hooks: passed, including lint, file-length, and full unit tests

Overlap audit confirmed that later integration must retain both sides of the shared contracts: #1750's stage usage checkpoint/rollup metadata, #1748's resumability eligibility predicate, and #1758's completed-run snapshot reconstruction alongside this PR's startedAt/durationMs session shape and timing-aware checkpoint identity. Added regression coverage for two successive process-boundary resumes with per-segment pauses, resumed task baseline propagation, and completed-output replay authority.

Greptile Summary

This PR preserves durable workflow stage timing across mid-session resumes. The main changes are:

  • Active stage and task checkpoints now include startedAt and pause-adjusted durationMs.
  • Durable stage-session lookup now hydrates the latest timing baseline through file, scoped, in-memory, and DBOS-backed paths.
  • Resumed stages and tasks rebase their live timer from the saved accumulated duration.
  • Completed-stage replay remains authoritative so timing-only checkpoints do not rerun completed work.
  • Docs, changelog entries, and tests cover repeated checkpoints, pauses, DBOS hydration, file-backed process boundaries, and completed replay.

Confidence Score: 5/5

Safe to merge with minimal risk.

The changes are focused on durable timing metadata and resume timer rebasing. Tests cover repeated checkpoints, pause accounting, file-backed resumes, DBOS hydration, tasks, and completed replay. No verified functional or security issues were found.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • I inspected the general-contract-validation proof log to verify the exact command, working directory, start and end timestamps, duration, exit codes, stdout/stderr, and per-test pass/fail counts for the timing/resume and lifecycle/replay validations.
  • I reviewed the durable timing/resume results, which show 70 tests across 3 files all passing with EXIT_CODE: 0 in about 1165 ms.
  • I reviewed the adjacent lifecycle/replay results, which show 48 tests across 4 files all passing with EXIT_CODE: 0 in about 841 ms.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
packages/workflows/src/durable/backend.ts Extends stage-session lookup to return latest active timing fields.
packages/workflows/src/durable/scoped-backend.ts Propagates active timing fields through scoped durable lookups.
packages/workflows/src/durable/stage-primitive.ts Records timing-aware session checkpoints and passes duration baselines into resumed stages and tasks.
packages/workflows/src/runs/foreground/executor-stage-call.ts Rebases live stage start time from the durable accumulated duration once per stage lifecycle.
packages/workflows/src/shared/timing.ts Adds helper logic for nonnegative stage timer rebasing.
test/unit/durable-backend.test.ts Adds file-backed persistence coverage for latest active-stage timing.
test/unit/durable-dbos-backend.test.ts Adds DBOS hydration coverage for latest active-stage timing.
test/unit/durable-stage-session-resume.test.ts Adds tests for repeated checkpoints, pause accounting, resume baselines, process-boundary resumes, and completed replay.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Live as Active workflow process
participant Backend as Durable backend
participant Fresh as Resumed workflow process
participant Replay as Later replay

Live->>Backend: recordStageSessionCheckpoint(sessionFile, startedAt, durationMs)
Live->>Backend: refresh checkpoint with latest pause-adjusted duration
Fresh->>Backend: getStageSession(replayKey)
Backend-->>Fresh: sessionFile + accumulated durationMs
Fresh->>Fresh: rebasedStageStartedAt(durationMs, Date.now())
Fresh->>Backend: completed stage checkpoint with cumulative duration
Replay->>Backend: stageCheckpointWithOutput(replayKey)
Backend-->>Replay: cached output + latest metadata
Replay-->>Replay: return durable output without live stage side effects
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Live as Active workflow process
participant Backend as Durable backend
participant Fresh as Resumed workflow process
participant Replay as Later replay

Live->>Backend: recordStageSessionCheckpoint(sessionFile, startedAt, durationMs)
Live->>Backend: refresh checkpoint with latest pause-adjusted duration
Fresh->>Backend: getStageSession(replayKey)
Backend-->>Fresh: sessionFile + accumulated durationMs
Fresh->>Fresh: rebasedStageStartedAt(durationMs, Date.now())
Fresh->>Backend: completed stage checkpoint with cumulative duration
Replay->>Backend: stageCheckpointWithOutput(replayKey)
Backend-->>Replay: cached output + latest metadata
Replay-->>Replay: return durable output without live stage side effects
Loading

Comments Outside Diff (1)

  1. packages/workflows/src/durable/stage-primitive.ts, line 318-319 (link)

    P1 Use latest metadata
    metadataValue takes the first checkpoint with durationMs, but active session checkpoints are now written repeatedly before completion and listCheckpoints() returns them in ascending completedAt order. For schema-backed stages whose output checkpoint lacks timing, replay hydration merges the earliest active duration instead of the final stage-meta or latest session duration, so graph/status replay can show stale elapsed time after a durable resume.

    Artifacts

    Repro: focused Bun test harness for stale replay hydration metadata

    • Contains supporting evidence from the run (text/typescript; charset=utf-8).

    Repro: failing Bun test output showing hydrated durationMs 111 versus expected 333

    • Keeps the command output available without making the summary code-heavy.

    View artifacts

    T-Rex Ran code and verified through T-Rex

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/workflows/src/durable/stage-primitive.ts
    Line: 318-319
    
    Comment:
    **Use latest metadata**
    `metadataValue` takes the first checkpoint with `durationMs`, but active session checkpoints are now written repeatedly before completion and `listCheckpoints()` returns them in ascending `completedAt` order. For schema-backed stages whose output checkpoint lacks timing, replay hydration merges the earliest active duration instead of the final `stage-meta` or latest session duration, so graph/status replay can show stale elapsed time after a durable resume.
    
    How can I resolve this? If you propose a fix, please make it concise.

Reviews (4): Last reviewed commit: "Merge origin/main into fix/1713-preserve..." | Re-trigger Greptile

@mintlify

mintlify Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
bastani 🟢 Ready View Preview Jul 12, 2026, 6:42 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Comment thread packages/workflows/src/runs/foreground/executor-stage-call.ts Outdated
flora131 and others added 3 commits July 12, 2026 20:57
Persist pause-adjusted active-stage timing at repeated checkpoints and rebase resumed stage clocks from the durable baseline. Cover file and DBOS hydration, process-boundary completion, pause accounting, and replay idempotency.

Assistant-model: GPT-5.6 Sol
Apply the durable timing baseline only once per live stage and cover concurrent tracked calls across process-boundary resume and replay.

Co-Authored-By: GPT-5.6 Sol <noreply@openai.com>
@flora131
flora131 force-pushed the fix/1713-preserve-stage-duration branch from 54f766c to 2d6ed61 Compare July 13, 2026 04:04
@lavaman131

Copy link
Copy Markdown
Collaborator Author

Verified PR #1751 after rebasing onto the current origin/main and force-pushing with lease.

$ AGENT=1 bun test test/unit/durable-stage-session-resume.test.ts test/unit/durable-backend.test.ts test/unit/durable-dbos-backend.test.ts
69 pass
0 fail
Ran 69 tests across 3 files.

The focused command passed twice with the same count.

$ AGENT=1 bun test test/unit/*durable*.test.ts test/unit/store.test.ts test/unit/node-card.test.ts test/unit/run-detail-render.test.ts test/unit/status-list-render.test.ts test/unit/executor-lifecycle-persistence-1.test.ts test/unit/executor-lifecycle-persistence-2.test.ts
254 pass
0 fail
Ran 254 tests across 21 files.

$ AGENT=1 bun run test:unit
3283 pass
0 fail
Ran 3283 tests across 377 files.

$ bun run typecheck
$ tsc --noEmit

$ bun run lint
$ tsc --noEmit

$ bun run check:file-length
File length check passed: 2062 files checked from tracked files (max 500; skipped 70 by path, 32 by generated marker).

$ git diff --check
# no output; exit 0

Base: 28388b7b6e8a5466d9d36c719ee999ac0fa13d26
Head: 2d6ed612ec9c588b66413f73def11e50a56d2373

Audit notes: added repeated two-boundary resume coverage with pauses in each resumed segment and task-baseline propagation. Completed output remains authoritative. Shared-file overlap was checked against #1750 (retain usage checkpoint/rollup metadata), #1748 (retain centralized resume eligibility), and #1758 (retain timing fields in completed snapshot reconstruction). Deterministic clock/backend tests are stronger than a tmux timing capture here, so no tmux proof was used.

@lavaman131
lavaman131 merged commit 6b6185c into main Jul 13, 2026
10 checks passed
@flora131
flora131 deleted the fix/1713-preserve-stage-duration branch August 14, 2026 01:16
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.

Preserve elapsed stage duration across mid-stage /workflow resume

2 participants