History

July 5, 2026 · View on GitHub

History is ADE's per-project timeline. It has two surfaces sharing a single page: a Commits view (GitKraken-style DAG for the focused lane) and an Activity view (the unified operations + sessions + worker feed). Both surfaces live inside HistoryPage and sync through a shared zustand store; the URL remembers which surface, which lane, and which selection are active so deep links land back on the same row.

The Activity feed is sourced from the operations SQLite table plus in-memory adapters that synthesize timeline rows from chat sessions and CTO sessions. Git commits in the Commits view come from git log on the lane's worktree, not from the operations table.

Where this runs

Operation recording, the operations SQLite table, and the export pipeline all live inside the active ADE runtime (local machine runtime for local-bound windows, SSH-attached remote runtime for remote-bound windows). Every git operation runs through the runtime's gitOperationsService which brackets the command with operationService.start / finish, so the timeline records work performed on whichever host owns the lane's worktree. The renderer's window.ade.history.listOperations and exportOperations go through preload's callProjectRuntimeActionOr("operation", …) first and fall back to the legacy in-process IPC handlers when no runtime is bound. For remote-bound windows the operations database lives on the remote machine; the desktop simply renders rows it pulled through the runtime. The Commits view runs git.listRecentCommits / git.listBranches through the same runtime route. The export-to-disk dialog itself still runs on the desktop because the file is saved on the user's local machine — the runtime returns the rows, then the desktop's IPC handler writes the CSV/JSON to disk through the native save dialog.

Supplemental activity sources (chat sessions, CTO snapshot) are fetched directly from the renderer through the existing per-feature preload bridges and merged into the timeline at render time; they are not persisted into operations.

Source file map

Main process / runtime services:

PathRole
apps/desktop/src/main/services/history/operationService.tsCRUD for operations rows; the canonical entry point for record, start, finish, list, get, listHeadChanges, and pruneOld. Same source backs the ADE runtime and the desktop fallback path.
apps/desktop/src/main/services/state/kvDb.tsSchema for operations, checkpoints, pack_events, pack_versions, pack_heads, terminal_sessions, and orchestration-related tables.
apps/desktop/src/main/services/git/gitOperationsService.tsBrackets every git operation with operationService.start / finish, captures pre/post HEAD SHAs, and owns the per-lane undo/redo head-change pipeline (undoLastHeadChange, redoLastHeadChange, createTag, resetToCommit, pull with ff-only / rebase / merge modes). Before lane git mutations or lane git reads, it verifies that the saved worktreePath is still the Git top-level for that lane; stale paths that resolve to the primary checkout are rejected as missing lane worktrees so History and Git Actions do not read or mutate the wrong branch. Undo selection is branch-aware: it ignores checkout/undo rows, requires the recorded operation's metadata.branchRef to match the lane's current branch, and rechecks the branch before running reset --hard.
apps/desktop/src/main/services/lanes/laneService.tsLane CRUD now accepts CreateLaneArgs.startPoint, used by the Commits view's "Create lane here" affordance to fork a new lane from a specific commit.
apps/desktop/src/main/services/prs/prService.tsRecords PR creation as an operation.
apps/desktop/src/main/services/conflicts/conflictService.tsRecords rebase operations.
apps/desktop/src/main/services/sessions/sessionService.tsTerminal session lifecycle (separate terminal_sessions table).
apps/desktop/src/main/services/ipc/registerIpc.tsade.history.listOperations, ade.history.exportOperations, plus the new git IPC channels surfaced by the History toolbar (ade.git.createTag, ade.git.resetToCommit, ade.git.undoLastHeadChange, ade.git.redoLastHeadChange, the ff-only/rebase/merge variants of ade.git.pull).

Renderer components (apps/desktop/src/renderer/components/history/):

FileResponsibility
HistoryPage.tsxHosts the TimelineStoreProvider, the two-pane PaneTilingLayout (timeline ~60%, detail ~40%), URL ↔ store hydration for surface / laneId / eventId / commitSha, selected-lane mirroring when the URL is not driving lane state, commit-on-lane tracking for destructive action gates, and the auto-refresh poll for running activity events. Surface defaults to commits.
useTimelineStore.tsZustand store: raw + enriched events, WIP-by-lane nodes, surface, focus lane, selected commit/event, view mode, scope level, filters, lane visibility, columns. fetchEvents merges history.listOperations with fetchSupplementalTimelineRecords and sorts/dedupes via sortTimelineRecords.
timelineTypes.tsHistorySurface = "activity" | "commits", ViewMode = "graph" | "list" | "compact", TimelineEvent (enriched OperationRecord), LaneTrack, GraphLayout, TimelineFilters, LaneVisibility, ColumnConfig, WIPNode, MinimapBucket. DEFAULT_COLUMNS order is timestamp → graph → event → lane → author → status → duration → sha.
eventTaxonomy.tsSource of truth for event categories, importance levels, node shapes, and the per-kind EVENT_KIND_META table that every renderer (graph, list, compact, detail panel) consults. Adds taxonomy entries for the new git head-change kinds (git_undo_head_change, git_redo_head_change, git_tag_create, git_reset_soft/_mixed/_hard) and the unified-feed kinds (chat.session, cto.session).
historyActivitySources.tsPure mappers + the single fetchSupplementalTimelineRecords(limit) entry point that builds synthetic OperationRecord rows from agentChat.list and cto.getState. Synthetic IDs are namespaced (chat:, cto-session:) and the actor/eventLabel are embedded in metadataJson so the detail panel + graph can render them uniformly.
historySearch.tsTokenizer + matcher behind the Commits view search input. Supports bare full-text, quoted phrases, and the message: / msg: / = / author: / @ / commit: / sha: / # / branch: / ref: / parent: / is: / type: keys (e.g. is:merge, is:local, type:pushed).
commitGraphLayout.tsPure buildCommitGraphLayout(commitsNewestFirst) that assigns DAG columns first-parent style, then exposes commitEdgePath, columnCenterX, rowCenterY, and the COMMIT_ROW_HEIGHT / COMMIT_GRAPH_COL_WIDTH / COMMIT_GRAPH_PAD_LEFT constants the SVG layer in CommitHistoryView consumes.
CommitHistoryView.tsxVirtualized GitKraken-style commit graph for the focused lane. Loads commits via git.listRecentCommits (initial limit 120, expands to 500 on scroll-to-bottom and on non-empty search), git.listBranches for ref pills, draws nodes + edges as an SVG layer overlaid on @tanstack/react-virtual rows, and dispatches right-click commit actions via HistoryGitContextMenu.
CommitDetailPanel.tsxRight pane for the Commits surface: subject, author, full message (lazy via git.getCommitMessage), changed file list (git.listCommitFiles), related operations (any OperationRecord whose preHeadSha or postHeadSha matches the commit), and the same git action set the context menu exposes. Destructive lane mutations are disabled when the lane has no worktree or the commit was resolved only through a targeted lookup outside that lane's visible history.
HistoryGitContextMenu.tsxReusable right-click menu shared by CommitHistoryView rows and the CommitDetailPanel actions strip; built from buildCommitContextActions + groupCommitContextActions.
historyGitActions.tsPer-commit action catalogue and dispatcher: Inspect (checkout, open in Lanes git pane, compare-with-parent, view files), Create (branch, lane, tag), Apply (cherry-pick, revert, soft/mixed/hard reset), Share (open/copy GitHub link, copy patch via git.listCommitFiles + diff.getFilePatch, copy SHA, copy subject). Calls window.ade.git.* + window.ade.lanes.create({ startPoint }) and centralizes disabled reasons for missing worktrees or commits not on the focused lane history.
historyUrlHydration.tsPure URL-hydration helper used by HistoryPage tests and effects. Re-applies commitSha when a URL-driven lane focus change clears the store selection, while refusing to hydrate commit selections on the Activity surface.
historyLaneActions.tsLane-level action catalogue surfaced through the Commits toolbar's "Lane git actions" menu: Remote (fetch, pull ff-only/rebase/merge, push, force-push-with-lease), Recover (undo/redo last head change), Branch and PR (copy branch name, open/copy branch link, open/copy PR link), Lane (rename, archive, delete worktree, delete + branch), Integrate (merge/rebase onto base), Stash, Conflict (rebase/merge continue + abort, only when a conflict is in progress), Open (jump to Lanes git pane).
TimelineToolbar.tsxThe shared toolbar above both surfaces. Renders the surface toggle (Commits / Activity), the lane selector + LaneGitActionsMenu on the Commits surface, and the activity controls (view-mode toggle, scope selector, search, export-to-JSON, column gear, category/status/time-range/lane filter chips).
TimelineGraph.tsx (lazy)SVG-based per-lane swimlane graph for the Activity surface — used when viewMode === "graph".
TimelineListView.tsxTable-style activity list with column visibility driven by columns from the store.
TimelineCompactView.tsxDensity-optimized one-line-per-event list.
EventDetailPanel.tsxRight pane for the Activity surface: label, status, lane chip, pre/post SHA buttons, parsed metadata, jump-to-lane / open-chat links derived from metadata.

Shared types:

FileAdds / changes
apps/desktop/src/shared/types/git.tsGitPullMode (ff-only / rebase / merge), GitPullArgs, GitCreateTagArgs, GitResetCommitArgs, GitHeadChangeActionArgs. ListOperationsArgs gains a server-side status filter; ExportHistoryArgs keeps its own "all" sentinel by omitting the new field.
apps/desktop/src/shared/types/lanes.tsCreateLaneArgs.startPoint?: string — fork the new lane's branch from a specific commit/ref. Used by the "Create lane here" affordance in the commit context menu.
apps/desktop/src/shared/types/sync.tsAdds git.createTag, git.resetToCommit, git.undoLastHeadChange, git.redoLastHeadChange to SyncRemoteCommandAction so controllers (iOS, peer desktops) can issue the new commands.
apps/desktop/src/shared/ipc.tsNew channels: gitCreateTag, gitResetToCommit, gitUndoLastHeadChange, gitRedoLastHeadChange.

Surfaces

Commits

GitKraken-style DAG for the focused lane. The user picks a lane from the toolbar's lane select; the view runs git.listRecentCommits and git.listBranches against that lane's worktree, builds a column assignment with buildCommitGraphLayout, and renders a virtualized list with an SVG layer for nodes and parent/merge edges.

Each commit row shows the short SHA, optional HEAD and merge pills, up to two ref pills (branches whose lastCommitSha is this commit), the subject, the author name, and a relative timestamp. Right-click — or the actions strip in the detail panel — opens HistoryGitContextMenu with grouped actions; see historyGitActions.ts for the catalogue.

The search field above the list parses through filterCommitsForSearch in historySearch.ts. Bare text matches subject / SHA / author / parent / refs. Prefixed keys narrow the match: message:, author: (or @), commit: (or #), branch: / ref:, parent:, plus is:merge, is:local, is:pushed. The view automatically bumps the commit limit from 120 to 500 when a search is active so unloaded commits do not silently miss.

The Commits toolbar also exposes a LaneGitActionsMenu populated by buildHistoryLaneActions / groupHistoryLaneActions. That menu drives lane-scoped operations against window.ade.git.* + window.ade.lanes.* without leaving the History tab.

Activity

Unified operations feed combining recorded OperationRecord rows with synthesized rows from chat sessions and CTO snapshots (see historyActivitySources.ts). fetchEvents merges them in useTimelineStore, deduplicates by id, and sorts by startedAt descending before clamping to limit.

Three view modes (graph / list / compact) are selectable from the toolbar:

  • GraphTimelineGraph lays events out on per-lane swimlanes, draws cross-lane connectors, and renders each event with its category color + shape from eventTaxonomy.
  • ListTimelineListView is a tabular view honoring the user's column visibility (columns in the store; defaults below).
  • CompactTimelineCompactView collapses each event to a single dense line.

Default visible columns: Time, Graph, Event, Lane, Status, Duration. Hidden by default: Author, SHA. Toggles persist in the store (not localStorage — they reset when the store is recreated).

The toolbar's scope selector controls the importance threshold applied before any other filter:

ScopeAllowed importance
Keyhigh
Standard (default)high, medium
Detailedhigh, medium, low
Alleverything, including noise

Additional filters: lane multi-select, category multi-select, status multi-select (running / succeeded / failed / canceled), time-range (1h / today / week / month / all), free-text search across label / kind / lane name / status / metadata, and solo/hide chips per lane (visibility.soloedLaneIds, hiddenLaneIds).

Auto-refresh: every 4 s while any event has running status, gated on document.visibilityState === "visible" and window.focus, and suppressed on the Commits surface.

The export button calls ade.history.exportOperations with the current focusLaneId and (when exactly one status is selected) the chosen status, format json, default limit: 500. The desktop runs the save dialog locally and writes the file. The button shows "Export unavailable (headless)" when the preload bridge is missing.

What history captures

History records operations — discrete, typed actions that changed state — plus synthesized rows from the chat and CTO feeds that pretend to be operations for rendering purposes only. Synthesized rows are not written into the operations table; they are rebuilt on every refresh.

Operation kinds

Tracked kinds (canonical, recorded by services via operationService):

KindSourceMetadata
git_commitgitOperationsService.commit{ message, filesChanged, sha, reason, branchRef, baseRef }
git_checkout_branchgitOperationsService.checkoutBranch{ fromBranch, toBranch, mode }
git.mergegitOperationsService.merge{ fromBranch, conflicts }
git.rebasegitOperationsService.rebase, conflictService.rebaseLane{ ontoBranch, commitCount }
git_pushgitOperationsService.push{ remote, branch, commitCount }
git_push_force_with_leasegitOperationsService.push({ forceWithLease }){ remote, branch }
git_pullgitOperationsService.pull{ mode: "ff-only" | "rebase" | "merge" }
git_fetchgitOperationsService.fetch{ remote }
git_sync_merge / git_sync_rebasegitOperationsService.sync{ baseRef }
git_cherry_pickgitOperationsService.cherryPickCommit{ commitSha }
git_revertgitOperationsService.revertCommit{ commitSha }
git_tag_creategitOperationsService.createTag{ commitSha, tagName, annotated }
git_reset_soft / git_reset_mixed / git_reset_hardgitOperationsService.resetToCommit{ commitSha, mode }
git_undo_head_changegitOperationsService.undoLastHeadChange{ undoneOperationId, undoneOperationKind, redoHeadSha, targetHeadSha }
git_redo_head_changegitOperationsService.redoLastHeadChange{ redoneUndoOperationId, targetHeadSha }
git_stash_push / _apply / _pop / _drop / _cleargitOperationsService.stash*{ ref?, message?, includeUntracked? }
git_rebase_continue / _abort / git_merge_continue / _abortgitOperationsService.rebase* / merge*{}
pack_update_lanepackService.refreshLane{ reason, trigger }
pack_update_projectpackService.refreshProject{ reason, trigger }

Synthesized kinds (Activity surface only)

These are built in historyActivitySources.ts and rendered as operations even though no row lands in the database. They show up in the activity feed and the detail panel; they will never appear in ade.history.exportOperations output.

KindSourceMetadata highlights
chat.sessionagentChat.list({ includeAutomation: true })sessionId, title, provider, model, chatStatus, awaitingInput, automationId
cto.sessioncto.getState({ recentLimit }).recentSessionssessionId, summary, provider, model, capabilityMode

Status

running | succeeded | failed | canceled. running is set at start and transitions on finish. Canceled operations are bracketed with finish({ status: "canceled" }) by the caller. The toolbar's status filter and the new ListOperationsArgs.status argument both apply this field; the export path uses its own status (with an "all" sentinel) and falls back to client-side filtering when a single status is requested via the toolbar.

SHA transitions

Every git operation records preHeadSha and postHeadSha. The undo and redo paths in gitOperationsService rely on this. undoLastHeadChange searches recent head changes for the current lane, skips git_undo_head_change and git_checkout_branch, requires the stored metadata.branchRef to match the lane's current branch, rechecks that the lane branch has not changed after selection, verifies current HEAD still equals the selected operation's postHeadSha, then runs git reset --hard preHeadSha. redoLastHeadChange inverts that, reading the redoHeadSha stamped into the latest undo's metadata. The CommitDetailPanel.relatedEvents view also keys off this pair: any operation whose preHeadSha or postHeadSha matches the focused commit is surfaced alongside the commit.

Retention

The operations table is pruned by operationService.pruneOld(), invoked once per project at runtime startup (see main.ts; failures are logged as operation_prune_failed and swallowed so a prune error never blocks boot). It applies two caps, both scoped to the current project_id and both of which never evict in-flight (running) rows:

  • Age cap — deletes finished operations whose started_at is older than maxAgeDays (default 60).
  • Count cap — keeps only the newest maxRows finished operations (default 5000), deleting the rest.

This keeps the timeline DB bounded on long-lived projects without losing recent history or orphaning operations that are still running.

Other history-adjacent tables

Several features own their own history-style tables. These are not queried via ade.history.* but contribute to the broader picture.

Terminal sessions

terminal_sessions (schema in kvDb.ts):

  • id, lane_id, pty_id, tracked, pinned, manually_named
  • goal, tool_type, title
  • started_at, ended_at, exit_code, status
  • transcript_path -- filesystem path to the persisted transcript
  • head_sha_start, head_sha_end -- git HEAD bracketing the session
  • last_output_preview, last_output_at, summary
  • resume_command, resume_metadata_json -- resume info for CLI tools (Claude Code, Codex, Cursor) so sessions can be picked up after exit.

Sessions are owned by sessionService.ts; their full transcript is on disk at transcript_path. Terminal sessions do not synthesize rows into the activity feed yet — chat sessions do.

Checkpoints (Phase 8)

checkpoints (schema in kvDb.ts):

  • Immutable SHA snapshots at session boundaries.
  • Carry diff stats and linked pack event IDs.
  • Surfaced internally via packService compatibility paths but not through a public ade.packs.* IPC.

Pack events (Phase 8)

pack_events:

  • Append-only log of pack state changes (checkpoint created, narrative updated, conflict detected, etc.).
  • Event-specific payload stored as JSON.

Pack versions (Phase 8)

pack_versions, pack_heads:

  • Track pack content hashes and which version is "live" per pack key.
  • Used by deterministic-context exports.

AI usage log

ai_usage_log:

  • Per-turn token and cost tracking across providers.
  • Consumed by the budget/usage dashboards, not the history UI.

IPC surface

Defined in apps/desktop/src/shared/ipc.ts, handled in apps/desktop/src/main/services/ipc/registerIpc.ts.

History

ChannelArgsPurpose
ade.history.listOperationsListOperationsArgs ({ laneId?, kind?, status?, limit?, offset? })Query operations with optional filters and pagination. status is applied server-side. Default limit 300, max 1000.
ade.history.exportOperationsExportHistoryArgs ({ format: "csv" | "json"; laneId?, kind?, status?, limit? })Export filtered history as CSV or JSON via a save dialog. status accepts OperationRecord["status"] | "all"; "all" skips both server-side and client-side status filtering. Cancellation returns { cancelled: true }.

Git (used by the History toolbar + commit context menu)

ChannelArgsNotes
ade.git.listRecentCommits{ laneId, limit? }Limit is clamped to [1, 500] (was 200). The Commits view uses 120 by default and bumps to 500 on search or near-bottom scroll.
ade.git.listBranches{ laneId }Used to overlay branch refs on commit rows.
ade.git.listCommitFiles{ laneId, commitSha }Drives the detail panel file list and the "Copy patch" action (capped at 50 files).
ade.git.getCommitMessage{ laneId, commitSha }Full commit message body (lazy).
ade.git.getOriginRemote{ laneId }Used to build GitHub commit / branch URLs.
ade.git.getOpenPrForBranch{ laneId }Used by "Open branch PR" and "Copy PR link".
ade.git.checkoutBranch{ laneId, branchName, mode, startPoint? }"Create branch here" passes the commit SHA as startPoint.
ade.git.cherryPickCommit{ laneId, commitSha }Records git_cherry_pick.
ade.git.revertCommit{ laneId, commitSha }Records git_revert.
ade.git.createTagGitCreateTagArgs ({ laneId, commitSha, tagName, message? })Lightweight tag when message is omitted, annotated otherwise. Records git_tag_create.
ade.git.resetToCommitGitResetCommitArgs ({ laneId, commitSha, mode: "soft" | "mixed" | "hard" })Records git_reset_<mode>.
ade.git.pullGitPullArgs ({ laneId, mode?: "ff-only" | "rebase" | "merge" })Default ff-only. Records git_pull with metadata.mode.
ade.git.undoLastHeadChangeGitHeadChangeActionArgs ({ laneId })Resets HEAD to the previous successful head-changing op's preHeadSha. Fails if HEAD has moved since. Records git_undo_head_change.
ade.git.redoLastHeadChangeGitHeadChangeActionArgs ({ laneId })Restores HEAD to the most recent undo's redoHeadSha. Fails if HEAD has moved since the undo. Records git_redo_head_change.

All lane-scoped ade.git.* reads and mutations validate the lane worktree root before running provider Git commands. A missing or stale worktree path throws the same "restore or recreate the lane worktree" error for history reads, commit-message generation, branch/stash/sync metadata, and mutating Git actions.

The conflict-resume channels (gitRebaseContinue, gitRebaseAbort, gitMergeContinue, gitMergeAbort) accept either a bare string laneId (legacy) or { laneId } (preferred); the preload normalizes via normalizeLaneIdArg before dispatching.

Lanes (used from the commit context menu)

ChannelArgsNotes
ade.lanes.createCreateLaneArgs with startPoint"Create lane here" forks the new branch from commit.sha instead of from the parent lane's tip.

Operation recording pattern

Every operation that should appear in history follows:

const op = operationService.start({
  laneId,
  kind: "git_commit",
  preHeadSha,
  metadata: { reason, branchRef }
});

try {
  const result = await doTheThing();
  const postHeadSha = await getHeadSha();
  operationService.finish({
    operationId: op.operationId,
    status: "succeeded",
    postHeadSha,
    metadataPatch: { message: result.message, filesChanged: result.files.length }
  });
} catch (error) {
  operationService.finish({
    operationId: op.operationId,
    status: "failed",
    postHeadSha: await getHeadSha(),
    metadataPatch: { error: error.message }
  });
  throw error;
}

For instantaneous operations (no async work between start and finish), operationService.recordCompleted() wraps both calls.

See gitOperationsService.ts for the canonical implementation (runLaneOperation / runTrackedOperation). That helper also emits lane-changed and HEAD-changed events so dependent services can invalidate caches.

What is NOT in history

Deliberately excluded from the persisted operations table; some of these are surfaced on the Activity surface as synthesized rows:

  • Individual tool calls during a session.
  • UI navigation events.
  • PR comment polling / check re-runs (captured in PR module).
  • Context-pack generation telemetry.
  • AI token usage (ai_usage_log).

Fragile and tricky wiring

  • Start/finish pairing. Operations without a finish stay running forever. Every code path that calls start must have a matching finish (success or failure). gitOperationsService wraps both in runLaneOperation; new call sites should adopt that pattern rather than calling start/finish manually.
  • Pre/post HEAD capture timing. preHeadSha is captured immediately before the operation; postHeadSha immediately after (even on failure). If the operation crashes the process between the two reads, the row is left with running status and null postHeadSha -- it must be reconciled on next startup or left as a tombstone.
  • Undo/redo HEAD guard. Both undoLastHeadChange and redoLastHeadChange re-read HEAD before they touch git; if HEAD has moved since the operation they target, they refuse. This prevents git reset --hard from clobbering uncommitted work the user did after the original operation.
  • Metadata merge on finish. operationService.finish merges the metadataPatch into the existing metadata via spread. Nested objects are overwritten wholesale, not deep-merged.
  • listOperations.status vs exportOperations.status. The list API takes a single concrete status; the export API additionally accepts "all". The toolbar passes a single status to export only when exactly one is selected, falling back to a client-side filter for multi-select cases. Large projects with heavy filters can hit the 1000-row limit before the filter applies.
  • laneName join. list() left-joins lanes.name. Deleted or archived lanes still show up with laneName: null instead of the original name -- good for stable history, surprising for the UI.
  • CSV export escaping. The export path embeds metadata JSON; CSV escaping must survive nested quotes. Validate with round-trip tests when adjusting export formats.
  • Synthesized rows do not export. Chat / CTO rows are renderer-side only. They appear in the Activity feed and the detail panel but never in ade.history.exportOperations output. Treat the export as the operations table, not the unified view.
  • Commit graph virtualization edge filter. CommitHistoryView filters edges to ones touching the current virtual window (±2 rows) to keep SVG cheap on tall histories. If a long edge skips more than two rows of virtual scroll, it will pop in and out at the seam — by design, not a bug.
  • startPoint validation. laneService.create resolves startPoint with git rev-parse --verify. An invalid ref throws Start point not found for new lane: … synchronously; callers in historyGitActions.create_lane rely on that error to surface a notice.
  • Pack/checkpoint data exists but is hidden. Phase 8 tables are populated but the History UI does not surface them. Any new IPC that exposes them must also respect the visibility/focus polling guards already in place for operations.

Detail doc

  • Recording and Export -- how git/PR/pack services emit operations, how head-change undo/redo bracket the user-visible recovery actions, and how the export flow writes CSV/JSON.
  • Lanes README -- CreateLaneArgs.startPoint is documented there alongside the rest of the lane creation flow.
  • Sync remote commands -- the new git head-change / tag / reset actions are exposed to iOS and peer desktops through SyncRemoteCommandAction.
  • Chat README -- chat transcript persistence is separate from the operations timeline but parallel in intent, and chat sessions also synthesize Activity feed rows.
  • Agents README -- CTO session logs are tracked in cto_session_logs; the CTO snapshot synthesizes rows into the Activity feed.