Skip to content

[docs] Document session rewind and branching design - #5917

Open
mmabrouk wants to merge 1 commit into
release/v0.112.0from
docs/session-rewind-branching
Open

[docs] Document session rewind and branching design#5917
mmabrouk wants to merge 1 commit into
release/v0.112.0from
docs/session-rewind-branching

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

Context

PR #5860 fixes rewind resurrection by moving the user onto a new session, but the implementation still leaves open questions about reload safety, cross-device durability, metadata, and future branch storage. Reviewers lacked one place that separated the immediate frontend fix from the longer-term backend model.

Changes

Adds a self-contained design workspace for session rewind and branching. It documents the user story, current frontend and runner behavior, known failure modes, and the requested changes before PR #5860 merges.

The durable proposal keeps records append-only. A child session stores lineage to its parent, an effective-turn count, and an indexed physical cutoff. A lineage-aware transcript read reconstructs inherited context without copying parent records or adding frontend request fan-out. Separate documents cover API contracts, frontend state, attachment access, migrations, query counts, storage growth, and rollout phases.

Tests / notes

  • Documentation-only change.
  • git diff --check -- docs/design/session-rewind-branching passes.
  • The workspace was independently reviewed for reload state, cutoff ordering, attachment ownership, transaction boundaries, migrations, and bounded query performance.
  • Start with docs/design/session-rewind-branching/README.md for the reading order.

@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Aug 10, 2026
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Blocked Blocked Aug 10, 2026 5:22pm

Request Review

@dosubot dosubot Bot added the documentation Improvements or additions to documentation label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Documentation
    • Added comprehensive design documentation for session rewind and branching.
    • Documented terminology, user flows, current limitations, and recommended implementation phases.
    • Defined proposed frontend and durable branching APIs, data models, lineage handling, and transcript behavior.
    • Included performance, migration, retention, security, and attachment-access considerations.
    • Recorded implementation status, research findings, risks, and open decisions.

Walkthrough

Added a design workspace for session rewind and branching. The documents cover frontend fork recovery, durable session lineage, effective transcript reads, attachment access, implementation sequencing, performance, migration, and open decisions.

Changes

Session rewind branching

Layer / File(s) Summary
Scope and current behavior
docs/design/session-rewind-branching/README.md, context.md, research.md
Documents current storage and rewind behavior, observed failures, terminology, and the separation between frontend fixes and durable branching.
Frontend fork lifecycle
docs/design/session-rewind-branching/api-design.md
Defines persisted fork bootstrap state, reload recovery, cleanup rules, child-session titles, and durable frontend session flow.
Lineage and transcript contracts
docs/design/session-rewind-branching/data-model.md, api-design.md
Defines lineage storage, inherited-turn cutoffs, attachment access, durable fork creation, effective transcript queries, and session-list lineage metadata.
Implementation and migration plan
docs/design/session-rewind-branching/plan.md, performance-and-migration.md
Specifies implementation steps, runner and frontend integration, compatibility removal, performance verification, and database migration guidance.
Decisions and open items
docs/design/session-rewind-branching/status.md
Records settled decisions, unresolved policies, and known implementation constraints.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • Agenta-AI/agenta#5486: Related durable session-record reconstruction and effective transcript-read concepts.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the documentation added for session rewind and branching.
Description check ✅ Passed The description accurately summarizes the documentation workspace, durable branching proposal, and documentation-only scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/session-rewind-branching

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 17

🧹 Nitpick comments (3)
docs/design/session-rewind-branching/data-model.md (1)

230-244: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Define keys and indexes for session_attachment_access.

The table has no primary key, unique constraint, or lookup index. Retries can create duplicate access rows, and attachment authorization can scan the table for every reference. Add uniqueness for (project_id, session_id, attachment_id) and indexes for source-owner and deletion checks.

docs/design/session-rewind-branching/performance-and-migration.md (2)

71-76: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Make the single-query requirement explicit.

bounded iterative query can mean one database round trip per ancestor. That contradicts the stated one lineage-chain query and the requirement that depth must not increase query count. Specify a recursive CTE or a single batched query. Add query-count tests for depths 0, 1, 5, and the configured maximum.


122-123: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

State the parent-prefix immutability contract.

The cache plan assumes that the parent prefix never changes. The documents also mention record lifecycle fields and ancestor soft deletion. If a retained record or attachment access can change, the effective transcript cache can serve stale or unauthorized content. Require stable inherited data while descendants exist, or add lineage-aware invalidation and versioning.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 6db91fa0-f4d4-436e-b96b-5f9223484abc

📥 Commits

Reviewing files that changed from the base of the PR and between 4af1551 and 883a970.

📒 Files selected for processing (8)
  • docs/design/session-rewind-branching/README.md
  • docs/design/session-rewind-branching/api-design.md
  • docs/design/session-rewind-branching/context.md
  • docs/design/session-rewind-branching/data-model.md
  • docs/design/session-rewind-branching/performance-and-migration.md
  • docs/design/session-rewind-branching/plan.md
  • docs/design/session-rewind-branching/research.md
  • docs/design/session-rewind-branching/status.md

Comment on lines +33 to +44
The retained messages remain in the existing local message store. The write rules are:

- Store edit or rerun mode when `rewindForkAtomFamily` creates the child.
- Store the restored user draft for edit mode.
- Restore the draft or automatic rerun behavior when the child mounts after reload.
- Read it when `buildAgentRequest` decides whether to disable last-message-only optimization.
- Mark it `in-flight` only when transport dispatch begins.
- Treat an `in-flight` bootstrap restored after page reload as `pending`, because the browser stream
that owned it no longer exists.
- Keep it through error, abort, and disconnect.
- Clear it only when the first request completes successfully.
- Delete it when the child session is permanently deleted.

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## files matching api-design.md"
fd -a 'api-design\.md$' . | sed 's#^\./##'

echo "## relevant file excerpt"
if [ -f docs/design/session-rewind-branching/api-design.md ]; then
  cat -n docs/design/session-rewind-branching/api-design.md | sed -n '1,120p'
fi

echo "## persist drainPersist references"
rg -n "drainPersist|persist|bootstrap|in-flight|pending|rewindForkAtomFamily|buildAgentRequest|onFinal|start|finish|onFinish|onError" services docs -S || true

echo "## package versions for ai"
for f in package.json apps services apps/* package-lock.json pnpm-lock.yaml yarn.lock; do
  [ -f "$f" ] || continue
  echo "--- $f"
  rg -n '"ai"|ai@\|`@ai-sdk`|`@ai-sdk/`(.*)' "$f" | head -80 || true
done

Repository: Agenta-AI/agenta

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## candidate session persist source"
fd -a 'persist\.(ts|js)$' services package docs | sed 's#^\./##' || true

echo "## runner persist outline if available"
if [ -f services/runner/src/sessions/persist.ts ]; then
  wc -l services/runner/src/sessions/persist.ts
  ast-grep outline services/runner/src/sessions/persist.ts || true
  cat -n services/runner/src/sessions/persist.ts | sed -n '1,260p'
fi

Repository: Agenta-AI/agenta

Length of output: 14284


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## focused api-design excerpt"
sed -n '1,90p' docs/design/session-rewind-branching/api-design.md | cat -n

echo "## runner persist full remainder around buildPersistingEmitter"
sed -n '237,441p' services/runner/src/sessions/persist.ts | cat -n

echo "## ai SDK version pins"
rg -n -S '"ai"|"`@ai-sdk/`(.*)"|ai@|pnpm-lock.yaml|package-lock.json|yarn.lock' package.json package-lock.json pnpm-lock.yaml yarn.lock **/package.json 2>/dev/null | head -200 || true

echo "## references to bootstraps/session reload state"
rg -n -S "bootstrap|in-flight|pending|last-message|buildAgentRequest|rewindForkAtomFamily|drainPersist|takePersistFailures|recordsIncomplete" services/runner services api apps -S || true

Repository: Agenta-AI/agenta

Length of output: 50375


🌐 Web query:

Vercel AI SDK 3 onFinish onFinishStream completed finishReason error documentation callback fields

💡 Result:

In the Vercel AI SDK, onFinish (now primarily referred to as onEnd) is a lifecycle callback used to perform actions after a generation or stream completes [1][2][3]. Key details regarding this callback in the AI SDK include: Callback Name: While onFinish was commonly used in earlier versions, it has been deprecated in favor of onEnd [2][4][3]. Both serve the same purpose: triggering logic once the entire generation (including all steps) is complete [1][5]. Callback Fields: When the onEnd (or onFinish) callback is triggered, it receives an event object containing aggregated data about the completed generation [1][5]. Common fields include: - text: The final generated text [1][6]. - finishReason: The reason the generation stopped (e.g., 'stop', 'length', 'content-filter', 'tool-calls') [1][5][7]. - usage: Token usage statistics (LanguageModelUsage) [1][8]. - responseMessages: The messages generated during the call [1][6]. - steps: Details regarding intermediate steps [1][5]. - totalUsage: Total token usage across all steps [1][5]. Error Handling: - Callback Errors: If the onEnd/onFinish callback itself throws an error, it is caught internally by the AI SDK, and the operation continues [4]. - Streaming Errors: The onEnd callback is typically triggered only upon successful or complete stream termination [9]. For errors that occur during a stream, developers should use try/catch blocks for the stream operation or monitor stream parts (such as 'error' or 'tool-error' types) when iterating over the stream [9][10]. - Abort Handling: If a stream is aborted (e.g., by the user), onEnd is not called [9]. Instead, an onAbort callback is provided for cleanup operations [9][10]. Note: Ensure you are using a compatible version of the SDK, as onFinish/onEnd functionality evolved significantly in early 3.x releases [11]. Documentation recommends using onEnd and onStepEnd for new implementations [4][8].

Citations:


🌐 Web query:

Vercel AI SDK onAbort error abort disconnect stream part error disconnect fields documentation

💡 Result:

In the Vercel AI SDK, the onAbort callback and stream abort handling are designed to manage server-side cleanup when a stream is canceled, typically via an AbortSignal [1][2][3]. Key concepts and documentation regarding stream aborts include: 1. The onAbort Callback: When a stream (e.g., created via streamText) is aborted, the onAbort callback is triggered instead of onEnd [1][3]. This allows you to perform cleanup tasks like updating UI states or persisting partial results [1][2]. It receives information about the completed steps before the abort [1][4]. 2. Handling Abort in Streams: You can also handle abort events directly by processing the stream chunks, where an abort chunk may be present [1][3][5]. 3. UI Message Streams (toUIMessageStream): For UI-level streaming responses, the onEnd callback receives an isAborted parameter [2][5]. To ensure this callback executes during an abort, you must pass consumeStream to the createUIMessageStreamResponse configuration [6][5]. Without this, the response may terminate before onEnd can be triggered [6]. 4. Distinction Between Abort and Disconnect: In setups using resumable streams (e.g., useChat with resume: true), a client-side abort (like closing a tab or calling stop) is treated as a "disconnect" rather than a request to cancel the underlying server-side generation [7][8]. To support explicit user cancellation in these cases, you should implement a dedicated stop endpoint that manually cancels the background work [7][8]. For detailed implementation and specific code patterns, refer to the official AI SDK documentation on Error Handling [1], Advanced: Stopping Streams [2], and the specific troubleshooting guide for stream abort handling [6]. Note that under certain conditions, such as very rapid abort signals, some users have reported the system triggering an onError path; ensure your error and abort handling logic is robust [9].

Citations:


🌐 Web query:

Vercel AI SDK streamText response createUIMessageStreamResponse onEnd isAborted no error flags documentation

💡 Result:

When using createUIMessageStreamResponse in the Vercel AI SDK, the onEnd callback may fail to execute when a stream is aborted because the abort signal immediately terminates the response before the callback can be triggered [1][2]. This behavior can result in missing cleanup operations or logs [1]. To ensure that the onEnd callback is executed even when the stream is aborted, you must explicitly pass consumeStream from the ai package to the consumeSseStream property within the createUIMessageStreamResponse configuration [1][2][3]. Example implementation: import { consumeStream, createUIMessageStreamResponse, toUIMessageStream, streamText } from 'ai'; export async function POST(req: Request) { const { messages } = await req.json; const result = streamText({ //... model configuration abortSignal: req.signal, }); return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream, onEnd: async ({ isAborted }) => { if (isAborted) { console.log('Stream was aborted'); } else { console.log('Stream completed normally'); } }, }), consumeSseStream: consumeStream, // Enables onEnd/onFinish to trigger on abort }); } When configured this way, the isAborted flag in the onEnd callback will correctly reflect the stream's status [2][4]. Without this configuration, the SDK does not inherently force the execution of onEnd upon abortion, as the underlying response connection is severed prematurely [1].

Citations:


🌐 Web query:

Vercel AI SDK 3.0.8 onFinishStream return value fields abort error disconnect

💡 Result:

In the Vercel AI SDK, including versions around 3.0.8, the onFinish callback behavior regarding aborts, disconnects, and errors has evolved to provide better status tracking [1][2]. If you are using the useChat hook, recent updates have introduced isAbort, isDisconnect, and isError as fields returned in the onFinish callback to help you distinguish between normal completion and abnormal termination [1][2]. If these fields appear to be missing, ensure your dependencies (specifically @ai-sdk/react and ai) are updated to the latest versions, as older versions may not expose these properties [2]. For server-side stream handling, the behavior is as follows: 1. Handling Aborts: By default, onFinish or onEnd callbacks might not execute when a stream is aborted because the abort signal terminates the response immediately [3][4][5]. To ensure these callbacks execute even during an abort (e.g., to perform cleanup or save partial messages), you should configure your response handler—such as createUIMessageStreamResponse—with consumeSseStream: consumeStream [3][4]. 2. Distinguishing Aborts: In server-side utilities like toUIMessageStream, the onEnd callback provides an isAborted flag [3][4]. Using consumeStream allows this callback to trigger during an abort, allowing you to run custom logic based on whether the stream completed or was interrupted [3][4]. 3. Troubleshooting Consistency: If you encounter cases where isAborted is false even when an abort occurred, this is often due to inconsistency between different utility functions (like createUIMessageStream vs toUIMessageStream) [6]. Additionally, note that onAbort is a separate callback explicitly intended for handling stream cancellation, while onFinish is primarily reserved for normal completions [4][7]. If you are experiencing issues where onFinish is not triggering as expected, verify that you are not relying solely on onFinish for cleanup, and consider adding an onAbort callback for specific abort-related logic [4][7]. Ensure your environment is using compatible, up-to-date SDK packages to avoid discrepancies in callback signatures [2].

Citations:


Tie fork bootstrap cleanup to durable child records.

persistEvent only enqueues durable records; drainPersist.sessionId) waits for those queued writes. Clear the bootstrap from a client streaming callback creates a race where a reload can send a last-message-only request without the queued first turn records, or replay a partially persisted turn that was drained on the first client connection. Use a server-acknowledged durable-write signal or reconcile records with a stable first-turn idempotency key before clearing the bootstrap.

Comment on lines +158 to +188
Add a logical transcript endpoint:

```http
POST /sessions/transcript/query
```

Request:

```json
{
"session_id": "session-B"
}
```

Response:

```json
{
"session_id": "session-B",
"count": 42,
"records": [
{"session_id": "session-A", "turn_id": "A1", "record_index": 0},
{"session_id": "session-A", "turn_id": "A2", "record_index": 0},
{"session_id": "session-B", "turn_id": "B1", "record_index": 0}
]
}
```

Existing `SessionRecord.session_id` already identifies provenance. The response does not rewrite
source IDs or add a misleading `inherited` field. A consumer can compare each record's session ID
with the requested session ID.

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.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repository files matching api-design:"
fd -i 'api-design\.md$|session-rewind|session' docs . | sed -n '1,120p'

echo
echo "Target file excerpt:"
if [ -f docs/design/session-rewind-branching/api-design.md ]; then
  nl -ba docs/design/session-rewind-branching/api-design.md | sed -n '130,205p'
else
  echo "target file not found"
fi

echo
echo "Search for transcript/query permissions/auth mentions:"
rg -n "transcript/query|/sessions/transcript|authorize|authorization|access|permissions|lineage|ancestor|parent|session_id|SessionRecord" . -g '*.md' -g '*.ts' -g '*.py' -g '*.go' -g '*.java' | sed -n '1,220p'

Repository: Agenta-AI/agenta

Length of output: 7759


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Target file excerpt using awk:"
awk '{printf "%6d\t%s\n", NR, $0}' docs/design/session-rewind-branching/api-design.md | sed -n '130,210p'

echo
echo "Search for transcript/query endpoint and authorization text:"
rg -n "transcript/query|/sessions/transcript|authorize|authorization|access|permissions|lineage|ancestor|parent|SessionRecord|session_id" docs/design/session-rewind-branching/api-design.md docs -g '*.md' | sed -n '1,260p'

Repository: Agenta-AI/agenta

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant line ranges in target design file:"
awk '{printf "%6d\t%s\n", NR, $0}' docs/design/session-rewind-branching/api-design.md | sed -n '1,170p'
awk '{printf "%6d\t%s\n", NR, $0}' docs/design/session-rewind-branching/api-design.md | sed -n '210,245p'

echo
echo "Focused search in target design and sessions docs:"
rg -n "authorize|authorization|access|permission|/sessions/transcript/query|transcript/query|SessionRecord|lineage" docs/design/session-rewind-branching/api-design.md docs/designs docs/designs/sessions docs/designs/advanced-auth --glob '*.md' | sed -n '1,220p'

Repository: Agenta-AI/agenta

Length of output: 43148


Authorize the full lineage before returning logical transcript records.

/sessions/transcript/query returns records from ancestor sessions while the request only names the child session. Add an authorization rule that requires access to the child session and every ancestor used by the logical transcript.

Comment on lines +249 to +255
Durable fork flow:

1. Count the complete visible turns before the selected UI turn.
2. Call the fork endpoint.
3. Add the returned child summary to local state and make it active.
4. Optimistically show the retained local prefix while transcript hydration resolves.
5. Send the edited or copied user prompt as the child's first turn.

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== git status =="
git status --short

echo "== locate file =="
fd -a 'api-design.md|desig.*|design.*' . | sed 's#^\./##' | head -100

echo "== target excerpt =="
if [ -f docs/design/session-rewind-branching/api-design.md ]; then
  nl -ba docs/design/session-rewind-branching/api-design.md | sed -n '220,275p'
else
  echo "target file not found"
fi

echo "== relevant mentions =="
rg -n "inherited_turn_count|durable|reconstructMessages|visible turns|rewind|turn_count|selected turn|branching" docs/design -S || true

Repository: Agenta-AI/agenta

Length of output: 2879


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file lines =="
awk '{printf "%6d\t%s\n", NR, $0}' docs/design/session-rewind-branching/api-design.md | sed -n '220,275p'

echo "== API contract sections =="
awk '{printf "%6d\t%s\n", NR, $0}' docs/design/session-rewind-branching/api-design.md | sed -n '1,140p'

echo "== relevant mentions =="
grep -RnE "inherited_turn_count|visible turn|visible turns|reconstructMessages|durable|rewind|branch|turn_count|selected turn|cut-point|boundary|assistant" docs/design/session-rewind-branching/api-design.md || true

Repository: Agenta-AI/agenta

Length of output: 9633


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== code references for fork API fields and reconstructMessages =="
rg -n "inheritedTurnCount|inherited_turn_count|cutoff_record_id|cutoff_session_id|reconstructMessages|visible turn|visible.*turn|effective transcript|complete turns" -S .

echo "== files likely containing record/message reconstruction =="
rg -n "function reconstructMessages|const reconstructMessages|reconstructMessages:|class .*Record|interface .*Record|SessionRecord|message.*record|physical record|turn" -S --glob '!node_modules' --glob '!dist' --glob '!build' | head -200

Repository: Agenta-AI/agenta

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== focused code references =="
rg -n "inheritedTurnCount|inherited_turn_count|cutoff_record_id|cutoff_session_id|replayHistory|rewindForkAtomFamily|reconstructMessages|visible.*turn|visible turn|complete turns" \
  -S \
  --glob '!docs/**' \
  --glob '!node_modules/**' \
  --glob '!dist/**' \
  --glob '!build/**' \
  --glob '!*.md' \
  . || true

echo "== focused frontend/design references in docs/session-rewind-branching =="
rg -n "replayHistory|rewindForkAtomFamily|reconstructMessages|visible.*turn|visible turn|complete turns|selected UI turn" \
  -S docs/design/session-rewind-branching || true

Repository: Agenta-AI/agenta

Length of output: 6783


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== services/runner/src/sessions/reconstruct.ts =="
awk '{printf "%6d\t%s\n", NR, $0}' services/runner/src/sessions/reconstruct.ts | sed -n '1,160p'

echo "== services/runner/src/sessions/reconstruct.ts tests =="
awk '{printf "%6d\t%s\n", NR, $0}' services/runner/tests/unit/session-reconstruct.test.ts | sed -n '1,170p'

echo "== services/runner/src/engines/sandbox_agent/reconstruct-history.ts relevant =="
awk '{printf "%6d\t%s\n", NR, $0}' services/runner/src/engines/sandbox_agent/reconstruct-history.ts | sed -n '95,130p'

Repository: Agenta-AI/agenta

Length of output: 14973


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs/session-rewind-branching references for visible and selected turn flow =="
grep -RnE "visible turn|selected UI turn|selected turn|replayHistory|buildAgentRequest|prompt|last-message-only" docs/design/session-rewind-branching || true

echo "== repository focused search for selected turn/fork bootstrap =="
grep -RnE "rewindForkAtomFamily|buildAgentRequest|replayHistory|last-message-only|selected.*turn|turnId" web/oss services runner --glob '!*node_modules*' --glob '!*.md' || true

Repository: Agenta-AI/agenta

Length of output: 2486


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== web/oss focused sources for transcript/render/fork =="
rg -n "visible turn|last-message-only|replayHistory|buildAgentRequest|turnId|rewindForkAtomFamily|selected.*turn|rewrite|copy.*prompt|prompt" web/oss/services --glob '!node_modules/**' | head -200 || true

echo "== design docs all relevant files line counts =="
find docs/design/session-rewind-branching -maxdepth 2 -type f -print | sort | while read -r f; do
  wc -l "$f"
done

echo "== session-rewind-branching all markdown references to selected / visible / inherited / first turn =="
rg -n "selected|visible|inherited|first turn|last-message-only|rewindForkAtomFamily|replayHistory|copy|edit|rerun|incomplete" docs/design/session-rewind-branching || true

Repository: Agenta-AI/agenta

Length of output: 13716


Use a durable selected boundary instead of visible turn counts.

inherited_turn_count is a durable cursor, but step 1 derives it from complete visible UI turns. reconstructMessages can fold multiple assistant records into one UI message, so a complete visible message is not a durable turn identity. If the user selects an assistant turn, including its preceding user turn will duplicate that request when step 5 submits the edited/copied prompt as the child’s first turn. Define a stable selected turn/boundary and explicit inclusion semantics for both user and assistant rewind.

Comment on lines +258 to +259
The durable flow removes `replayHistory` and the persisted bootstrap because the runner no
longer depends on a browser-supplied prefix.

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Keep pending action state separate from transcript replay.

The durable flow can remove replayHistory, but it cannot remove persisted action state solely because the runner can reconstruct inherited context. ForkBootstrap also stores edit/rerun mode and the restored draft. After reload between fork creation and the first send, lineage does not tell the frontend whether to restore the composer or auto-rerun. Keep a persisted pending-action record until the first child request succeeds.

Comment on lines +267 to +270
Add a composite database operation, behind a core DAO interface, that owns one SQLAlchemy session
and transaction. It inserts the child `session_streams` row, the `session_lineage` row, and any
`session_attachment_access` rows. Unique constraints make an identical target retry idempotent; a
conflicting target causes the entire transaction to roll back.

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Implement idempotency beyond the unique constraint.

A unique constraint prevents duplicate rows. It does not make a repeated POST return the existing fork. The DAO must handle the conflict, compare source, target, and cutoff, and return the existing child only for an identical request. Return a conflict for a different lineage.

Comment on lines +42 to +43
Recommended default: add `session_attachment_access` rows that grant the child access to existing
parent-owned attachments without copying bytes or changing attachment IDs.

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.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Bound attachment access to the fork snapshot.

Define whether the child can access only attachments available at fork time or also attachments added to the parent later. Also define whether the access row grants read, write, or delete access, and how revocation works. Without these rules, child access can expand beyond the selected parent state.

Comment on lines +47 to +48
Recommended default: reject a fork when the selected turn is incomplete. Decide whether a completed
prefix of a currently running parent may be forked safely.

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Capture a stable snapshot when forking a running parent.

Rejecting incomplete turns does not define how validation and cutoff capture synchronize with append-only writes. Capture the completion state, effective-turn count, and physical record cutoff atomically, or retry when the parent advances. Otherwise the child can inherit partial or mismatched context.

Comment on lines +68 to +69
- Frontend and backend may deploy at different times. Removing `replayHistory` requires a capability
or rollout gate.

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Specify the mixed-version rollout contract.

“Requires a capability or rollout gate” is not an executable compatibility rule. Define the capability name, behavior for old and new client/server combinations, default behavior when absent, and the removal condition for replayHistory.

Comment on lines +70 to +71
- Session turns live in the core database while records may live in the tracing database. Effective
transcript resolution needs a bounded two-store read rather than a relational cross-database join.

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Define consistency semantics for the two-store read.

A bounded read from the core database and tracing database is not one atomic snapshot. Specify write ordering, visibility delay, retry behavior, and the response when lineage exists but records are not visible. Otherwise transcript resolution can omit records or violate the physical cutoff.

Comment on lines +74 to +75
- The package-level `@agenta/chat` rewind remains in-place and can reintroduce the original bug when
that surface becomes active.

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.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Gate the package-level rewind path before activation.

If @agenta/chat becomes reachable, its in-place rewind contradicts the settled new-session behavior and can reintroduce the original bug. Update that path before activation, or enforce a route or feature gate that excludes it from the release.

@mmabrouk
mmabrouk requested a review from ardaerzin August 10, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant