Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9bdb930
[tm-auto-update-updates-wvb] Fix GitHub per-branch PR selection so a …
0101 Jul 27, 2026
4310c93
tm-auto-update-updates-frj Never queue the same pending prompt twice
0101 Jul 27, 2026
e5c9637
[tm-auto-update-updates-bdu] Add a durable accepted-sync-revision store
0101 Jul 27, 2026
a7e0096
tm-auto-update-updates-a11 Suppress repeat sync prompts for an alread…
0101 Jul 27, 2026
6ce154a
tm-auto-update-updates-880 Stop auto-syncing a branch once its PR is …
0101 Jul 27, 2026
cf1c9e6
[tm-auto-update-updates-7sl] Add the mechanical git sync operation
0101 Jul 27, 2026
77f4e62
[tm-auto-update-updates-mie] Model the auto-sync target so session op…
0101 Jul 27, 2026
bdc58ad
tm-auto-update-updates-776 Add live per-branch open-PR detection
0101 Jul 27, 2026
b7ec463
tm-auto-update-updates-776 Clarify fork-source PR lookup
0101 Jul 27, 2026
3ddd1bf
[tm-auto-update-updates-7cp] Add a bounded non-force branch push
0101 Jul 27, 2026
65a8222
[tm-auto-update-updates-gom] Coalesce duplicate survivors during queu…
0101 Jul 27, 2026
4f71ab6
Adapt the mechanical sync to the ComparisonContent rename
0101 Jul 27, 2026
43a2341
[tm-auto-update-updates-2si] Retry an expired accepted revision in th…
0101 Jul 27, 2026
923efd2
tm-auto-update-updates-3fw Reject auto-sync enable when merge lands d…
0101 Jul 27, 2026
e403160
tm-auto-update-updates-5ct Detect fork-origin GitHub PRs in the live …
0101 Jul 27, 2026
b06d7e9
[tm-auto-update-updates-5ct] Clarify GitHub fork head destination
0101 Jul 27, 2026
a3f083d
[tm-auto-update-updates-vwl] Keep accepted claims synchronized with d…
0101 Jul 27, 2026
656e343
[tm-auto-update-updates-vwl] Clarify acceptance generation invariant
0101 Jul 27, 2026
944f530
[tm-auto-update-updates-vwl] Scope accepted-record clearing to one ac…
0101 Jul 27, 2026
a7d8c99
[tm-auto-update-updates-mq9] Replace prohibited ref cells in auto-syn…
0101 Jul 27, 2026
9152c47
tm-auto-update-updates-kup Use the branch remote's effective push URL…
0101 Jul 27, 2026
49c0890
tm-auto-update-updates-08x Run mechanical sync when no session is ope…
0101 Jul 27, 2026
48ef051
tm-auto-update-updates-08x Clarify branch-bound mechanical sync
0101 Jul 27, 2026
e834d61
[tm-auto-update-updates-77u] Keep mechanical sync and push bound to o…
0101 Jul 27, 2026
34b3389
[tm-auto-update-updates-77u] Bind the mechanical merge to the observe…
0101 Jul 27, 2026
1adf3a5
tm-auto-update-updates-1n7 Reject an option-like push remote name
0101 Jul 28, 2026
1979ebd
[tm-auto-update-updates-c82] Verify the head owner when classifying a…
0101 Jul 28, 2026
03a6bec
tm-auto-update-updates-1pl Keep the auto-sync operation guard held un…
0101 Jul 28, 2026
74ec5dc
tm-auto-update-updates-8jp Keep open-PR classification pure and log a…
0101 Jul 28, 2026
3e3085d
tm-auto-update-updates-6gd Split the two oversized server modules the…
0101 Jul 28, 2026
56ae855
Simplify auto-sync: one dedup layer, cached PR openness
0101 Jul 28, 2026
744f1a2
Merge branch 'main' of https://github.com/0101/treemon
0101 Jul 28, 2026
1957fbe
Fix the 11 confirmed focused-review findings
0101 Jul 28, 2026
aecd0c6
Address Copilot review: hooks, dirty re-probe, future acceptance stamps
0101 Jul 28, 2026
488026d
Sync a worktree whose session is open but not working
0101 Jul 29, 2026
116b4d9
Select the auto-sync target through the existing session collapse
0101 Jul 29, 2026
6115449
Name worktreeApi's dependencies instead of passing ten positionally
0101 Jul 29, 2026
6058293
Make the in-flight auto-sync toggle visible without animation
0101 Jul 29, 2026
08003c1
Merge branch 'main' of https://github.com/0101/treemon
0101 Jul 29, 2026
6b7fc8d
Log converging the two persistence stacks as a backlog candidate
0101 Jul 30, 2026
35fe1a0
Merge commit 'cb953ed30a005caea96f0cb8c4327482aed2f619' into auto-upd…
0101 Jul 30, 2026
4d6e690
Wait for a free worktree instead of prompting a busy one
0101 Jul 30, 2026
f256b89
Address focused-review findings on the wait-for-idle sync
0101 Jul 30, 2026
52dc3a3
Merge commit 'a6e63fc382258feb31a6910204a848840d808cea' into auto-upd…
0101 Jul 31, 2026
70bcc2f
Split the busy verdict out of the delivery address type
0101 Jul 31, 2026
931b060
Let the auto-sync glyph turn regardless of motion preference
0101 Jul 31, 2026
9c1f17b
Re-read a worktree as soon as Treemon's own sync moves it
0101 Jul 31, 2026
e419956
Bend the auto-sync arrows into a circle so the spin reads
0101 Jul 31, 2026
a5ff493
Clear the accepted-revision record for externally removed worktrees
0101 Jul 31, 2026
67aa553
Merge commit '4a5e961fcd6a657b0c5595702bc68807ce230b59' into auto-upd…
0101 Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/spec/beads-overview-band.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ activity is **derived** from the skill via the pure Shared classifier (no separa

Adding record fields breaks every construction site (no default record values in F#) — each
type-growth task must update all sites (`DemoFixture.fs` ×8, `WorktreeApi.fs` mapping,
`RefreshScheduler.fs`, client/server `IWorktreeApi` impls, test fixtures) in the same change to keep
`SchedulerState.fs`, client/server `IWorktreeApi` impls, test fixtures) in the same change to keep
the solution compiling (no compat shims, per house rules).

### Client aggregation + band
Expand Down
8 changes: 4 additions & 4 deletions docs/spec/canvas-interaction-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ guard covers only Treemon's own spawns.
An AgentDoc interaction with no reachable author is queued without launching, because a new session
would not be that document's author.

Queued messages retain the existing cap of 10 and five-minute TTL. On drain, an AgentDoc prompt goes
only to its recorded owner, so ownership changes made while a message waits are honored. A SystemView
prompt stays bound to the session resolution picked, if any; when nothing was reachable it drains to
the next identified registration — the session the queue caused to launch. An anonymous
Queued messages retain the existing cap of 10 and five-minute TTL. On drain, an AgentDoc
prompt goes only to its recorded owner, so ownership changes made while a message waits are honored.
A SystemView prompt stays bound to the session resolution picked, if any; when nothing was reachable
it drains to the next identified registration — the session the queue caused to launch. An anonymous
(session-less) registration never drains either kind.

### Persistence and Cleanup
Expand Down
5 changes: 3 additions & 2 deletions docs/spec/canvas-pane.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ A `SystemView` drives its own updates, so it needs neither morph nor the author
- The Elmish client accepts only messages from `http://127.0.0.1:5002`, validates the payload shape, and turns it into Elmish messages.
- The client forwards valid payloads through Fable.Remoting with `sendCanvasMessage`.
- The server forwards live messages by HTTP POST to the registered bridge `/inject` endpoint.
- `SessionBridge` wraps the payload in a typed `{kind:"canvas",prompt}` envelope; the extension maps that kind to the unchanged `[canvas] {payload}` session prompt and sends it through its serialized `enqueueSend` chain.
- `SessionBridge` wraps the payload in a typed `{kind:"canvas",prompt}` envelope; the extension maps that kind to the unchanged `[canvas] {payload}` session prompt and sends it through its serialized `enqueueSend` chain. Identical sends already queued but not started are coalesced; once `session.send` starts, the same payload may be queued again.
- When the reporting extension later publishes that `[canvas]` prompt as session activity, the
dashboard collapse projects it through `UserMessageFormatting`: the first-party
`canvas-selection` action displays its human-readable `request`, other known actions get concise
Expand Down Expand Up @@ -259,7 +259,7 @@ changed rows already use).
| `src/Server/IdiomorphScript.fs` | Vendored idiomorph runtime (library only — the controller lives in `CanvasMorphScript.fs`) |
| `src/Server/CanvasMorphScript.fs` | Morph controller injection: the embedded `canvas-morph.js` source plus the `canvas-updated` highlight style |
| `src/Extension/canvas-morph.js` | Morph controller — reacts to `content-updated`, morphs the body via idiomorph, and marks the blocks the morph changed |
| `src/Extension/extension.mjs`, `session-prompt.mjs` | Session bridge registration, `/inject` server, typed prompt-transport decoding, heartbeat, and reconnect backoff |
| `src/Extension/extension.mjs`, `session-prompt.mjs`, `send-queue.mjs` | Session bridge registration, `/inject` server, typed prompt-transport decoding, serialized send queue with pending-duplicate coalescing, heartbeat, and reconnect backoff |
| `src/Extension/skill/SKILL.md` | Authoring contract for agent-created canvas docs |

## Decisions
Expand All @@ -275,6 +275,7 @@ changed rows already use).
- **`Model`+`Msg` lifted into `AppTypes.fs`** — the Elmish `Model` and `Msg` types, plus the shared plumbing the canvas update arms need (`worktreeApi`, `findWorktree`, `saveCollapsedReposCmd`), live in `src/Client/AppTypes.fs` (compiled after `CanvasState.fs`, before `CanvasUpdate.fs`/`App.fs`). This is a pure type/value relocation that creates a compile-order seam: the canvas update arms are extracted into `CanvasUpdate.fs` (compiled between `AppTypes.fs` and `App.fs`) without a cyclic reference, while `update` remains a single function in `App.fs` (no sub-`Msg`/`Cmd.map` split). Consumers that previously reached these via `open App` (three test files) add `open AppTypes`; nothing references them by `App.`-qualified name (the activity helper once at `App.computeActivityLevel` now lives in `ActivityState.fs`).
- **Canvas `update` arms extracted into `CanvasUpdate.fs`** — the canvas `update`-arm bodies (`ToggleCanvasPane`, `SetCanvasPosition`, `SelectCanvasDoc`, `OpenCanvasDoc`, `ArchiveCanvasDoc`, `ArchiveCanvasDocResult`, `ShareCanvasDoc`, `ShareCanvasDocResult`, `ClipboardWriteResult`, `DismissShareNotice`, `NavigateCanvasDoc`, `CanvasMessageReceived`, `CanvasSendResult`, `DismissCanvasMessageError`, `LaunchCanvasSession`, `MorphActiveDoc`, `MorphComplete`), the shared canvas helpers (`activeVisibleDoc`, `isKnownCanvasDoc`, `markVisibleDocCmd`), and the `messageListener` subscription glue move to `src/Client/CanvasUpdate.fs` (compiled after `AppTypes.fs`, before `App.fs`). Each canvas arm in `App.fs` is now a one-line delegation (`| ToggleCanvasPane -> CanvasUpdate.toggleCanvasPane model`). This is **body extraction**, not a `Cmd.map` sub-component split: `update` stays a single function over the flat `Msg`, and each helper takes the whole `Model` and returns `Model * Cmd<Msg>` (data-last `model` parameter). `FocusOverviewCard` stays inline in `App.fs` — it is an overview-card focus arm, not a doc/morph/archive arm, and is outside the moved set. The `isKnownCanvasDoc` consumer in the tests adds `open CanvasUpdate`. Realized line counts: `App.fs` 2015 → 1861 (canvas update logic, ~150 lines, removed); it does **not** reach `main` size (1635) because the canvas **view** code (`canvasEventEntry`, `canvasEventLog`, `focusedWorktreeCanvasDoc`, and the pane-view dispatch wiring) and the canvas params threaded through `worktreeCard`/`renderCard`/`repoSection` remain — a separate view extraction, since completed. The stale "~430 lines / main size" estimate in the original task conflated this deferred view extraction with the update-arm extraction; only the update arms are in scope here. The structural gate (each canvas arm is a one-line delegation; bodies live in `CanvasUpdate.fs`) is what proves the extraction.
- **Canvas model slice as a nested record** — the canvas Model-field group is extracted as a nested record `Canvas: CanvasState.CanvasState` on `App.Model` (mirroring the existing `CreateModal`/`ConfirmModal` nesting precedent). The four pure helpers (`touchVisitedDoc`, `canvasDocKind`, `activeVisibleDoc`, `markVisibleDocCmd`) plus the `MaxLiveIframes` literal live in `src/Client/CanvasState.fs` (compiled before `App.fs`); they take pure slices (`repos`/`focused`/`activeCanvasDoc`) rather than the whole `Model`, and `markVisibleDocCmd` is parameterized over the message constructor so the module needs no concrete `Msg` type. Thin `App.fs` wrappers keep `update` call sites unchanged. This is field-path nesting only — **not** the larger `Cmd.map` sub-component split (no sub-`Msg`/sub-`update`; `update` stays one function), which is out of scope.
- **Exact-equality duplicate coalescing** — the extension queue compares payloads exactly on transport kind and session prompt text. There is no revision metadata and no content normalization, and a payload stops being pending the moment it is handed to `session.send` (`createSendQueue`, key deleted before the call so a rejected send leaves nothing stale). Suppression therefore only ever removes a message that would repeat an undelivered one. The server queue does not coalesce: auto-sync's durable accepted record already prevents a repeat prompt at the source.
- **Cross-platform canvas doc path** — `CanvasPrompt.continueWorking` (`src/Shared/Types.fs`) builds the canvas-session launch path with forward slashes (`{worktree}/.agents/canvas/{filename}`), which resolve correctly on Windows, Linux, and macOS. `System.IO.Path.Combine` is deliberately not used because `src/Shared` is Fable-compiled to JavaScript and cannot reference `System.IO`.

## Related Specs
Expand Down
3 changes: 2 additions & 1 deletion docs/spec/future/code-improvements.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ its own worktree. This file is the entry point; detailed designs live in their o
| 4 | **Process: guard against spec drift** — a lightweight check (or review rule) that flags `Key Files` references to moved/renamed modules so docs can't silently rot after refactors | — | Idea |
| 5 | **Survey other large modules** — *investigated.* `RefreshScheduler.fs` and `WorktreeApi.fs` both mix concerns; the concrete split is broken out as #8 below (and #7, the `GlobalConfig` extract, now *Done*). `WorktreeDiff.fs` is tracked separately as #13. (Strict-FP smells are already clean: no stray `let mutable`/loops/`null` in production; `Dictionary` only at cache/registry boundaries.) | — | Done (survey) |
| 6 | **Remoting CSRF / Origin hardening** — pipeline-level Origin/Referer check so a cross-origin browser page can't drive the unauthenticated loopback Fable.Remoting API (covers the dangerous pre-existing process-launching endpoints, not just watched-roots) | `docs/spec/remoting-csrf-hardening.md` | **Done** — shipped on `quicklaunch` (`HttpSecurity.csrfGuard`) |
| 8 | **Split `RefreshScheduler.fs`** — the scheduler module also carries the `PerRepoState`/`DashboardState`/`StateMsg` state slice (everything above `SchedulerServices`) and an embedded `CanvasWatchers` filesystem-watcher module at the end of the file. Lift one or both out of the scheduling loop into their own files (vertical-slice seam, like canvas/mascot/activity). Behavior-preserving but a heavy ripple: `WorktreeApi.fs` alone reaches into those state types throughout. | — | Idea (from survey) |
| 8 | **Split `RefreshScheduler.fs`** — the `DashboardState`/`StateMsg`/`processMessage` state slice now lives in `SchedulerState.fs`; the embedded `CanvasWatchers` filesystem-watcher module (~110 L) still sits inside the scheduling loop and can follow the same seam. | — | Partly done (state slice extracted) |
| 10 | **Share the `WorktreeDiffTests` git fixture** — the suite creates a real repository per test in `[<SetUp>]` (`initRepoOnMain`), making it the single largest contributor to Fast-suite runtime. Build the repository once in `[<OneTimeSetUp>]` and give each test its own branch or clone. | — | Idea |
| 11 | **Fast-suite runtime exceeds its documented budget** — AGENTS.md advertises `<60s` for `Category=Fast`; the suite has been several times that for a while, dominated by browser-driven fixtures (`DashboardTests`, `CreateWorktreeServerTests`, `ArchiveTests`) plus #10. Either bring the suite back under budget or correct the figure, because a stale number stops it acting as a gate. | — | Idea |
| 12 | **Pin a SystemView to a chosen session** — SystemView interactions resolve per interaction to the worktree's most recently active live session, so with two agents alternating the target follows whoever spoke last. A user-visible pin would make it sticky. Deliberately out of scope when the resolution rule was adopted: storage would exist solely to hold rare, uncontended overrides. | `docs/spec/canvas-interaction-routing.md` | Idea |
| 13 | **Split `WorktreeDiff.fs`** — it exceeds the 1,000-line limit in `review/rules/file-size-limit.md`. Extract the entry parsing and untracked-content handling so the file owns comparison orchestration and result types only. | `docs/spec/worktree-diff-viewer.md` | Idea |
| 14 | **Process: route review findings that need a decision to a human** — focused-review marks some findings as needing a product decision rather than a fix. Implementing the reviewer's suggested mechanism instead of answering the question has produced whole subsystems that were later removed. Make that state a stop in the review→fix flow. | — | Idea (process) |
| 15 | **One persistence mechanism instead of two** — durable state is split across two stacks: SQLite (`SessionActivityStore`'s `session_status` + `activity_events`, WAL, indexed) and per-port JSON files behind `PersistentStore` + `JsonStore` (`MergedPrStore`, `AutoSyncStore`, `CanvasDocOwnership` in `data/*-{port}.json`). The split is defensible per store — the SQLite tier is an append-heavy event log with time-range queries, the JSON tier is small point-read/point-write maps — but two mechanisms means two sets of failure modes, load paths, and hand-written parse/validate code. Converging the runtime-state stores onto SQLite would leave one storage story and delete the bespoke serializers. Note the usual argument for the move does *not* apply: `JsonStore.tryPersist` already writes temp-then-`File.Move`, so durability is not the gap. Move all three together — converting one leaves it the odd store out, which is worse than today's consistency. | — | Idea |


> Add new candidates here as they surface (often from focused-review findings). Keep the list
Expand Down
4 changes: 2 additions & 2 deletions docs/spec/overview-activity-history.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ missed time, late inputs, or downtime.
a gap. Within that bounded skew, mailbox order is authoritative: updates ordered before the
barrier may appear in `B`, while updates ordered after it cannot.
- For boundary `B`, the capture uses `B` as `RepoAssemblyInputs.Now`, one immutable
barriered `RefreshScheduler.GetState` result, one set of assembly inputs, one
barriered `SchedulerState.GetState` result, one set of assembly inputs, one
`WorktreeApi.assembleRepos` result, and one `OverviewData.aggregate` result. Wall-clock reads
performed later in the attempt cannot change the projection time.
- Tasks and agents are reduced from that same `Overview` value and committed atomically as one
Expand Down Expand Up @@ -173,7 +173,7 @@ start/cancel/await lifecycle machinery while retaining independent cadence polic
| `src/Server/OverviewSnapshotStore.fs` | Direct snapshot schema, migration, retention, and bounded reads |
| `src/Server/OverviewSnapshotCapture.fs` | Serial capture scheduling, shared projection, and atomic capture |
| `src/Server/WorktreeApi.fs` | Shared repo assembly and bounded history query |
| `src/Server/RefreshScheduler.fs` | Immutable live state and `GetState` |
| `src/Server/SchedulerState.fs` | Immutable live state and `GetState` |
| `src/Server/Program.fs` | Capture lifecycle |
| `src/Client/App.fs` | Window state, fetching, and refresh cadence |
| `src/Client/OverviewChart.fs` | Stepped chart geometry and hover |
Expand Down
22 changes: 13 additions & 9 deletions docs/spec/session-status-push.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,10 @@ titles, context usage, and session resume all use this shared state; no session-
to several activity groups at once.
- `CodingToolSince` is captured when the collapsed worktree status changes and remains stable while
Idle heartbeats advance `last_seen`.
- Auto-sync selects the active open winner, then the greatest-`UpdatedAt` open Idle session, and
only then a retained identity when no session is open. This delivery-aware ordering avoids
launching a second CLI while an existing bridge can accept the prompt.
- Auto-sync defers entirely while any open session is mid-turn or has been idle for less than
`settleWindow` (30 s). Otherwise it takes the greatest-`UpdatedAt` open session that has settled,
and only then a retained identity when no session is open. That ordering keeps any fallback prompt
on an existing bridge rather than launching a second CLI.

### Context usage, resume, and restart

Expand Down Expand Up @@ -193,10 +194,13 @@ candidate set before collapsing it. The representative's own `LastSeen` still de
contributes an open dot, while its `UpdatedAt` keeps it eligible for footer and resume ownership.

The remoting contract exposes `toggleAutoSync`. When enabled and the branch falls behind, `AutoSync`
uses the same live and retained session state to select a target identity, then sends a typed
agent-prompt through `SessionBridge`. A matching live registration receives it directly; transient
delivery failure queues it for retry, while no live target triggers the guarded fallback launch.
The passive reporting extension never sends prompts.
uses the same live and retained session state but preserves whether the selected identity is busy,
settled-idle, or offline. A session mid-turn — or one that went idle within the settle window — makes
the worktree busy, and the observation is deferred without delivering anything. Otherwise — no
session, or one that has settled or is waiting on its user — AutoSync attempts the bounded mechanical
path defined in `docs/spec/worktree-monitor.md`, and uses the idle session, retained identity, or a
guarded launch only when that path requires agent fallback; transient delivery failure queues that
prompt for retry. The passive reporting extension never originates prompts.

The projection keeps the activity source through the `AgentActivity` union and exposes every open
session for status/context rendering. Overview snapshot capture uses the same live session
Expand All @@ -220,7 +224,7 @@ projection but persists the complete count-only aggregate independently; see
| Context usage | Persist the last-known gauge and ordering timestamp; do not append it to activity events. |
| Persistence | Store latest session state plus idempotent accepted events in SQLite WAL. |
| Overview history | Capture canonical direct snapshots every 30 seconds; never reconstruct from activity events. |
| Auto-sync | Prefer an active/open bridged session, then retained identity only when no session is open; launch only when delivery has no live target. |
| Auto-sync | Wait while any open session is working or has not settled; otherwise prefer the settled open bridged session, then retained identity only when no session is open; launch only when delivery has no live target. |
| Resume | Query durable most-recent activity identity, not the bounded live cache or heartbeat recency. |
| Explicit close | Not required; heartbeat expiry handles clean exit and crashes uniformly. |
| Window state | Keep terminal/window `HasActiveSession` separate from push-session openness. |
Expand All @@ -237,7 +241,7 @@ projection but persists the complete count-only aggregate independently; see
| `src/Server/SqliteStorage.fs` | Shared SQLite UTC timestamp encoding/parsing and immutable reader draining. |
| `src/Server/SessionActivityStore.fs` | Session persistence, additive migration, idempotent event append, representative queries, and retention. |
| `src/Server/CodingToolStatus.fs` | Per-worktree collapse, heartbeat-independent activity/footer projection, and resume lookup. |
| `src/Server/RefreshScheduler.fs` | Live session state and `CodingToolSince` transitions. |
| `src/Server/SchedulerState.fs` | Live session state and `CodingToolSince` transitions. |
| `src/Server/WorktreeApi.fs` | Card assembly, retained-session merge, direct snapshot history API, and resume command wiring. |
| `src/Server/SessionBridge.fs` | Session registration, targeted prompt delivery, retry queue, and bridge liveness. |
| `src/Server/AutoSync.fs` | Delivery-aware session selection and guarded sync-prompt fallback launch. |
Expand Down
2 changes: 1 addition & 1 deletion docs/spec/user-idle-detection.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ The `activityDetection` Elmish subscription registers DOM event listeners (mouse

On activity level transitions, `reportActivity` is called to inform the server.

### Server-Side (`src/Server/RefreshScheduler.fs`)
### Server-Side (`src/Server/SchedulerState.fs`, `src/Server/RefreshScheduler.fs`)

`DashboardState` gets a `ClientActivity: ActivityLevel` field, defaulting to `Idle`. New `ReportClientActivity` message updates it. `intervalOf` becomes a function of `(ActivityLevel * RefreshTask)` with explicit intervals per combination (no multiplier math).

Expand Down
Loading
Loading