Commit Graph

3713 Commits

Author SHA1 Message Date
Peter Steinberger 099d6351b3 refactor(sessions): canonical lineage model — creation provenance, fork ancestry, generation chain, typed row contract (#111861)
* fix(sessions): preserve spawn/fork lineage across implicit daily/idle rollover

* refactor(sessions): canonical creation model with forkSource ancestry and generation chain

* feat(sessions): stamp creation provenance across all creation paths and emit created events

* refactor(gateway): lock lineage patching, split control from navigation, add typed session-row contract

* docs(gateway): document creation provenance stamping and lineage patch tightening

* fix(sessions): keep provenance proof-only on wire fallbacks and strip node-local lineage from cron continuations

* fix(gateway): never journal a created event for session adoption

* fix(gateway): keep post-create work on adoption while gating the created event

* fix(sessions): restore trusted ensure-main provenance and stamp navigation parent at spawn

* fix(sessions): allow parentSessionKey through the direct child spawn patch

* fix(ci): break type cycles, satisfy export scan, regenerate Swift protocol models

* refactor(sessions): replace createdBy with createdActor

* fix(protocol): export session row type

* fix(sessions): preserve proven creation provenance

* fix(sessions): close lineage creation gaps

* test(sessions): align atomic spawn lineage coverage

* test(sessions): widen transcript search reconcile wait

* fix(sessions): stamp reset-created rows

* test(sessions): keep reset provenance coverage focused

* fix(sessions): journal chat-created rows

* test(ci): anchor release skill reads to repo

* test(ci): avoid cached module paths
2026-07-22 22:54:31 -04:00
Jason (Json) 57473e6f7e fix(ui): keep user footer actions together (#112836) 2026-07-22 20:03:58 -06:00
Peter Steinberger 6c5713b6f1 refactor(ui): move sidebar attention into controller (#112817) 2026-07-22 21:15:44 -04:00
Jason (Json) 7eec1345f9 fix(gateway): preserve Control UI access across device-auth upgrades (#112558)
* fix(gateway): preserve device auth upgrade recovery

* fix(gateway): satisfy device auth upgrade gates

* fix(doctor): clean disabled device auth bypass

* fix(gateway): recheck migration operator boundary

* fix(gateway): keep migration guard internal

* fix(gateway): preserve insecure migration access

* fix(gateway): reject stale migration handshakes

* fix(gateway): revoke legacy migration sessions

* fix(gateway): bound device-less migration authority

* fix(gateway): require explicit migration pairing

* fix(gateway): revoke alternate migration sessions

* fix(gateway): close migration admission races

* style(gateway): format migration pairing import

* fix(security): audit pending device-auth migration

* fix(gateway): reconcile migration on startup

* fix(gateway): cap device auth migration scopes

* fix(gateway): retain migration socket restrictions

* perf(ui): trim migration startup bundle

* perf(ui): lazy-load device auth migration

* fix(gateway): bind migration completion to approved key

* fix(gateway): preserve migration authorization bounds

* fix(gateway): grant migrated device pairing capability

* fix(ui): preserve device migration bundle budget

* fix(ui): stabilize migration startup budget

* chore(ui): retain startup budget headroom

* fix(ui): split migration overlay helpers
2026-07-22 18:44:48 -06:00
Peter Steinberger 1a7a1808f7 refactor(ui): replace sidebar render snapshot with host (#112809) 2026-07-22 20:23:59 -04:00
Peter Steinberger db5e59d15e refactor(ui): extract sidebar session-list render functions (#112753)
* refactor(ui): extract sidebar session-list render functions

* refactor(ui): trim obsolete sidebar render wrappers

* refactor(ui): compact sidebar render snapshot

* refactor(ui): add sidebar startup margin

* refactor(ui): reduce sidebar render duplication

* refactor(ui): reduce sidebar render overhead
2026-07-22 19:30:39 -04:00
Peter Steinberger 3525e06587 test(ui): collapse chat-send request mock boilerplate (#112783)
* test(ui): collapse chat send request mocks

* test(ui): satisfy chat mock lint
2026-07-22 15:23:28 -07:00
Peter Steinberger f4e465d295 refactor(ui): sidebar cleanups — shared tooltips, one idle-import helper, cross-tab outbox bridge (#112780)
* refactor(ui): sidebar cleanups — shared tooltips, one idle-import helper, cross-tab outbox bridge

Three bounded cleanups following the offline-state work:

- Tooltip unification: every sidebar-family tooltip (session-row badges,
  offline status, agent card, attention actions, brand icons) now uses
  the shared <openclaw-tooltip> component instead of raw title attrs,
  completing the direction #112639 started. Aria labels unchanged.
- One idle-import helper (ui/src/lib/idle-import.ts): the duplicated
  idle-load/retry logic from app-sidebar chrome and app-host's outbox
  loader collapses into createIdleImport (cached promise clears on
  failure, one idle retry while online, online re-arm, dispose). The
  helper migration is net negative at its call sites.
- Cross-tab outbox bridge: subscribeStoredChatOutboxChanges now also
  notifies on storage events for the composer outbox keys, installed on
  first subscribe and removed with the last subscriber, so a message
  queued in another tab refreshes badges here.

* fix(ui): keep idle-import scheduling statement-form for narrow-safe types and consistent-return

* fix(ui): give idle-import a strictly void schedule and promise-only load
2026-07-22 15:21:33 -07:00
Peter Steinberger 4e9ae9fbff feat(cron): system-owned heartbeat monitor jobs replace the dedicated interval scheduler (#112585)
* feat(cron): system-owned heartbeat monitor jobs replace the interval scheduler

- new internal cron payload kind {kind:"heartbeat"}: execution pokes
  requestHeartbeat({source:"interval"}); reported in the protocol job
  schema, not accepted from client create/patch
- gateway converges one declaration-keyed monitor job per heartbeat-enabled
  agent (schedule every+deterministic phase anchor) at startup and on
  config reload; removes monitors for unconfigured agents
- heartbeat runner loses its interval setTimeout machinery; nextDueMs
  stays as the cooldown gate, event wakes unchanged

* test(cron): heartbeat monitor regressions; docs for cron-owned cadence

- converge/prune/failure-containment tests for heartbeat monitor jobs
- heartbeat payload run fires an interval wake, no system event
- scheduler tests converted from timer self-fire to wake-queue pokes;
  timer-mechanics-only tests deleted with the timer
- persisted-shape accepts the heartbeat payload kind
- docs: heartbeat cadence ownership + system payload kind

* fix(cron): heartbeat monitor review round 1

- targeted cron-monitor interval ticks use the full per-agent path so
  due-commitment sessions still deliver
- cron-disabled gateways keep a local fallback interval timer (shipped
  cron.enabled=false contract; removed when heartbeat config folds into
  cron in #110950)
- heartbeat job reconciliations serialize with latest-wins epochs and a
  bounded 30s retry after a failed convergence pass

* fix(cron): chain clamped fallback heartbeat timers past the setTimeout cap

* fix(cron): heartbeat monitor review round 3

- targeted monitor redirect skips wakes carrying heartbeat overrides and
  surfaces the per-agent terminal skip reason instead of not-due
- cron-disabled fallback timer re-arms with a 1s floor after each firing
  so a dropped wake cannot end the chain
- heartbeat payloads are system-owned at the service boundary: add requires
  the gateway opt-in, patches to the kind are rejected

* fix(cron): heartbeat monitor review round 4 — full ownership enforcement

- prune only jobs proven to be monitors (prefix AND heartbeat payload)
- existing monitors reject every update patch; declarative upserts on the
  monitor key require the gateway opt-in even with a different payload

* fix(cron): complete heartbeat monitor ownership boundary

- converge scopes declarative matching to real monitors so a colliding
  user job with the same key is never adopted or overwritten
- monitor removal requires the gateway systemOwned opt-in; ad-hoc
  API/CLI deletion is rejected, reconciliation cleanup still prunes

* docs(cron): record intentional enrollment-snapshot semantics for monitor ticks

* fix(cron): repair heartbeat monitor CI gates
2026-07-22 14:03:29 -07:00
Peter Steinberger 6bf03cd674 fix(ui): keep composer sends on one stable bubble through the history handoff and add a subtle entry animation (#112567)
* fix(ui): keep composer sends on one stable bubble through the history handoff and add a subtle entry animation

* fix(ui): keep userTurnSendIdentity module-local
2026-07-22 13:02:49 -07:00
openclaw-mantis[bot] e92e338e9c chore(ui): refresh control ui locales (#112673)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-22 11:03:28 -07:00
Peter Steinberger 85fda04df7 feat(ui): surface queued-outbox counts in composer hint, session rows, and offline footer (#112649)
* feat(ui): surface queued-outbox counts in composer hint, session rows, and offline footer

Follow-up to the offline-state unification: queued sends were mechanically
safe (durable outbox with reconnect replay) but only visible inside the
open session's thread.

- Composer offline hint now includes the visible session's queued count.
- Session rows show a clock badge with the per-scope outbox count,
  independent of connection state (covers waiting-idle and failed too),
  with alias-safe scope resolution so agent-main never double-counts.
- The offline footer button appends the aggregate ("· N queued"); the
  connected state stays completely silent.
- One narrow subscription seam (subscribeStoredChatOutboxChanges) added
  in composer-persistence; no send/drain logic touched.

* perf(ui): keep the startup bundle under budget with a lean outbox read module

The queued-count feature statically imported composer-persistence from
startup modules, hoisting the chat page's persistence machinery into the
startup chunk and breaking the Control UI startup JS gzip budget
(319.6 KiB > 314.0 KiB limit).

Split ownership instead of gaming the budget: a lean read/subscribe
module (ui/src/lib/chat/outbox-store.ts + codec/draft-state) serves
startup consumers (app-host, sidebar), while writes, migrations, and
drain stay in the lazy chat chunk (composer-outbox-store/composer-storage);
composer-persistence keeps its export surface for chat callers. Startup
is back to 313.8 KiB gzip at 12 requests with no chunking-config changes.

Also fixes an autoreview finding in the new summary: legacy bare-main
outbox rows now resolve through session defaults (online) or the
persisted mainAlias (offline reload) instead of trusting the row's stale
embedded agent id, so badge counts key to the same scope the sidebar
resolves. The shared footer status renderer is deduplicated into
session-row-badges.

* fix(ui): correct type-only import and const tuple in outbox split

* perf(ui): idle-load the outbox summary so startup carries no outbox code

The lean outbox read module still cost ~2.8 KiB of startup gzip against
1.5 KiB of budget headroom. Follow the sidebar chrome pattern
(lobster-pet/facepile): app-host idle-loads outbox-store, subscribes on
arrival, and passes the sidebar a resolver callback instead of letting
startup modules import scope resolution. Badges and counts hydrate
moments after load; before that the summary is empty by design.

Failed chunk loads recover on browser online events and, because chunks
are usually served by the gateway itself, on gateway reconnect — the
exact moment the offline badges become relevant again.

Raise the initial-graph packing ceiling 448->512 KiB: the grown core
graph split at the old boundary into an extra chunk, costing ~1.9 KiB of
startup gzip to compression-context resets (same documented tradeoff as
the earlier 400->448 bump). Startup lands at 313.5 KiB gzip / 9 requests,
matching the origin/main baseline, limit 315.0.
2026-07-22 10:53:22 -07:00
Peter Steinberger 96d16c3c46 fix(ui): discussion button stays open on second click (#112671)
* fix(ui): toggle discussion sidebar from header

* test(ui): cover discussion sidebar toggle
2026-07-22 08:38:36 -07:00
Peter Steinberger b680843eb7 fix(ui): keep Model Providers select chevrons on the right (#112530)
* fix(ui): prevent repeated settings select chevrons

Signed-off-by: sallyom <somalley@redhat.com>

* refactor(ui): fix settings select chevron wipe at the root cause

Replace the restored chevron longhands with background-color in the
select.settings-select rule so the .field select chevron (image, repeat,
position) is never wiped, in dark mode too, and scope the 36px gutter to
.field select.settings-select so native-appearance settings selects keep
their 10px padding.

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>
2026-07-22 08:22:43 -07:00
Peter Steinberger 685fb849bb fix(onboarding): recover expired wizard sessions (#112286)
* fix(onboarding): recover expired wizard sessions

* build(protocol): project expired wizard detail to Swift
2026-07-22 06:42:25 -07:00
Peter Steinberger df3ff35277 refactor(state): move device auth tokens to SQLite (#112663)
* refactor(state): store device auth tokens in sqlite

* fix(state): keep device auth migration types acyclic

* fix(state): keep migration detection type private
2026-07-22 06:13:03 -07:00
Peter Steinberger cf2f591161 feat(sessions): permanent creator attribution, owner avatars, person filter, multi-user docs (#112658)
* feat(sessions): persist creator attribution

* feat(ui): add session creator filtering

* chore(sessions): refresh sqlite schema baseline

* docs(security): explain shared-agent trust

* fix(sessions): project catalog creator ownership

* fix(ui): restore startup JS budget headroom for creator attribution
2026-07-22 05:47:21 -07:00
Peter Steinberger 0226ad50fa feat(ui): inspect chat images in an accessible lightbox (#112442)
* feat(ui): add chat image lightbox

* fix(ui): preserve image viewer media lifecycles

* refactor(ui): move chat attachment drop handlers

* fix(ui): scope chat image interactions

* fix(ui): preserve Markdown image spacing

* fix(ui): safely open data image originals

* fix(ui): clear stale image lightboxes

* fix(ui): isolate lightbox interactions

* fix(ui): narrow lightbox focus targets

* perf(ui): track linked images in one pass

* fix(ui): satisfy lightbox CI contracts
2026-07-22 05:37:38 -07:00
Peter Steinberger 0c99a4e362 feat(ui): manage DM pairing requests in Channels (#112401)
* feat(ui): manage DM pairing requests

* fix(ui): clear pairing data across auth changes

* test(ui): tighten pairing page fixture type

* fix(gateway): complete pairing protocol contracts

* fix(ui): guard pairing mutations across epochs

* fix(ui): restore chat teardown gates

* fix(ui): isolate channel auth lifecycles

* fix(ui): remove stale chat view export
2026-07-22 04:54:20 -07:00
Peter Steinberger 0d7e870a2b fix(ui): move footer title tooltips onto the shared tooltip component (#112639) 2026-07-22 04:47:02 -07:00
Peter Steinberger 01da67e3ff feat(ui): unify Control UI offline state into one indicator and a queueing composer hint (#112600)
* feat(ui): unify Control UI offline state into one indicator and a queueing composer hint

The Control UI showed three competing offline surfaces (top connection
banner, agent-card presence dot, sidebar footer label), each deriving its
own view of the same gateway snapshot with inconsistent debounce.

- Centralize the 2s offline debounce as `offlineStable` in the gateway
  store; delete the sidebar-local timer.
- Delete the connection banner and the agent-card presence dot; the
  gateway link is client state, not agent presence.
- The sidebar footer indicator is now canonical: a single offline button
  with reconnect detail, redacted last-error tooltip, and click-to-retry
  (absorbs the banner's "Retry now"). Connected state renders nothing.
- Settings sidebar footer follows the same offline-only rule (no green
  connected dot).
- The composer shows an offline hint strip ("messages will be queued and
  sent when the connection returns") while staying fully usable; the
  existing durable outbox already replays queued sends on reconnect.

* fix(ui): keep offline debounce constant module-local for knip production scan
2026-07-22 03:31:58 -07:00
Peter Steinberger 92540b8735 feat(ui): redesign session observer HUD with labeled status and visibility-gated digests (#112594)
* feat(ui): redesign session observer HUD with labeled status and visibility-gated digests

* chore(protocol): regenerate app protocol bindings for sessions.observer.visibility
2026-07-22 02:56:10 -07:00
Peter Steinberger a67b937fce fix(ui): unify tooltip arrows and rework sidebar footer hover cards (#112602)
* fix(ui): unify tooltip arrows and rework sidebar footer hover cards

* fix(ui): keep rich tooltips open while trigger or card retains hover or focus

* fix(ui): type trigger slot query and drop unused build-card export

* test(ui): type usage hint button query for test-types lane
2026-07-22 02:17:35 -07:00
Peter Steinberger aa35dc8c40 feat(ui): builtin observer board card with digest timeline (#112565)
* feat(ui): builtin observer board card with digest timeline

* fix(ui): satisfy type and deadcode gates for observer board card
2026-07-22 01:44:04 -07:00
Peter Steinberger d4f19bfd79 feat(sessions): first-class archived-session handling (tri-state filter, pruning exemption, inline archived UI) (#112554)
* feat(sessions): first-class archived-session handling

Archived sessions are now exempt from every automatic maintenance path
(age prune, entry cap, model-run prune, disk budget, cleanup repairs);
only explicit sessions.delete removes them. sessions.list gains an
additive tri-state archived filter (true | false | "all"). The sidebar
gets a persisted Active/Archived/All status filter with inline dimmed
archived rows, replacing the nav-away View-archived button; the Sessions
page toggle becomes the same tri-state with dimmed+badged rows in All
mode; the chat composer's archived notice becomes a full-width banner
with an Unarchive action.

* chore(protocol): regenerate Swift gateway models for tri-state archived filter
2026-07-22 01:28:53 -07:00
Peter Steinberger db2ea84e9a fix(ui): collapse discussion panel chrome to a single header (#112574) 2026-07-22 01:07:16 -07:00
Peter Steinberger 6f7388c9cc feat(ui): teach the working claw three new rare tricks (#112552)
Zen (2%) breathes through a 6s cycle with one deliberate late snip,
drummer (1%) rocks a two-beat rhythm with jaw hits on the tilts, and
peekaboo (1%) ducks down small and pops back wide open. All three stay
in place (scale/rotate/vertical only); weights rebalance from the
default and southpaw stances.
2026-07-22 00:00:25 -07:00
Peter Steinberger 344feae7a1 fix(ui): hide presence avatar initials fallback when the image renders (#112523)
The initials fallback span inside openclaw-viewer-avatar was hidden with a
single-class selector (.viewer-avatar__fallback, 0-1-0) that the later
.viewer-avatar > span display rule (0-1-1) outranks, so image avatars rendered
the photo and the colored initials side by side in the who's-online roster,
footer facepile, and session facepiles. Bump the hiding selector to
.viewer-avatar > .viewer-avatar__fallback so it wins again; the
.is-fallback swap (0-3-0) still shows initials when the image fails to load.

Regressed in #111421.
2026-07-21 23:58:56 -07:00
Peter Steinberger b8ccb5dfa9 test(ui): isolate chat pane lifecycle state (#112546) 2026-07-21 23:32:53 -07:00
Peter Steinberger e01f3e18a7 feat(config): tier settings as common and advanced (#112538) 2026-07-21 23:17:47 -07:00
Peter Steinberger c670fb0e27 feat(ui): show live output tokens while agents run (#112333)
* feat(ui): show live run output tokens

* fix(ui): map live usage to active runs

* test(ui): split live usage coverage

* fix(agents): scope live usage cleanup

* fix(agents): reject stale live usage events

* fix(agents): keep live usage type local

* refactor(ui): keep chat view within line budget

* fix(agents): require lifecycle for live usage

* refactor(ui): dedupe chat reply target type
2026-07-21 23:10:17 -07:00
Peter Steinberger 70fd8d23c8 fix(ui): keep session narration live across chat switches (#112526)
* fix(ui): refcount session message subscriptions

* chore(release): keep changelog generation release-owned
2026-07-21 22:32:06 -07:00
Peter Steinberger 7014d72752 refactor(ui): unify sidebar row visibility (#112511)
* refactor(ui): unify sidebar row visibility

* test(ui): consolidate sidebar visibility coverage

* style(ui): restore chat view line budget

* chore(ui): drop superseded lint workaround
2026-07-21 22:19:28 -07:00
Peter Steinberger d823cdc794 fix(ui): normalize nullable workspace conflict in chat-view notices 2026-07-21 22:06:08 -07:00
Peter Steinberger c7dd7b4be2 fix(pr): rebuild stale prep branches after head changes (#112510)
* fix(pr): refresh stale prep branch on head drift

* fix(ci): repair PR landing gate failures
2026-07-21 21:57:56 -07:00
Peter Steinberger adf81e14a8 refactor(ui): split notice rendering out of chat-view to restore max-lines headroom 2026-07-21 21:55:43 -07:00
Peter Steinberger e81a2ce657 feat(ui): show chat run startup status (#112339)
* feat(ui): show chat run startup status

* refactor(agents): isolate run status emission

* chore(protocol): refresh startup status models

* refactor(swift): remove unused chat helpers

* test(swift): align retry and i18n fixtures

* fix(swift): restore outbox display helper
2026-07-21 21:25:23 -07:00
Peter Steinberger c780d8b1c0 feat(ui): unify agent pickers with avatars (#112488)
* feat(ui): unify agent pickers with avatars

* fix(ui): close agent picker on draft reset
2026-07-21 21:11:18 -07:00
Peter Steinberger b83af8157b test(ui): reuse shared non-isolated workers (#112493)
* test: isolate OpenClaw home in shared setup

* test(ui): reuse shared workers for pure suites

* test(ui): align scoped runner contract
2026-07-21 21:08:12 -07:00
Peter Steinberger 1a8583ba45 feat(gateway,ui): ask-the-observer questions from the session HUD (#112448)
* feat(gateway,ui): ask-the-observer card input over sessions.observer.ask

* refactor(ui): single home for observer run-identity helper after restack

* test(ui): drop duplicated observer hud test after restack

* test(ui): give the observer ask flow its own colocated suite

* refactor(gateway): leaf observer contract and ask module split for ci gates

* refactor(gateway): drop observer contract re-export shims
2026-07-21 20:59:32 -07:00
Peter Steinberger 153636c702 feat(dashboard): workboard:mini aggregates all boards when boardId is unset (#112484)
* feat(dashboard): workboard:mini aggregates all boards when boardId is unset

* docs: regenerate docs map
2026-07-21 20:32:33 -07:00
Peter Steinberger 3c3a712913 fix(ui): keep chat author identity readable (#112357)
* fix(ui): keep attributed chat identity readable

* test(ui): verify hover-only chat metadata
2026-07-21 19:40:05 -07:00
Peter Steinberger 6c3caa38ab fix(ui): allow direct sessions in non-Git folders (#112433)
* fix(ui): allow direct sessions in non-Git folders

* test: align New Session fixtures with place picker

* fix(ui): preserve worktree intent on Git probe failures

* fix(types): keep worktree status alias internal
2026-07-21 19:36:53 -07:00
Peter Steinberger 0f066eec81 feat(dashboard): plugin widget kinds — native WorkBoard card and mini-board widgets (#112434)
* feat(board): add plugin widget kinds

* feat(ui): render native Workboard widgets

* fix(dashboard): compose plugin widgets with current main

* chore: internalize widget-kind contribution types

* fix(ui): retry plugin widget renderer loads

* fix(ui): harden Workboard widget refresh lifecycle

* fix(ci): clear plugin widget landing gates

* fix(boards): migrate plugin widget storage

* fix(db): migrate unreleased board widget constraint

* fix(ui): retry failed Workboard widget loads

* fix(ui): keep stale widget refresh cleanup inert

* fix(ci): align plugin widget landing guards
2026-07-21 17:59:20 -07:00
Peter Steinberger f0c43dcf72 feat(ui): format international phone numbers for display (#112400)
* feat(ui): format phone numbers for display

* ci: track normalization package exports in Knip
2026-07-21 17:10:25 -07:00
Peter Steinberger 1f0a3ecc68 feat(ui): session observer HUD, sidebar subtitles, and settings (#112260)
* feat(ui): session observer HUD, subtitle integration, and settings

* test(ui): observer demo fixtures for the mock control-ui harness

* fix(ui): satisfy lint and deadcode gates for observer surfaces

* test(ui): adopt renamed pull-request summary api after rebase

* fix(ui): clean rebase artifacts in observer test files
2026-07-21 17:05:14 -07:00
Peter Steinberger 440539b538 feat(workboard): routable boards, sidebar pinning, icon and color (#112302)
* feat(ui): add Workboard board routes and pins

* fix(ui): reject malformed Workboard board catalogs

* perf(ui): lazy-load Workboard sidebar runtime

* test(ui): align sidebar board fixtures with render contract

* fix(ui): clear Workboard landing gates
2026-07-21 15:16:38 -07:00
Peter Steinberger 98742bc2c7 feat(cron): stream schedule sources with durable source identity (#112387)
* feat(cron): stream schedule sources (supervised command stdout)

Add gated argv stream schedules with bounded line batching and trigger.streamBatch composition.

Reuse the gateway ProcessSupervisor for source ownership, deterministic teardown, capped restart backoff, and schedule-key guarded batch execution. Expose additive protocol, CLI, tool, UI, docs, and generated snapshot surfaces without storage DDL.

Contract: stream schedules are event-driven, require cron.triggers.enabled, reject command payloads, and retain at most one bounded pending batch.

* fix(cron): reject retired stream source epochs at run admission

Thread an invalidatable per-owner source-generation token (ownerNonce.generation)
through cron.run admission alongside the schedule key. A batch handed to cron.run
under one source epoch can wait behind another run while its owner is stopped; a
disable→re-enable or A→B→A edit leaves the schedule key unchanged, so the key
check alone would admit the retired epoch's batch. The token is persisted in
job.state on every lifecycle write and compared at every admission site plus the
executeJobCore guard, so a stale epoch's batch is skipped.

Also fix direct stream-job mutations recording the wrong lifecycle status when
global cron is off but triggers are on: extract resolveStreamStopReason so the
direct path reports the remediable cron-disabled state like reconcile does.

* fix(cron): close stream admission windows from round-10 review

- Persist the retired source generation before draining stop teardown, so a
  batch queued behind another cron run cannot gain admission during the up-to-10s
  in-flight-batch wait (server-cron routed stop path).
- Add streamSourceGeneration to the closed gateway response schema (excluded from
  the writable patch schema) so a running stream job passes strict result
  validation without letting callers spoof source identity.
- Close the mutation-epoch ABA: track an eviction epoch so a snapshotted absent-0
  is trusted as unchanged only when no LRU eviction happened during the await.

* refactor(cron): stream sources own a durable logical identity

Split the conflated restart-generation/admission token into two concepts:
a persisted streamSourceIdentity owned by cron store mutations (rotates on
enable/disable, source replacement, once-trigger auto-disable, and explicit
retirement; stable across supervised child restarts) and a watcher-local
process generation used only to fence stale child callbacks. Admission now
requires schedule key + identity together at every window, closing the
A-to-B-to-A and restart-flush races from review rounds 8-11.

Also: match-mode regexes now see raw source text (the [truncated] marker is
applied after matching), stop-timeout failures set the live restartExhausted
mirror so shutdown preserves the terminal diagnostic, and the watcher is
split into owner/output/registry modules under the max-lines budget.

* fix(cron): harden stream teardown and intake from round-4 review

An exit queued ahead of a requested stop no longer counts toward restart
exhaustion (the synchronous stop fence owns it), overlapping cron.stop and
stopAndDrain share one memoized shutdown drain instead of double-stopping
every owner, raw output intake is bounded at 4x the batch cap so normal
64 KiB pipe reads stop losing complete lines to OS chunk boundaries, and
persisted-shape quarantine coverage for unsafe match expressions is pinned.

* fix(cron): keep watcher-internal owner disposal from retiring live identity

Disposing an obsolete owner while a start replaces it is not a durable
removal; a retiring stop there rotated the live job's identity and stranded
the replacement behind the CAS ownership guard. Also align the docs with the
implemented match semantics: complete lines match on full text past the
batch cap, only intake-cut prefixes are unmatchable.

* fix(cron): make oversized-line matching independent of pipe chunking

Partial lines are retained up to the raw-intake bound rather than the
delivery cap, so a complete over-cap line matches identically whether it
arrives in one callback or several; only a line the intake bound itself cut
remains an unprovable prefix. Reconcile also contains schedule-replacement
stop failures per job, matching the other stop branches.

* fix(cron): bound assembled lines, drop stale payload override, barrier stopAll

- enforce the 4x raw-intake per-line cap while assembling split callbacks,
  so an oversized line stays an unprovable prefix regardless of chunking
- stop passing the watcher-cached payload as a cron.run override; the run
  snapshots the persisted payload under its admission lock
- stopAll waits for every owner stop to settle before surfacing failures
- split cron-stream-output interleaving tests into their own file (max-lines)

* fix(cron): address first full-CI round (lint, knip, schema test, unused param)

* chore(cron): refresh codex prompt snapshots for stream schedule schema

* fix(cron): keep the first clean line after an intake drop ending at a newline

* fix(cron): fence stream reconcile list snapshots against direct mutation routes

A cron.list snapshot captured across the reconcile await could be applied
after a direct add/update route already started the owner, stopping it as
removed and retiring its live identity. A mutation revision bumped at every
direct route start invalidates the stale snapshot; reconcile re-lists
(bounded) instead of applying it.

* style(cron): format stream owner imports

* fix(cron): discard severed stream prefixes at EOF

* fix(cron): retry failed stream shutdown drains

* fix(cron): honor stream stop fence after output drain

* chore(cron): refresh landing checks
2026-07-21 15:01:30 -07:00
Jason (Json) c4911a2c77 improve(ui): simplify coding agent session headers (#112354)
* improve: simplify coding session catalog headers

* improve: simplify coding session catalog headers
2026-07-21 15:27:49 -06:00
Peter Steinberger 3a1351a303 fix(cron): show script automations across user clients (#112195)
* fix(cron): preserve script jobs across clients

* chore(i18n): refresh native cron strings

* chore(i18n): align rebased native catalogs

* chore(i18n): defer generated locale refresh
2026-07-21 08:02:59 -07:00