Commit graph

2553 commits

Author SHA1 Message Date
Kayshen-X 0bd9947310 feat(collab): add authenticated p2p collaboration 2026-07-29 00:06:58 +08:00
leinaldo 42688d4081
fix(agent): update winget package name from GitHub.CopilotCLI to GitHub.Copilot (#193) 2026-07-29 00:05:25 +08:00
leinaldo 3e1fc81d7a
fix(desktop): prefer .exe/.cmd/.bat over extensionless npm shims on Windows (#191) 2026-07-28 23:52:33 +08:00
Fini 8102ca506b docs(ai): teach overlay-only layout:none and concentric ring authoring
Two schema-skill clarifications for shapes models keep getting wrong:
layout:none is overlay stacking only — a row or column of cards belongs
to horizontal/vertical (one corpus document authored a carousel as a
none frame and depended on the old engine bug to display) — and a ring's
track and progress arc are never flex siblings but same-origin layers in
one fixed wrapper.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:14:11 +08:00
Fini 093d7ed094 fix(agent): rerun gutter dedupe after content rails install their padding
strip_wrapper_double_inset has always known how to strip a transparent
wrapper's duplicate side padding, but it runs before the mobile rail
pass installs the rail gutter it deduplicates against; the later rerun
used the single-child collapse whose contract cannot see a wrapper with
siblings. A padded sheet wrapper inside a freshly railed section thus
kept a second 24px inset and its card measured 279 against 327 rail
siblings. The dedupe now reruns after rail installation, and gutter
ownership is inherited along transparent chains — resetting at any
painting surface — so nested wrappers converge on one gutter owner
instead of reproducing the bug one level deeper. The rail skill's
wrapper rule is sharpened to name the shape models actually emit.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:13:47 +08:00
Fini 9e417c238a fix(agent): exempt image-backed cards from the deck opacity repair
fix_deck_front_card_transparency saw a photo card's transparent content
layer over its alpha gradient scrim as a deck front card leaking a
painted sibling, and lidded it with an opaque surface fill — hiding the
photo and the scrim the model had deliberately composed (three theme
cards in one measured document). An image sibling anywhere in the stack
now marks the see-through front as the composition itself, the same
reasoning as the existing ellipse exemption.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:13:31 +08:00
Fini fd671651a1 feat(agent): flag bottom-flush screens and teach bottom breathing room
A detail screen without a bottom tab bar that ends flush against the
device edge reads as clipped, and the corpus had no rule for it. The
geometry diagnostics now echo a mobile-bottom-flush fact when a mobile
root's lowest resolved child edge sits within 12px of the root bottom
and the trailing child is not nav-shaped — nav detection reads the
authored role or pure row geometry (full-width band in the nav height
range with evenly split targets), never node names. Repair stays with
the model: whether the fix is padding or a spacer is an intent call.
The mobile skill gains the matching breathing-room rule.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:13:13 +08:00
Fini b74c94d2da fix(ai): budget runtime-augmented planning skills and raise the phase ceiling
The per-skill budget guard measures skill files as they sit on disk, but
style-guide-selector grows at resolve time when the available-style-guide
catalog replaces its placeholder — 819 tokens against a 500 budget, so
the catalog tail was silently cut from every planning prompt. Worse, the
three base planning skills alone already exceeded the 4000-token phase
ceiling, which made the only domain skill of the phase undeliverable on
any prompt, with no warning anywhere. The skill budget now covers its
worst-case injection, the planning ceiling rises to 6000, and three new
guards keep the hole shut: augmented skills must fit their own budget,
every augmentation placeholder must declare a worst case, and the end-to-
end resolver must deliver the full phase without drops or truncation.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:12:57 +08:00
Fini 2ca873346c fix(mcp): default omitted effect fields instead of dropping the whole node
ShadowBody deserializes with five required fields, so a model omitting
just spread lost the entire node — and every child line that named it as
a parent cascaded into 'Insert parent not found' (a measured 19-line,
whole-card silent loss). Effects are now normalized before the strict
parse: identity defaults for offsets/blur/spread, alias folding for the
common synonyms, a lone effect object wrapped into the array, numeric
strings coerced, and glow/drop-shadow spellings canonicalized. Truly
unknown effect kinds still fail the node — inventing an effect the model
did not ask for would be worse than dropping one. The modify path's
parser reuses the same normalizer so the two lanes cannot drift.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:12:36 +08:00
Fini 3571d70a53 fix(agent): extract ring fragments from flex rows into a concentric wrapper
Models keep authoring a progress ring's track and arc as flex siblings,
so the layout engine lays the two circles out side by side with the
percentage label floating off the edge. radial_repair already converts
a parent that IS the ring's own wrapper, and correctly declines padded
general-purpose containers — which was exactly the hole the defect kept
returning through. This pass closes it from the other side: the arcs are
extracted into a fixed-size layout:none wrapper that takes the first
arc's slot, the parent keeps its layout and other children, and a lone
percent-content text sibling is adopted as the centred label. The lane
predicate is shared with radial_repair so exactly one pass owns any
given ring, and the same detector feeds geometry diagnostics so the loop
sees the violation it can no longer ship.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:12:19 +08:00
Fini 97b8f089a2 fix(canvas): adopt stack semantics for layout:none and retire the flow bandaid
Bump jian for the single-cell stack compilation of layout:none frames.
The loader's repair_inferred_horizontal_container previously rearranged
explicit layout:none children into a row to paper over the engine gap;
with the engine fixed that bandaid would shove correctly stacked layers
out of the frame, so it now only fires for documents that omit the
layout field entirely. Corpus-wide differential over 184 documents:
60 of 240k node rects change, all overlay repairs (hero scrims and
avatars regaining their parent box), none collapsing to zero.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:11:59 +08:00
Fini 1bf5f9b8cc fix(renderer): register pan-cache strips to the layer anchor and refresh on decode
Two independent defects in the pan bitmap cache showed as misregistered
bands while dragging at high zoom and photos stuck on their blur-up
placeholders. Strips repainted during an in-place scroll used the live
viewport pan while the layer stays anchored at the snapped cache pan, so
every threshold crossing baked a band at twice the residual; the restore
rebase also vacated a margin strip nothing ever repainted. And a decode
landing on the worker mutates no editor state, so a sharp layer kept
serving stale placeholder pixels forever. Strips now paint against the
cache anchor, vacated margins join the progressive restore, and a raster
generation stamp reopens the restore when new pixels install.

Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
2026-07-28 22:11:36 +08:00
Fini 878a393517 fix(ci): satisfy the fmt gate and the version-literal fixture rule
The rebase port left a hand-wrapped import list rustfmt disagrees
with, and two restored-comment references to the current product
version trip the version-sync fixture rule — reflow the imports and
reword the comments without the literal.
2026-07-28 00:56:22 +08:00
Fini 9354d786cb fix(web): import the scroll helper the popover wheel path uses
Leftover from porting the color-variable popover onto the unified
scroll tier: the web host's popup interception references
scroll_by_max, which only default-feature builds resolved via an
unrelated re-export — import it directly so the canvaskit build
compiles too.
2026-07-28 00:49:57 +08:00
Fini 225c12d1ce feat(i18n): complete locale coverage for batch export and match-ratio keys
The catalog integrity gate requires every locale to carry exactly the
English key set — the eight keys added for batch frame export and the
image-ratio action existed only in en / zh-CN / zh-TW. Translate them
across the remaining twelve locales (placeholders preserved) and bump
the intentional catalog size.
2026-07-28 00:49:57 +08:00
Fini d539576f83 fix(editor): route escape through the color-variable popup on native too
The popover's escape layer was applied to the web host but dropped
from the native ladder while porting across the unified
keyboard_escape split — close the popup (and reset its scroll) one
layer before the fill-type dropdown, matching web. Also rustfmt
normalization of the rebase port sites.
2026-07-28 00:36:35 +08:00
Fini b567a6a027 docs: refresh image-fill popover description and add the gl-host test invariant
The stale 'Image — fill row' line made the crop/adjust feature read
as unimplemented; describe the actual popover. Record that
op-host-native tests need --features gl-host — the default run
compiles out widget_host entirely and covers none of it.
2026-07-28 00:29:39 +08:00
Fini 25a38f4c00 fix(ai): point the latin line-height tiers at the cjk rules
text-rules and cjk-typography co-load on every Chinese request with
contradictory lineHeight tiers and no cross-reference — the model
picks one silently, and a wrong pick renders cramped headings rather
than an error. Mark the tiers latin-only and route CJK text to the
cjk-typography values, keeping latin digits and labels inside a CJK
design on the latin tiers.
2026-07-28 00:29:39 +08:00
Fini cb8a2624ba fix(editor): align the font picker's wheel direction with every other list
The inspector's font dropdown scrolled against the wheel: it added
delta to its offset while the settings-panel font picker — same
widget, same scroll field, same file — and a dozen other scroll
surfaces all subtract. Flip it on both hosts and pin the convention
with direction tests that drive the real wheel entry point and
require the rail popups to move the same way as the panel behind
them. Also gate agent_indicator_test_support behind the same cfg as
its only consumers inside gl-host'd widget_host, so a default-feature
clippy run is clean without blanket allows.
2026-07-28 00:29:39 +08:00
Fini d3a7191848 fix(editor): float the color-variable picker in a scrolling popover
The variable-binding list was emitted by the section walker as plain
panel rows — no height cap, no viewport, no scroll — so forty
variables inflated the inspector past the window and the clipped tail
stayed unreachable (yet still clickable through the clip). Rebuild it
as an anchored overlay in the same family as the fill-type and font
pickers: 320px cap with its own wheel scroll, outside-click and
Escape to close, name column ellipsized against a right-aligned hex
column so long tokens never overlap their value. Bind/unbind emission
is unchanged, the stroke-section picker shares the implementation,
and a regression test compares every other panel rect open-vs-closed
so the inline-inflation shape cannot come back.
2026-07-28 00:29:39 +08:00
Fini 853369006d test(editor): cover the frame-path image fill controls
Every image-fill popover test built its fixture on a rectangle, but a
dropped screenshot lands on a frame — a different SectionCapabilities
branch. A regression there would present exactly as 'the dropped
image can't be adjusted' with no red test. Walk the real chain once
from apply_image_drop on a frame through the fill row, mode chips and
crop-editing gate.
2026-07-28 00:29:39 +08:00
Fini 9a5c387c63 feat(editor): match a node's height to its image fill ratio
A wide screenshot dropped into a tall frame and viewed in Fit mode
letterboxes, and closing the gap meant computing width x aspect by
hand. Add a 'Match image ratio' action to the image-fill popover:
keep the current width, write the aspect-derived height through the
same property-edit path a manual handle drag uses — one undo step,
selection kept. The source dimensions come from the fill's
originalSize, falling back to the encoded image header via the
popover's shared byte cache, which also serves standalone image nodes
and legacy fills that never recorded a size; with no resolvable size
the row is disabled and produces no hit rect.
2026-07-28 00:29:39 +08:00
Fini 1f93cc8c0a feat(desktop): export every frame of a set in one action
A five-frame carousel took five rounds of select-frame → export. Add
File ▸ 'Export all frames' (and 'Export N frames' when several are
selected): one directory pick, every visible top-level frame written
as a PNG named by document order, per-frame failures collected into
one bilingual summary instead of aborting the batch. The planner
strips a frame-name ordinal only when it equals the assigned index, so
'01 Cover' at position one exports as 01-Cover.png while reordered
frames keep both numbers. Batching wraps the existing single-node
raster exporter — the Export image dialog and its pipeline are
untouched — and web hosts leave the capability flag off, so no dead
menu row appears in the browser.
2026-07-28 00:29:39 +08:00
Fini 322ae23760 feat(editor): drop an image file onto a node to set its fill
Dropping a png/jpg/webp used to be silently ignored — placing a
screenshot meant importing, dragging into position and matching sizes
by hand. Now a drop resolves the deepest fillable node under the
cursor (walking up past text and icons), writes a cover image fill in
one undo step, and rings the target while hovering so the outcome is
visible before release. Empty-canvas drops insert an image node at the
point; document drops keep their open/import behavior. Drag positions
come from an AppKit probe on macOS; other platforms degrade to a
centered insert.
2026-07-28 00:29:39 +08:00
Fini 80d9b2aca3 feat(agent): surface a quality credential after generation
The cleanup passes and blocker detectors already run on every
generation, but their work was invisible: problems produced nudges
while a clean result said nothing. Count each accepted document edit
per check category through a DocSink decorator (pass bodies untouched),
carry the tally through loop finalize and the orchestrator's progress
events, and render an honest completion line — categories actually
run, repairs actually applied, open issues never papered over. A run
with nothing to fix still earns its positive credential; a turn that
checked nothing reports nothing.
2026-07-28 00:29:39 +08:00
Fini 1ab8c0cf01 fix(editor): advance the document revision in canvas drag mutators
The layout-scene cache keys on a revision counter, so any mutator that
edits the tree without bumping it leaves the canvas painting a stale
scene: handle-resize, rotation, pen and path-anchor drags all updated
the document every frame while the cache kept answering 'unchanged',
and the new geometry only appeared after some later click bumped the
counter. Register the change inside each mutator (gated on an actual
write), document the contract on mark_document_changed, and pin it
with an end-to-end test that settles the cache first and then asserts
the painted scene follows the drag within the same frame.
2026-07-28 00:29:39 +08:00
Fini 864753b950 test(desktop): keep persistence tests out of the real user config
Desktop tests exercised agent_connect_store against the real
~/.openpencil, so a parallel test run could leave the user's actual
reconnect list in whichever state the last writer happened to save.
Add a process-level user-root override to op-config-store (a OnceLock
seam, not an env var — set_var races parallel test threads) and a
guard at the desktop store entry points that redirects every test
write to a per-pid scratch root, so isolation does not depend on
which test runs first.
2026-07-28 00:29:39 +08:00
Fini e6667b77c1 fix(desktop): stop failed probes evicting remembered connections
Every probe outcome wrote the connected flags through to agents.json,
so one bad launch — a GUI environment missing the user's shell env, a
machine offline at login — persisted connected=false and removed the
provider from the startup replay list for good; the user had to press
Connect every launch. Treat the store as the last-known-good list:
only successes join it, and an explicit Disconnect is recognized by
the card's phase returning to Idle rather than by the flag snapshot,
which also spares providers still queued for replay. The replay queue
now advances through begin_provider_connect — a bare request-seam
write left the card outside Probing, so the landing hook discarded
the outcome and stranded every provider after the first.
2026-07-28 00:29:39 +08:00
Fini 103cf81217 fix(agent): give networked model probes a budget past the cli's own error
The shared 10s probe budget cut off agy's real failure message, which
takes just over 11s to arrive when the process carries no proxy vars —
the card showed a bare timeout one beat before the actionable 'please
sign in' would have landed. Keep 10s for the local version gate and
give the models step, a network round trip, 20s in both the connect
and discovery chains.
2026-07-28 00:29:39 +08:00
Fini c4300d100b fix(agent): surface the cli's own error instead of 'not responding'
cli_version folded spawn failures, non-zero exits and timeouts into
one None and discarded stderr, so a codex launched without the user's
PATH — whose shebang dies with 'env: node: No such file or directory'
— read as an unexplainable 'Codex CLI not responding'. Return a
structured failure carrying the output tail and word the card message
per cause, draining both pipes on capture threads.
2026-07-28 00:29:39 +08:00
Fini bb46ec68af fix(agent): capture the interactive login shell env for gui launches
The login-shell probe ran 'zsh -lc', which reads .zprofile but not
.zshrc — where homebrew, nvm and proxy exports conventionally live —
so a Dock launch still missed node for shebang CLIs and stalled
networked probes behind a proxy-only route. Switch the probe to -ilc
under a hard 8s deadline (an unbounded interactive rc could hang
startup), and graft an allowlisted set of proxy variables onto the
process alongside PATH, never overwriting values already present.
2026-07-28 00:29:39 +08:00
Fini d3f2f52933 fix(agent): bound pipe capture when a probe kill leaves grandchildren
Killing a timed-out CLI does not close pipes its own children still
hold, so the drain thread's join waited on the grandchild instead of
the deadline — a 400ms probe budget measured 30s wall clock. Replace
drain_pipe with PipeCapture: normal exits still join to EOF, kill
paths take the captured bytes after a short grace instead of joining.
2026-07-28 00:29:39 +08:00
Kayshen-X 693423f2f9 fix(ai): constrain retry replay to failed turns 2026-07-27 21:35:09 +08:00
Adrian Rojas 0748002045 fix(ai): normalize scripted modification nodes 2026-07-27 21:31:17 +08:00
Adrian Rojas c333d85e8b fix(ai): preserve modify context across retries 2026-07-27 21:30:17 +08:00
Kayshen-X 90870370ad refactor: typed error enums across the whole workspace
Completes the stringly-error conversion: 341 remaining
Result<_, String> signatures drop to two documented boundary sites
(a String-compat wrapper consumed across a crate boundary and a
test-only diagnostic helper). Eighty-plus enums follow the
established recipe — one enum per failure domain in a sibling
module, byte-identical Display text, From impls replacing map_err
adapters. mcp_live is fully typed (the screenshot channel included)
and all six temporary String bridges are deleted. The flaky
cli-model-discovery trio is made load-proof: exec'd sleeps so the
probe's kill actually closes the pipes, a serializing test lock,
and an escalating budget with a deadline-boundedness assertion.
2026-07-27 21:09:06 +08:00
Kayshen-X 2f7040cb58 docs: refresh the architecture guide after the dedupe and typed-error campaigns
Adds the campaign stale-path banner (839f474a..d2d8104c), documents
op-util, the shared host-logic module tables, the three input-ladder
spines, the i18n panel-shard key workflow, block_on_anywhere, and the
typed-error recipe; deletes claims the reorg made false (old file
splits, 113-tool count, shell-core paths, five-file property panel).
Every path named was existence-checked against the tree.
2026-07-27 21:08:34 +08:00
Kayshen-X dcf8c3b1b3 fix(desktop): support Wayland image clipboard paste 2026-07-27 21:08:15 +08:00
Kayshen-X 3064554ff4 fix(codegen): preserve generated results across framework tabs 2026-07-27 21:04:23 +08:00
Kayshen-X d2d8104c57 refactor(editor): decompose the three input-ladder god methods into ordered tiers
apply_cursor_move (1141 lines) and both hosts' apply_press (1052/897)
become short spines calling Option-returning tier helpers in sibling
modules, preserving the documented hit-test priority order exactly —
verified mechanically: every tier body is byte-identical to its
original line range after undoing the three systematic transforms,
and the spines invoke tiers in strictly ascending original order.
No file in the workspace exceeds the 800-line cap anymore.
2026-07-27 00:11:49 +08:00
Kayshen-X def6a8bcdd refactor: final 800-line sweep across editor, mcp, services, and tool crates
Pure code motion: twenty-eight remaining oversized modules split into
spine + sibling layouts with re-exports keeping every import path and
test name stable. op-codegen, op-smoke, op-mcp, op-design-lint,
op-host-services, op-host-desktop, op-cli, op-i18n, op-editor-core,
op-editor-ui, and op-pen-loader no longer have any file over the cap.
2026-07-26 22:34:37 +08:00
Kayshen-X 9b988d9620 refactor(editor): finish widget_host unification leftovers and split host files
Agent-settings press logic is single-sourced with an explicit
credential-ownership scope (operator vs browser); the blank-press
blur path uses the full commit chain on both hosts; the marquee
release commit is shared (web was missing the entered-container sync
— a real bug); sixteen inline screen-to-doc conversions route through
one geometry helper; the wall-clock-zero file-menu anchor is verified
safe and documented. Host spines, keyboard, input tests, agent
settings tests, and the skia backend split under the 800-line cap;
press_helpers drops from 837 to 165 lines.
2026-07-26 22:30:15 +08:00
Kayshen-X 83005257cd refactor(editor): single-source widget_host cursor/drag, misc pairs, and spines
Completes the widget_host fork unification: drag commit/preview
policy, image-crop gesture, scroll handlers, cursor-hover flows,
account/a11y/missing-fonts/variables-panel presses, drag-state
structs, and the canvas/overlay geometry (the coordinate invariant
now lives in one file) are all shared between the native and web
hosts. Proven drift fixed with history evidence: native shift-drag
skipped the undo snapshot, native painted two variables menus at once
(stale row-menu clears), commit sequencing dropped drafts on
Resize/Close, and the per-press full-document variable-table walk is
replaced by direct state reads. Intentional platform differences
remain host-side with comments.
2026-07-26 20:32:43 +08:00
Kayshen-X 6c8f41dc0e refactor(mcp,web): typed errors for mcp_serve, export, and batch direct ops
McpServeError separates protocol faults from socket failures;
ExportError carries structured fields with byte-identical Display;
ProgramError gains precise InvalidValue/ValueOutOfRange variants.
Fifteen map_err adapters in the web canvas server collapse into From
impls. The flaky cli model-discovery trio is fixed at the root: the
test scripts now exec their sleep (the forked child used to inherit
the probe's pipes and hold the reader for the full duration) and poll
with a doubling budget, turning an assumed deadline into a proven one.
2026-07-26 20:32:26 +08:00
Kayshen-X 5b996a93b3 refactor(editor): single-source widget_host press, keyboard, and dispatch families
The native/web widget_host twins' press/click, keyboard, and
property/image dispatch layers now share one implementation
(op-editor-core host_*_transitions for pure state, op-editor-ui
press_flow/chat_click_flow/property_panel_dispatch for widget-typed
logic); hosts keep thin platform arms. Proven drift fixed along the
way: web's Interactions actions were silent no-ops, web left font
pickers open behind property popovers, native's image-src writes
skipped the pan-cache invalidation, and FocusEffectParam dropped the
previous draft on native. Intentional platform differences stay
host-side with comments.
2026-07-26 17:18:29 +08:00
Kayshen-X 6f242bd1a8 refactor: split every oversized module into sibling files
Pure code motion across seven crates (orchestrator, host-services,
host-desktop, editor-core, pen-loader, editor-ui, mcp, canvaskit):
spine keeps the public surface, siblings take cohesive clusters,
re-exports keep every import path compiling unchanged. Test counts
are identical before and after per crate; op-orchestrator and
op-host-desktop no longer have any file over the 800-line cap.
2026-07-26 14:58:19 +08:00
Kayshen-X 961a149c8a fix(editor): finish block_on_anywhere rollout and narrow design-ack snapshots
Every remaining private current-thread runtime and bare block_on in
sync code now routes through block_on_anywhere, removing the latent
'runtime within runtime' abort on tokio workers. The desktop design
pump's per-apply-ack full EditorState clone (and the subtask-retry
seed) switch to the narrowed snapshot — chat/codegen/theme_presets
grow with session length and are read by nothing in the worker mirror.
2026-07-26 14:58:12 +08:00
Kayshen-X c188205368 refactor(editor): split editor_ui_state into sibling modules and narrow snapshots
Spine + seven siblings (all under the 800-line cap) with re-exports
keeping every import path stable; three measured field groups become
substructs (preview, size toggles, design-md panel — 192 -> 176 flat
fields; larger clusters rejected by workspace touch-count); MCP and
chat-launch request snapshots drop chat/codegen/theme_presets, which
grow with session length and are read by no consumer.
2026-07-26 13:21:06 +08:00
Kayshen-X 0fc6753dd9 refactor(cli,mcp,web): typed error enums for the densest stringly paths
CliError/SkillInstallError carry every op-cli failure (94 sites),
ProgramError types batch_program (14), WebCanvasError maps route
failures onto their existing HTTP statuses (23 -> 6 deliberate
boundary adapters). Display output is byte-identical to the old
strings so caller-visible text and tests are unchanged.
2026-07-26 13:20:59 +08:00
Kayshen-X 621f30e425 fix(editor): async hygiene — safe block_on, bounded acp channels, thread teardown
One block_on_anywhere helper (block_in_place inside a multi-thread
runtime, shared runtime otherwise, loud failure on a current-thread
runtime where both options deadlock); acp transport channels bounded
at 1024 with reader-safe try_send; a stranded child process + reader
thread leak in model discovery fixed; detached threads either joined
or documented; Antigravity/Grok probe strings localized (catalog 1105).
2026-07-26 13:19:34 +08:00