Skip to content

Refactor almost all modules#72

Merged
su-fen merged 64 commits into
mainfrom
style/sidebar-menu-animation
Jul 5, 2026
Merged

Refactor almost all modules#72
su-fen merged 64 commits into
mainfrom
style/sidebar-menu-animation

Conversation

@su-fen

@su-fen su-fen commented Jul 5, 2026

Copy link
Copy Markdown
Member

No description provided.

su-fen and others added 30 commits June 24, 2026 22:50
Move chatEventPayload/chatControlPayload/chatEventType/trim helpers out of
internal/server into a standalone internal/chatwire package so the session
layer can shape events at ingress without an import cycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ifecycle

New conversation stream store: one ordered event log per conversation with
gateway-assigned monotonic seq, exactly-once run_finished, supersession-based
run handoff, persistent subscribers with overflow-resume, retention decoupled
from run lifecycle, snapshot hydration for late joiners, pending-run binding
for draft conversations, and a chat activity hub whose events are composed
inside the locked transitions (run ids from birth). Additive — the legacy
chat run store still serves traffic until the cutover.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the per-run chat relay with the conversation stream store end to end:

- delete manager_chat_runs.go (run state machine, conversation claim map,
  runEpoch, per-run subscribers) and route agent dispatch through the
  normalizing ingress; agent-sent history running/idle events are dropped —
  activity now derives from run lifecycle transitions with run ids from birth
- new WS contract: persistent per-conversation chat.subscribe with
  stream_epoch/after_seq resume and reset semantics, first-class
  conversation_id/run_id/seq on every chat.event, chat.activity broadcast hub,
  chat.command_update for pre-stream command outcomes; chat.replay and
  chat.subscription_end are gone
- cancel keeps the run alive in a cancelling state until the agent's terminal
  signal, with a 15s force-finish watchdog; startup watchdog fails commands
  whose run never settles
- remove the unused POST /api/chat/commands endpoint, its rate limiter and
  CSRF helper (origin checks move to http_origin.go), and the per-connection
  history running/idle run_id enrichment
- rewrite gateway chat tests against the new contract (websocket chat suite,
  session integration suite)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New web/src/lib/chat/stream modules implementing the client half of the
conversation-scoped protocol:

- ConversationStreamClient: persistent per-conversation registrations inside
  the socket layer with automatic seq-cursor resume on every reconnect,
  pre-sync event buffering, gap-triggered resync, and overflow recovery
- transcriptStore: GUI-style committed/settled/live split — replies settle in
  the tail with unchanged ids and fold into committed at the next run_started;
  optimistic user entries adopted by client_request_id; id-preserving history
  merge; rebased truncation; snapshot rebuild
- activityStore: single source for running dots, fed by chat.activity and
  history.list hydration
- ChatCommandPipeline: optimistic echo + command_update outcomes
  (bound/queued_in_gui/failed) with a 60s pending watchdog
- gatewaySocket: chat generators, per-run subscriptions, __gatewayRunId
  metadata and subscription_end handling removed; chatCommand promise, stream
  client wiring into auth/disconnect, chat.activity / chat.command_update
  fan-out; chat streams now count toward shouldMaintainConnection
- pushChatEvent gains entryIdPrefix so entries of different runs never
  collide on id

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GatewayApp.tsx drops from 6656 to 4533 lines: transcript rendering sources
from per-conversation transcript stores (committed + tail), busy and sidebar
dots derive from the activity store and command pipeline, and the displayed
conversation holds one persistent stream subscription regardless of running
state — GUI queue auto-sends simply flow in. sendChat shrinks to a pipeline
submission with command_update-driven draft binding; cancel no longer marks
terminal locally; edit_resend truncation rides the rebased stream event; the
queued_in_gui delayed reloads and all fifteen recovery layers are gone
(page-restore rehydration, completed markers, hydration blocks, draft
adoption windows, per-run abort registries).

Rendering: GatewayTranscript consumes disjoint committed/tail entries (tail
dedup helpers deleted); run_started performs the single scroll-compensated
flushSync fold; Markdown render mode is fixed per entry birth (live-born
entries stay in streaming mode forever, shikiTheme passes in both modes) so
the reply-end streaming→static reflow cannot occur.

Tests: socket suite rewritten for the persistent subscribe/resume contract;
history-chat-ui covers the id-preserving quiet merge; queue helper module
trimmed to its one live export.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove chatwire.ControlPayload/IsTerminalControl (the ingress no longer
forwards raw controls), collapse the webui ChatEvent union's control and
runtime_snapshot variants into the two data events that still exist on the
wire (user_message, rebased), and drop the dead "failed" branch from
pushChatEvent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gateway:
- runtime snapshots carry as_of_seq (the log seq they represent through) so
  clients rebuilding from a snapshot drop the overlapping buffered replay
  instead of double-applying it
- chat.activity events carry client_request_id, letting the command pipeline
  settle pendings from the always-on activity channel (no spurious startup
  timeout after switching conversations away)
- token-recreated streams after a gateway restart mark runNeedsSnapshot so
  late joiners hydrate instead of getting a silently incomplete replay
- the reaper spares silent runs while the runtime heartbeat reports busy (a
  12-minute quiet tool call is no longer force-failed) and can now also reap
  orphaned queued activities; the runtime-idle heuristic that could kill
  GUI-local runs is gone
- chat.subscribe/unsubscribe execute inline on the read loop so a
  re-subscribe's frame order can never let the stale unsubscribe cancel the
  fresh subscription (StrictMode double-effect)
- WatchChatCommand cleanup closes the channel (one goroutine no longer leaks
  per submitted command); activity replay channels are sized to the replay
  (blocking send under both store mutexes could deadlock all chat ingest)
- supersession-started runs keep their seeded user_message inside the
  eviction-protected window

WebUI:
- transcript stores gain an idempotency seq cursor: re-subscribe replays and
  snapshot+replay overlaps no longer duplicate bubbles or reply text; resets
  zero the cursor and fold (not drop) the settled tail
- run_finished respects run ownership: stray terminals for non-active runs
  never settle the streaming tail, and the settle sweep leaves other runs'
  seeded/optimistic entries live
- pipeline settlement is strict-identity (runId or own client_request_id) —
  foreign runs can no longer disarm the startup watchdog
- activityStore hydration is an ordered merge and re-hydrates after an
  offline/online transition (no phantom running dots)
- second send while pending parks into the GUI queue instead of erroring;
  quiet refresh can no longer truncate long transcripts; history merges
  upgrade tail entries in place so the latest exchange stays edit-resendable;
  parked/failed edit_resends restore the optimistically truncated transcript

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Queue-bound prompt flash: sending while a run is streaming used to show the
user bubble for an instant before the prompt landed in the GUI queue, on
every viewer. Two sources, both removed:
- the gateway now defers seeded payloads for commands accepted while another
  run is active — they reach the log only when the run actually starts (or
  fails, for error context) and are dropped when the desktop parks the
  prompt, making the agent's echo authoritative for queued items
- the webui command pipeline gains optimistic:false, used by the
  queue-destined composer path so no local echo is inserted at all

Transcript render misalignment: applyHistorySnapshot matched entries with a
dedup-key -> single-entry map, so a history snapshot containing repeated keys
(identical re-sent prompts, id-less tool calls sharing name+round across
runs) let several entries adopt the same rendered id — duplicate React keys
corrupt the row virtualizer's measurement cache and rows collapse onto each
other (and extra occurrences were silently dropped). The merge is now
occurrence-aware: per-key queues consumed in transcript order (committed
copies before tail copies), with merged ids guaranteed unique. The
transcript renderer additionally suffixes any duplicate row key as a
defensive backstop for the virtualizer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the char-count text summary (contentChars=/oldChars=/newChars=)
in the collapsed Write/Edit tool bar with a green +N/red -N line-diff
badge (FileChangeBadge + animated OdometerNumber), mirrored across
agent-gateway/web and agent-gui. Falls back to total line counts when
the streaming preview meta reports truncation or the diff exceeds the
200k-char budget.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
su-fen and others added 29 commits July 4, 2026 09:51
- Add biome to gateway WebUI (same config as GUI) and format all web sources;
  fix the lint errors surfaced on both ends (unused imports, assignments in
  expressions, useless switch case, optional chain)
- Align both biome configs: legacy a11y rules downgraded to warn, CSS
  progressive-enhancement fallbacks exempted from noDuplicateProperties
- Add scripts/check-mirror.mjs + scripts/mirror-manifest.json to enforce
  byte-identical GUI/WebUI mirrored files
- CI: lint both frontends, typecheck GUI build, run mirror check

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gateway:tunnel-state previously carried two unrelated revision domains
(local desired-state counter vs gateway process counter), which defeated
the panel's monotonic guard: a gateway restart froze the GUI panel and a
dropped link kept the health badge green. All emissions now go through a
single publish path that stamps a store-level monotonic emit_seq and
caches before emitting, and every disconnect path emits a local
agent_online=false state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the polling-based freshness model for git/file-tree panels with a
best-effort invalidation push: a Rust notify-based watcher per workspace
(250ms debounce, .git noise filtering, changedPaths capped at 64 with a
truncated flag, per-workdir monotonic revision, mtime-sampling fallback)
emits workspace:activity Tauri events and forwards to the gateway via new
WorkspaceActivityEvent/WorkspaceWatchRequest envelope messages (field 90).
The gateway fans out to workspace.subscribe WS subscribers, tunnel-hub
style, and replays the declarative watch set on agent reconnect. Both
frontends share a byte-identical WorkspaceActivityClient contract and a
useWorkspaceInvalidation hook (dirty-while-hidden, flush-on-activate,
refetch on reset/revision regression). Consumers land with the panel
rewrites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Persisted dock state now records user intent only: activeTabId, tabOrder
(which may hold live/dead/not-yet-loaded session ids), singleton tools
keyed by kind, and merge bookkeeping. Terminal tab existence is derived
from live sessions at render time, which structurally removes the passive
reconcile effect that raced session-list loading (pruning persisted tabs
and broadcasting the damage to other clients) along with the "+2 beats
the echo" version hack.

- normalize keeps unknown tabOrder ids and never resets activeTabId; the
  legacy tabs shape migrates in place (terminal entries dropped)
- version stamping is centralized in updateRightDockProjectState
  (stateVersion+1, writerId, lastUsedAt); merge orders writers by
  (stateVersion, writerId) so ties converge symmetrically
- project buckets evict by lastUsedAt LRU instead of insertion-order
  break at 100, and empty-bucket tombstones expire after 90 days
- session-list mutations compute first and notify outside state updaters
  (StrictMode-safe); manual 20-field session comparison replaced
- sessionsLoaded threads from both integration points so the loading
  window renders a fallback without writing settings
- concurrent close tracking uses a Set; four pure-logic dock files are
  now byte-identical mirrors enforced by scripts/check-mirror.mjs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…daries

RightDockContent drops from 45 props / hardcoded branches to a registry
loop: each tool definition now carries isAvailable/render and reads a
RightDockToolContext (clients, capabilities, fileTree/git/ssh callbacks,
openExternal) provided by a memoized RightDockPanel. Both integration
points memoize projectState/fileTreeState and stabilize every callback so
the memo boundary actually holds — unrelated ChatPage/GatewayApp state
changes no longer re-render the dock subtree.

Every panel now receives an `active` prop (consumed by the panel rewrites
that follow), and each end gains a thin non-mirrored
WorkspaceActivityClient adapter (Tauri events / gatewaySocket). Panel,
TabStrip, Launcher, Content, Context and registry are byte-identical
mirrors (12 files enforced in CI); the GUI keeps its taller header via a
CSS override instead of divergent markup, and the launcher trigger uses
buttonVariants directly to erase the Base UI/Radix asChild split.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Git review: the 5.6k-line panel splits into git-review/ modules with a
context-fed data hook. History signatures no longer embed the status
signature, so agent writes stop resetting pagination/selection/diff; all
polling and the unscoped liveagent:git-changed broadcast are gone in
favor of workspace-activity invalidation (10s active-only fallback when
no push client); per-cwd epochs retire the stale-response and
swallowed-interaction races; the overlay scrollbar becomes a
lifecycle-safe hook merging both ends' one-sided fixes; diff chunks
parse lazily via IntersectionObserver; menu clamps are measured.

File tree: rewritten under file-tree/ with a virtualized flat list,
request dedup held in refs (not updater side effects), per-path epochs,
reference-preserving merges, targeted subtree refresh from
activity.changedPaths, a correct rename remap for expanded paths,
single-source expanded-state sync, LRU project buckets, and clipboard/
error-copy hardening. The fileTree uiState drops its redundant
stateVersion; revision now means reveal-nonce only.

Terminals and tunnels: XTermViewport resets and replays the snapshot on
stream gaps or truncation instead of appending duplicates, renders the
cached snapshot while attach retries, keys errors by session id (with a
per-session error map in the panel), and rebinds on client change.
LocalTunnelPanel clears its snapshot across client swaps, prunes orphan
row state, isolates check-all errors, and moves the TTL countdown into a
memoized leaf gated by active. SshTunnelPanel claims created sessions by
returned id instead of guessing by host (no more swallowed auth
prompts), gates latency probes on active with stable deps, tracks
concurrent closes in a Set, and gains autofocus parity; the WebUI drops
its duplicate 5s ssh reconcile poll for an activate-time catch-up.

All rewritten panels are byte-identical mirrors (27 files enforced);
GitBranchSelector consumes activity invalidation on both ends.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rotocol

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- structured evidence args on write/update/apply_batch; Rust renders the
  canonical evidence frontmatter and owns the confidence contract
- mutation responses report appliedConfidence/autoDowngraded
- new memory_quota_summary command (per-scope used/headroom/unreviewed age)
- organize_runs schema v4: additive migration, phase/final_count/
  compression/token-usage columns, trimmedProtocol renamed to report
- apply_batch accepts op=accept for the single persistence path

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- lib/memory/schema.ts single source for enums, plan/decision shapes,
  confidence contract; lib/memory/config.ts owns every magic number
- api.ts rewritten on the schema: evidence pass-through, quota summary,
  organize-run v4 report
- prompts/ split by audience (shared/injection/extraction/managerTool);
  extraction prompt targets the SubmitMemoryPlan tool-call protocol with
  no hardcoded status sentinels
- extraction/planTool.ts per-item validation + single apply_batch mapping;
  extraction/{context,gating}.ts compact window, deterministic workspace-
  mutation digest, language-neutral skip heuristics
- memoryTools.ts rewritten: structured evidence to Rust (client
  frontmatter serializer deleted), auto-downgrade surfaced in results
- delete dead memoryFrontmatter.ts and memoryPrompt.ts; new test/memory
  suite (51 tests)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oller

- extractionEngine: compact self-contained context (last-4-turn window +
  deterministic workspace-mutation digest + candidates/rejections), one
  SubmitMemoryPlan submission, single-tool forced retry when missing,
  single memory_apply_batch persistence, summary-model fallback absorbed,
  i18n status text (Chinese sentinels deleted)
- extractionController: synchronous gate+claim (fixes throttle TOCTOU),
  per-run AbortController detached from the chat request signal (a new
  turn no longer kills in-flight writes), coalesce-skip queue, dispose on
  conversation delete (fixes state leak), LRU-capped state
- turn runners + ChatPage rewired; delete silentMemoryExtraction*,
  memoryProtocol, memoryExtractor, memoryDecisionLog, memoryPolicy and
  their legacy tests (8 files)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- organizer/pipeline.ts: pure clustering/risk/gate stages ported from the
  1575-line headless React runner; single coercion helpers; typed review
  items and rejection buckets
- organizer/runRecord.ts: OrganizeRunReportV4 replaces organizerProtocol's
  defensive normalization — one version check, no key-spelling probes;
  pre-v4 runs degrade to read-only summaries
- organizer/quota.ts: quota ladder (normal/notice/degraded/critical/
  exhausted) drives the prompt compression target and panel banner
- organizer/service.ts: one-shot wake timer (nothing armed while disabled,
  no mount-time forced claim), poke() replaces the window CustomEvent bus,
  explicit scan→cluster→plan→gate→apply phase updates, quota headroom and
  token usage recorded on the run
- useMemoryOrganizer hook + MemoryOrganizerHost replace the runner mount;
  MemoryOrganizerRunner.tsx deleted

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- pages/settings/memory/: platform adapter (base-ui vs radix Select,
  buildModelOptions path, organizer poke) + five byte-mirrored components
  (panelModel, useMemoryPanelData, MemoryPanel, OrganizerHistoryModal,
  MemorySettingsDrawer); the 2501-line panel forks on both ends die
- organize-run polling only while a run is pending/running; idle panels
  never poll
- history modal consumes the typed v4 run record; pre-v4 runs degrade to
  read-only summaries; manual apply writes back plain-array state
- settings drawer surfaces the quota ladder banner from memory_quota_summary
- gateway web: memory domain files byte-mirrored, organizerProtocol and the
  runner stub deleted, radix platform adapter added
- scripts/mirror-manifest.json now enforces 10 memory files (37 total);
  api.ts compat re-exports removed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- docs/features/memory.md: module table, evidence contract, quota ladder,
  SubmitMemoryPlan extraction protocol, organizer phases, mirror discipline,
  refreshed troubleshooting entries
- mark the May organizer-optimization proposals (phases/quota/schema-v4)
  as landed by the memory-system rewrite

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ebar state layer

Both frontends get a lib/sidebar module (store, reconcile, openController,
scope, backend) driving the chat history sidebar via server-authoritative
reconcile, replacing the old historyListScope/historySync/useChatHistoryList
hooks that tracked history state ad hoc per page.
Long version strings could overflow the fixed-width version selector; clip with truncate and show the full label via title/tooltip instead.
…main

Consolidates the previously separate hooks and cron subsystems, which had
three uncoordinated writers (UI full-table replace, LLM incremental edits,
executor self-decrement) with no version control, into a single
services/automation store with revision-CAS applies, notifier-broadcast
snapshots, a persisted prompt-run lease queue, and a differential
scheduler. Hooks and cron settings move out of AppSettings into their own
read-only, revisioned snapshots synced over the settings channel.

Mirrors the change across agent-gui and agent-gateway/web per the shared
settings/hub UI convention.
Adds a --hub-canvas token (darker than --background in dark mode) so
background-tinted glass panels on the MCP/Skills/status hub pages read as
elevated surfaces instead of blending into a gray fog, plus matching
dark: border/background/shadow variants across cards, tab bars, and icon
tiles.
Pass -w <pid> so the keep-awake assertion is scoped to this process: if
the app crashes without running stop(), caffeinate exits on its own
instead of blocking system sleep until reboot.
Even out the Advanced Options toggle grid (sm:grid-cols-2 xl:grid-cols-3
instead of a ragged 4-col layout for 6 items), extract SectionCardHeader/
ToggleOptionCard helpers for consistent card structure, clean up the
Gateway Connection field spacing, and move hardcoded connection-status
strings into i18n.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace the delegate/subagent implementation with a single lib/subagents
domain (pure domain -> ipc -> runtime -> tool adapters), a structured
zero-inference model API, and a redesigned persistence and UI protocol.

- TS: delete lib/tools/delegate/* and lib/chat/subagent/*; new four-layer
  lib/subagents with SubagentConversationStore as the single source of
  truth and a provision/execute/settle/report run state machine
- API: structured agents array with atomic validation and structured
  errors listing roster + enabled templates; agent_spec manifests,
  task_intent, and all prompt-text heuristics removed; SendMessage
  recipients validated against the roster; template.enabled honored
- Persistence: turn-boundary incremental run saves (interrupted runs
  resume from the last completed round), persistence failures surfaced
  to the model, new cancelled status; Rust subagent_store.rs with
  schema v2 (versioned drop-and-recreate, no event table) and a
  12-command camelCase surface; delegate.rs -> subagent_worktree.rs
- UI: subagent_batch/subagent_card/subagent_message protocol shared
  byte-identically via lib/subagents/protocol.ts (mirror-manifest);
  placeholder cards parse the structured agents array; rejected Agent
  calls now render visibly in both frontends
- Tests: 81 behavioral tests in test/subagents plus rewritten registry,
  messages, and Rust suites; docs updated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… state

Safari can fire compositionend before the confirming Enter's keydown with
isComposing already false, so a bare Enter suppression window plus a short
post-composition tail is needed to keep IME confirmation from sending the
message. Also dedupes commit tooltip re-renders, drops large-paste map
entries when their chip is removed via native editing, guards mention
suggestion selection against a detached text node, and avoids redundant
mention refetches when the cached snapshot still covers the narrowed query.

Mirrored across agent-gateway/web and agent-gui per dual-frontend UI parity.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… while scrolling

Cap the branch dropdown to 66vh of the viewport (via the available-height
CSS var) instead of only scrolling once it hits the window edge, mark the
currently tracked remote branch with a checkmark like local branches, and
restructure the popup into a flex layout so the repo/refresh header and
create-branch footer stay fixed while only the branch list scrolls.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…iders

Extract Anthropic/Gemini thinking-runtime resolution and the OpenAI
reasoning-effort clamp out of streamByApi.ts into thinkingLevels.ts, the
single source of truth for provider thinking-level mapping. Prefer
catalog-declared compat.forceAdaptiveThinking / thinkingLevelMap over id
heuristics, guard the Claude version regex against date-suffixed ids, and
add clampOpenAIReasoningEffort to downgrade xhigh/minimal on models that
don't support them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…popup motion

The tailwindcss-animate classes on DropdownMenuContent were dead (the
plugin is not installed on either end), so the sidebar context menus
popped in and out with no transition. Key the motion off each end's real
popup primitive instead: Base UI data-starting/ending-style transitions
in the GUI, Radix data-state keyframes in the WebUI. Menus now grow from
the anchor origin with a side-aware slide and staggered item entrance,
fade out quickly on close, and disable entirely under
prefers-reduced-motion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rites

The Gateway WebUI and GUI CI jobs were failing on tests that still
referenced APIs removed by the automation domain rewrite (504f57a) and
the subagent system rewrite (41c0137):

- hook-lifecycle: drop messageUpdated/message_update and
  tool_execution_update, match the 8-event lifecycle driven once per
  tool execution
- normalization/web-settings: delete normalizeConversationHook and
  normalizeCronTask tests; validation moved to Rust
  services/automation/validate.rs and is covered by its tests
- chatUi-agent: rewrite delegate-agent card tests against the
  structured agents protocol (buildSubagentPlaceholderToolCalls,
  subagent_batch/subagent_card details)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the unused in-memory sqlite helper and stale imports left in the
system and terminal test modules, and mark truncate_process_error as
macos-only since its sole caller is behind the same cfg.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@su-fen su-fen merged commit 4bfb06a into main Jul 5, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant