Skip to content

fix(ui): let New project choose a folder without a custom name - #4

Closed
Master-Frank wants to merge 56 commits into
mainfrom
cursor/optional-new-project-name-e179
Closed

Master-Frank wants to merge 56 commits into
mainfrom
cursor/optional-new-project-name-e179

Conversation

@Master-Frank

Copy link
Copy Markdown
Owner

Summary

Creating a project in the desktop app required a custom name before Choose folder would enable. An empty name is a valid choice and means “name this project after the folder I pick.”

NewProjectDialog now submits with the name field empty. Choose folder stays available either way. A typed name is still trimmed and applied in the same projects.add step; a blank name is omitted so the existing folder-basename fallback in projects.add applies. The en / zh-CN / zh-TW copy no longer says the name has to come first.

Fixes apache#6047

Verification

  • npx biome check on the changed files
  • npm --workspace @maka/ui run build and node --test packages/ui/dist/__tests__/new-project-dialog.test.js (4 pass)
  • node --test packages/ui/dist/__tests__/conversation-copy.test.js (7 pass)
  • npm run check:locale-hygiene
  • Desktop renderer typecheck (tsc -p apps/desktop/tsconfig.renderer.json --noEmit)
  • node --test --test-force-exit apps/desktop/dist/main/__tests__/project-add-restore.test.js apps/desktop/dist/main/__tests__/task-entry-provider-scope.test.js apps/desktop/dist/main/__tests__/task-entry-controller.test.js (80 pass), including the new “Settings Add with a blank name still opens the folder picker” case

Not run: Electron desktop app / folder-picker UI. Native directory choosers are not available in this environment, so the behavior is checked through the dialog and Settings registration tests rather than a live window.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope:

Cursor Grok 4.6 implemented the dialog, copy, caller wiring, and tests from the issue’s planned direction. A human should review before merge. CONTRIBUTING currently asks not to use Grok for Maka contributions pending terms clarification (apache#5746); this PR discloses that use rather than hiding it.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No
Open in Web Open in Cursor 

chihumyum and others added 30 commits October 9, 2026 22:43
…st only (apache#5985)

The health-probe test used one service with probeTimeoutMs: 100 for both
the normal HEAD-to-GET fallback and the deliberately stalled request. The
budget covers the whole probe, so the fallback's two round trips also had
to finish within 100 ms; on a loaded runner the /ready probe timed out
(18 of 100 runs under 4x CPU oversubscription locally, 0 of 100 after).

Check the fallback with the default probe timeout and keep the 100 ms
budget for the stalled request it is meant to bound. Product health-check
code is unchanged.

Generated-by: Codex
test(runtime-host): bound the health probe fallback check at 5 s

The fallback check used the 30 s production default, so a regression
that hung the GET fallback would stall the test for 30 s before failing.
A 5 s budget still sits far above any load-induced latency.
…ailure (apache#5984)

WslProcessByteStream forwarded only the ChildProcess error event to the
framed transport's failure listener. The bridge's stdin and stdout emit
their own error events (EPIPE after wsl.exe dies, a destroyed stream),
and nothing listened for them, so a broken bridge pipe escaped as an
uncaught exception instead of failing the connection attempt.

Route stdin and stdout error events through the same listener. The
transport fails once and the connect call rejects with handshake_failed,
like the existing cancellation and oversized-stderr paths. stderr is read
through an async iterator that already absorbs its errors.

Generated-by: Codex
test(runtime-host): cover a real WSL bridge EPIPE on a pending write

The existing cases destroy a bridge pipe with no write pending. In
production the common shape is a reader that goes away while the
handshake is being written, where Node reports EPIPE to the write
callback and also emits a stdin error event. On main that event escapes
uncaught even though the write callback fails the transport.

The new case closes the bridge's stdin before connecting, so the
handshake write hits a real EPIPE. It fails on main and passes with the
stream error routing. The pipe-error cases now use the 10 s handshake
budget of their siblings.
…pache#5972)

Limit Candidate cleanup to preparation before Kernel.start takes ownership.
Keep the root owner held after startup cleanup hits the fail-stop deadline,
and cover preparation failure, clean recovery failure, and late cleanup.

Generated-by: Codex
fix(runtime-host): drop unreachable Candidate authority cleanup

The Candidate's preparation catch also closed the access authority, but
opening it is the last fallible preparation step. When the open rejects
there is no authority to close, and once it resolves only the options
literal runs before Kernel.start takes the authority and owns its
cleanup. Remove the dead close and note the ordering at the open, so a
later edit does not add a fallible step after it.
fix(runtime-host): keep Kernel-retained root owned in service startup

startExecutionRuntimeHostService closed the State Root owner on every
startup failure, including after RuntimeHostKernel.start had taken it.
When Kernel cleanup overruns the shutdown deadline, during startup or
in the post-start peer-termination close, the Kernel deliberately keeps
the owner until process exit, so the service released a root while the
composition could still be closing. Release the owner only for failures
before Kernel.start, as the Candidate does. Peer endpoint and mesh
cleanup stays, since the Kernel adopts those only once its listener set
exists.

Run the Candidate's clean recovery failure and deadline overrun tests
against the service too, and cover a service preparation failure
releasing the root.
…pache#5974)

Exercise the reader Retry callback through the observation lifecycle and real transcript controller. Cover initial recovery failure, repeated Retry, session handoff, observation replacement, and unmount.

Generated-by: Codex
fix(desktop): retry history through the active observation

Keep the observation controller available before the first transcript publication so Retry can recover after the initial read and automatic recovery fail. Fence retry admission and late errors by session selection and observation identity.

Generated-by: Codex
fix(desktop): report a failed transcript Retry once

The range controller's reload() hands a failure to its recovery, which
reports it through the observation's onError (the "Failed to load task"
toast and read error), and then rethrows. retryMessages reported the
rethrown error again as a refresh failure, so one failed Retry raised
two toasts and replaced the read error with the refresh message.

Leave the report to the controller, as its own gap and recovery reloads
do, and only release the pending Retry. The controller still withholds
a reload that lands on the cached transcript, so Retry stays quiet then
too. refreshMessages never calls reload() and keeps its own report.

The retry test now records error toasts and checks that a failed Retry
raises exactly one, keeps the read error, and that a successful Retry
raises none.
fix(desktop): answer a failed Retry over the cached transcript

The range controller does not report a failed reload while it holds the
cached transcript Main serves during Host reconnection. Since Retry left
reporting to the controller, a Retry clicked there (for example after a
refresh had set a load error) failed with no feedback at all.

Expose that state as holdsCachedTranscript() on the range controller and
its conversation port, reusing the controller's own cached check. When
reload() rejects, retryMessages now reports the refresh error itself only
if the controller holds the cached transcript and the observation is
still current and selected. Every other failure is still reported once,
by the controller's onError.

The retry test can now answer a read with a cached snapshot. It shows a
failed Retry over that snapshot raising one refresh toast, alongside the
existing single load toast for the live case. The other conversation
test fakes return false for the new method.
…5976)

Track initial acquisition alongside reconnect work and complete closed only
once all owned cleanup has settled. Register tasks before callbacks can
reenter, and keep explicit cancellation out of the fatal error path.

Close Desktop candidates that fail admission before handoff, and prevent
removed target generations from reactivating or publishing stale failures.

Generated-by: Codex
test(desktop): cover a replaced Local generation's late failure

The ownership guards were only exercised with the manager closed. The
initial Local start is not a serialized target mutation, so once it
marks its generation invalid, retryLocalStart can replace that
generation while the old #markUnavailable still waits on observation
teardown.

Hold that teardown open from the startCandidate seam, let the retry
reach ready, then release the old generation. Its epoch stays inactive
and its late unavailable state is not published over the replacement.
Without the #publishState ownership guard, the old generation publishes
unavailable after the replacement is ready.
… times (apache#5975)

Generated-by: Codex
fix(runtime-host): stop delivering waiting notifications after expiry

A notification fire waiting for a Desktop provider kept its claim after
its task expired. The scheduler then polled every five seconds for as
long as no provider appeared, and a provider that connected later, or a
Host recovering the claim, still delivered the reminder after expiresAt.
Expiry already closes due-time and Trigger Now admission, so a native
fire that has not crossed delivery admission now settles as blocked once
its task expires, which also ends the poll. A once task completes with
that blocked run, as an incognito-blocked fire does; a recurring task
ends expired.

Trigger Now refreshes the retry schedule after persisting a waiting
claim. A Store read failure there replaced the provider-unavailable
outcome with a generic persistence failure. The refresh failure now
drains the Host, whose recovery retries the durable claim, and the
caller still receives the provider-unavailable outcome.

Tests cover expiry of once and interval notifications, recovery after
expiry, the Trigger Now refresh failure, and the next slot of a
recurring notification delivered late.
test(runtime-host): pin expiry edits replacing a waiting notification

The expiry check for a waiting native fire reads the task snapshot held
by its claim. That snapshot cannot hold a stale expiresAt: the Store
rejects every task edit while a claim exists, and an update first
cancels a fire that is still waiting for a provider. The edited task is
then claimed again with the new expiry.

Cover shortened, added, and extended expiry during a provider wait. A
shortened or added expiry blocks the next claim at the new deadline
without a late delivery. An extended expiry delivers once a provider
connects after the original deadline. With both edit guards removed,
all three cases fail because the old claim keeps its stale snapshot.
test(runtime-host): cover recurring trigger and bot notification waits

Trigger Now on an interval notification without a provider now has
coverage. The caller gets the provider-unavailable outcome. The claim
retries every five seconds while its wait spans a regular slot, which
adds no second claim. After one delivery the task resumes at the next
regular slot without catching up.

A due bot notification waits for a provider like a local one and is
then delivered once through notify_bot with its body, platform, and
chat. A stored bot task whose platform has no bot delivery, which
create rejects, is blocked at its due time instead of waiting, and a
provider that connects later delivers nothing.
…che#5973)

Preserve stable identities and source order while SQLite allocates target-local tool journal and message admission keys. Cover populated and fresh imports, cancellation identity, and rollback/retry after a later stable identity conflict.

Generated-by: Codex
test(storage): derive the import's local-sequence guard from the schema

LOCAL_SEQUENCE_COLUMNS is hand-maintained. A later Session table keyed
by an INTEGER rowid would be copied verbatim by the import and collide
only when importing into a populated workspace, with nothing failing in
CI.

The new test exports a real Session bundle, migrates a target the way
the import opens it, and walks every table the merge copies. Each one
keyed by a single INTEGER column must be reallocated through
LOCAL_SEQUENCE_COLUMNS or listed in the test's PORTABLE_INTEGER_KEYS
with a reason; that allow-list is empty today. A listed column that is
not the table's key also fails.

So the test reads the merge's own answer rather than a copy of it, the
table selection in mergeAttachedBundle moves unchanged into the exported
listSessionBundleMergeTables, which the merge now iterates.
test(runtime): prove the import's ORDER BY keeps local sequence order

tool_journal_events.journal_seq and message_admissions.sequence alias
the rowid, so a scan of the attached bundle returns rows in sequence
order however they were written. The existing order tests therefore
pass with the import-side ORDER BY removed, and rewriting the bundle
rows out of order cannot change that.

The new test turns on PRAGMA reverse_unordered_selects on the
connection the import borrows, imports into a populated workspace, and
checks that both tables keep the source order with sequences allocated
after the target's own. Without the ORDER BY, either table comes out
reversed. An unchanged PRAGMA data_version confirms the import
committed through that connection, so the test cannot pass vacuously
if the import stops sharing it.
Merge remote-tracking branch 'upstream/main' into fix/session-bundle-local-sequences

# Conflicts:
#	packages/storage/src/__tests__/session-bundle-policy.test.ts
…e#5986)

Pipe-mode commands are spawned detached, so each leads its own process
group and nothing stops it when the Host dies without running cleanup
(SIGKILL, crash, OOM kill). The command is reparented and keeps writing
to the workspace with no owner left to stop it or record its result.

Start each bounded and background pipe command through a small owning
supervisor (owned-process-main.js, launched with process.execPath, as
Electron in Node mode under Desktop). The supervisor leads the process
group, spawns the command into it with the Host's stdio and descriptor
payloads inherited directly, and holds a private IPC channel to the Host
as a lifetime lease; when the channel disconnects it SIGKILLs its tree.
Host-initiated signals still reach the group once, and the command's exit
code or signal is mirrored, including SIGPIPE and SIGUSR1, which Node
would otherwise ignore or repurpose.

Keep the shell run's reported pid the command's own, so `kill <pid>`
behaves as before; group signalling uses the supervisor. Require the
supervisor entry in CLI release artifacts.

Each running pipe command now costs one extra process: about 26 ms per
command under Node and 75 ms under Electron on an idle Apple Silicon
machine, and 13-15 MB of physical footprint while it runs.

Generated-by: Codex
Generated-by: Claude Code
ci(windows): follow the win32 branch into owned-child-process

The Windows recovery path filter must equal the win32-branching closure
of the suites that lane runs. The platform branch for spawning pipe-mode
commands moved from shell-exec.ts and pipe-process-driver.ts into
owned-child-process.ts, so the filter now lists that module instead.
fix(runtime): hold supervised command output until admission

A supervised command writes to the Host's pipes directly, while its
admission arrives over the supervisor's IPC channel, so the Host could
read output before `ready` resolved. A foreground Bash aborted on its
first output then failed startup with an AbortError instead of
returning a cancelled result, as seen on Linux CI. With a direct spawn
the spawn event always preceded any output.

Pause stdout and stderr until admission has been observed. Release them
in the check phase after `ready` resolves, or when startup fails or the
supervisor exits.

The workspace executor abort test now aborts on the command's first
output instead of after a fixed 100 ms, which also had to cover the
supervisor's startup on a loaded runner.
fix(runtime): harden the command supervisor's exit, signals and launch

After the supervisor exits without reporting, its PID may be reaped and
reused, so walking its tree could reach unrelated processes. Signal only
its POSIX process group, which cannot be reallocated while members
remain, and skip taskkill on Windows. Decide such an exit once the IPC
channel has drained rather than on `exit`, and report a normal exit
without a result as an integrity error instead of the command's status.

The command shares the supervisor's process group, so a script that
signals its own group could end the supervisor or start its inspector.
Ignore HUP, USR1, USR2, QUIT and ALRM in the supervisor on POSIX.

Spawn the command in the check phase after the launch message, so a
group stop pending in the same turn is handled before the command
starts.

Tests cover a script trapping and sending group signals, a lost lease,
and a setsid descendant of a live command on owner death.
ci(windows): run the shell command ownership suites on Windows

Every Windows Bash command now runs under the owning supervisor, but no
Windows lane ran its suites. Add owned-child-process, shell-owner-death,
pipe-process-driver and shell-exec to windows_recovery, and list their
Windows-branching test sources in the lane's path filter.
fix(runtime): detach the Windows supervisor and keep forced exits quiet

The first Windows run showed two problems.

On Windows the Host's forced stop is taskkill, which ends the
supervisor with an ordinary exit code. Treating every non-zero exit
without `completed` as a lost result turned timeouts and aborts into
integrity errors. Only a clean exit without a result is reported now.

libuv puts non-detached children in a job that is killed with their
parent, but not their own children. A non-detached supervisor therefore
died with the Host before it could stop anything, and a shell root's
child survived, as on main. Detach the supervisor on every platform.
It now outlives the Host long enough to stop its whole tree; the
command stays in the supervisor's job.

Add a `wrapped` owner-death case, a shell root running the script as
its child, which fails on main. Skip the stop-requested variant on
Windows, where a stop is an immediate forced kill. Accept EOF for a
broken stdin pipe on Windows. Drop shell-exec from the Windows lane:
its suite uses POSIX shell syntax throughout and fails on Windows
without this change.
fix(runtime): report supervisor faults and pin the Windows ownership lane

A `failed` message means the supervisor is already exiting on its own,
so a tree walk from its PID could start after it is reaped. Signal only
its process group on that path, as after an exit.

On POSIX the Host's own stops end the supervisor by signal, so an exit
code without `completed` can only be a supervisor fault. Report it as a
lost result there, rather than as the command exiting with that code.
Windows keeps only a clean exit, since taskkill also ends with a code.

The Windows ownership step now asserts its TAP counts, like the other
gates in that job, so a widened POSIX-only skip cannot leave it green
while it tests less.
fix(runtime): report a supervisor fault before stopping its tree

An uncaught exception or rejection in the supervisor stopped its tree
with its own group's SIGKILL, so the Host saw a signal exit it could not
tell from a forced stop, and reported it as the command being killed.
While the lease is still up, report the fault as a failure first, then
stop the tree. A one-second fallback stops it even if the report hangs.

The test plays the Host against the real supervisor, injecting the fault
through a preload rather than a product hook. The Windows ownership step
now expects 19 tests, 11 passed, 8 skipped.
fix(runtime): start a bounded command's timeout at its admission

The bounded runner started its timeout when it spawned the supervisor,
so the supervisor's startup consumed part of the command's budget. On a
loaded Linux runner a 100 ms timeout fired before the command had run
at all, and the escalation test found no heartbeat file. With a direct
spawn the budget belonged to the command, and the background runner
already arms its timeout after admission.

Start the timer once the supervisor admits the command. A supervisor
that never admits it still fails through its own startup timeout.
fix(runtime): let a faulting supervisor finish its own tree stop

Since a fault after admission is reported as `failed`, the Host's
immediate group kill on that message hit the supervisor while its own
tree walk was still reading the process table, so descendants that had
left the group survived. On `failed` after admission the Host now only
reports the error and leaves the stop to the supervisor. The group kill
on its exit stays the backstop. A spawn failure before admission still
signals the group at once.

A fault while the `completed` report is in flight no longer sends
`failed`, and the Host ignores a `failed` that follows `completed`, so a
valid result is never turned into an error.

Tests cover the Host leaving a reporting supervisor alive with the
backstop still stopping the command, and the supervisor's walk reaching
a setsid descendant on a fault. The Windows ownership step now expects
20 tests, 12 passed, 8 skipped.
Merge upstream/main into fix/shell-owner-death

Main renamed the filesystem worker bundle to .mjs; keep that name and
this branch's owned-process-main.js entry in both release file checks.
The supervisor runs in the Host's context, outside the Windows sandbox,
so the module-type lookup that broke the worker does not apply to it.
)

Generated-by: Codex
fix(runtime): keep graph stops exact without wedging or widening them

Address review of the activation-scoped graph stop:

- Public turn.stop, Host shutdown, WorkHub and whole-graph supervisor
  stops call stopRoot without a scope again, so they keep the
  Session-level Runtime stop that also reaches a claim whose Run has
  not attached yet. Only stopAgentGraphActivation asks for
  { scope: 'run' }.
- A hosted graph claim counts as Host-owned only once the Host
  admission gate admits it. The gate runs in the Session admission
  lease right before the durable admission and refuses a claim whose
  local capability was stopped, so a stop between submission and
  admission cancels the activation instead of failing with "Turn was
  not admitted" while the child runs on.
- A terminal stop target reaches the runtime only while a failed stop
  still retains cleanup for that exact Run, and a stop that succeeded
  in this reconciliation is not repeated on later passes or wakes.
  Historical targets no longer cost a Host stop per wake or fail the
  pass before new dispatch.
- A failed control pass during a live wave schedules a capped backoff
  wake (100 ms doubling to 5 s), so a stop failure that parks the only
  running child is retried without another schedule commit. A repeated
  failure for the same target replaces the earlier one without
  notifying the supervisor again.

Tests cover a public stop before Run attach and a graph stop before
durable admission on the real Host, historical and repeated stop
targets, and a retry without a wake. Pinning tests record that a
graph-supervisor Session stop lets an operator accept its next claim
while a user stop does not, and that a failed backend delivery is
reported to its stopper while the child's result stays authoritative.
fix(runtime): give exact graph Run stops their own Host method

An authority that ignored the optional `{ scope: 'run' }` argument of
stopRoot still satisfied RuntimeHostedRootAuthority, and a graph
activation stop through it would silently widen to a Session stop that
also cancels queued sibling claims. TypeScript accepts a function with
fewer parameters, so no option shape can catch that.

Replace the option with a required stopRootRun(identity, input). An
authority without exact-Run support is now a compile error; building
the tests caught one more Host fixture that forwarded only stopRoot.
RootTurnCoordinator.stopRoot is again byte-for-byte main's Session-level
stop, and stopRootRun carries the exact-Run delivery, redelivery and
completed-Run cleanup retry. SessionManager.stopAgentGraphActivation is
its only caller.
fix(runtime): report a stuck graph stop once and keep retrying it

A stop whose cleanup keeps failing parks its child, and so its wave,
while the backoff retries every 5 s. Each retry replaced the failure
silently, so the graph sat parked behind the first failure record.

Count consecutive failures per target. The eighth one reaches the
supervisor once, through the existing reconciliation failure
notification, with a distinct "is stuck after N consecutive failures
and is still retrying" reason that also lands in the durable client
failure. Eight failures take about 11 s, after the backoff reaches its
cap, so a fault that clears during the ramp is never reported as stuck.
The stop is never dropped, and a later success clears the failure and
resets the count.

A driver abort no longer ends the control loop while the wave is still
running. Before, the loop stopped retrying on the next wake after an
abort, so a child parked behind a transient stop failure kept the
aborted reconciliation waiting forever. Repeated control read failures
now notify once per streak as well.
fix(runtime): report a stuck graph control read once

A schedule control read that kept failing notified the supervisor on its
first failure and then retried every 5 s in silence while the wave stayed
parked, the same silent park the stop path no longer has. Count read
failures the same way: notify on the first and, once, on the eighth
consecutive failure as stuck and still retrying. A successful read resets
the streak and clears the failure.

The threshold is now CONTROL_STUCK_FAILURES, shared by stops and reads.
test(runtime): pin sibling stops past a stuck provider and the stop quarantine

Two review gaps, both test-only.

A graph stop pass now stops two children together, one of whose
provider neither returns from stop nor ends its send. The cooperative
sibling still reaches its aborted terminal while the other stop is
pending, so one stuck provider delays only the pass, not its siblings.
Serializing the stop calls makes this test fail.

A stop whose cleanup cannot persist its terminal keeps the operation
retained. An unrelated Turn on that Session, which holds no intent of
that stop, is refused as quarantined until a retry settles the
cleanup, and then runs. Widening the quarantine bypass to any attached
execution makes this test fail.
Merge upstream/main into fix/graph-schedule-stop

Keep both sides of the graph driver fields: the schedule change listeners
from this branch and the session activity projection from main.

Main's new coordinator tests build an AgentGraphCoordinatorRuntime without
stopAgentGraphActivation, which this branch makes required; give them the
same no-op stop the existing tests use.
…#6021)

A failed turn's banner and the provider retry notice both passed
`container="section"` to Astryx `Banner`. That variant is the page-level,
full-bleed, square-cornered surface, so the 800px message column showed a
hard-edged block of status color that read as a page alert instead of part
of the turn (apache#5799).

Drop the prop so both fall back to the default `card` container — the
standalone, rounded variant for content inside a column. Nothing else
changes: the retry notice keeps its `role="status"` live region with the
aria-hidden countdown (stable accessible name, see
`ModelProviderRetryIndicator`), and every `.maka-turn-failed-banner` rule
in styles.css was audited — all of them set block rhythm or style the
`<pre>` diagnostic, none compensate the section shape, so none is dead.

Fixes apache#5799

Generated-by: GLM-5.3-Flash (ZCode)
…indow (apache#6023)

Follow-up to apache#5890 (merged): the runner passes the same startedAt/settledAt
window to the completeness check and to the usage summary, and nothing
asserted it - the review noted a later edit could quietly separate the two
and re-open the delegated-Session undercount. The coverage stub now records
its calls and the residencies test asserts it received exactly the window
the usage summary read (100, 200 with the harness clock). Swapping the
arguments in the runner turns the assertion red.

Implements the non-blocking suggestion from the apache#5890 review.

Generated-by: GLM-5.3-Flash (ZCode)
`engines.node` was `>=22.19.0`, which semver-accepts Node 23.x. Node 23 never
received `node:sqlite` `DatabaseSync.isTransaction` (added in 22.16 / 24.0),
so `inspectOperationalStateSchema` in the storage layer reads `undefined`,
issues a nested `BEGIN`, and every State Root open fails with
`cannot start a transaction within a transaction`. npm does not warn because
the range is satisfied.

Tighten the range to `^22.19.0 || >=24.0.0`, make the release packaging
check reject major 23 explicitly, and align the contributor and CLI docs.

Generated-by: Claude Code
- SECURITY.md pointed at `web-search-boundary.test.ts` and
  `claude-subscription-ipc-boundary.test.ts`, both deleted in apache#1851; name
  the tests that enforce the IPC token boundary today.
- ARCHITECTURE.md described the Desktop conversation projection as "not yet
  implemented" while the linked document states it is implemented; the
  Chinese edition was also missing that reading-path entry.
- docs/tui-live-ctx-updates.md still said "Status: design" after apache#4550
  shipped `tui-context-refresh.ts`, and referenced `session-trace-refresh.ts`
  at its pre-move path.

Generated-by: Claude Code
…ner (apache#5810)

Concurrent openInteractiveContextOffloadStoreForWrite calls each awaited
the on-disk lease check before claiming or joining the per-lease single
flight. Those checks finish in no fixed order, so a later caller with
different limits could become the writer and reject the earlier callers.

Check the lease synchronously and claim or join the slot before the first
await; the root identity is still verified on disk before a writer is
returned. The single-flight test now awaits the conflicting open with the
others, and a new test replays the race to guard the ordering.

Fixes apache#5801

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#6006)

Since undici 8 (undici#1247) ProxyAgent forwards plain-HTTP targets to
the proxy in absolute form instead of opening a CONNECT tunnel, and
that forwarding semantics is the established contract on main: the
eval fixture explicitly asserts the absolute-form request line and
rejects CONNECT with 403. The reported 8-second hang (apache#5805)
does not reproduce on undici 8.11.2 (local scoped-fetch-transport
baseline 42/42 green; recent main CI runs green), and cancellation is
already covered at the connect level via withSocketCancellation and
buildAbortableConnector inside the dispatcher factory.

The fake HTTP proxy used by the success-path test answered every
request with a fixed 200, so the proxied HTTP path passed without ever
exercising a relay. Replace it with a real forwarding proxy that
records the absolute-form request line and the proxy-authorization
header as the bytes flow through, then relays the raw connection to a
live target server over a TCP pipe; the client response can only come
from that target. This also pins the credentials-ride-the-forward
contract on the HTTP path. Validated the test itself by temporarily
self-answering from the proxy (mimicking the old fixture): the relay
assertion fails until the pipe is restored.

Generated-by: GLM-5.3-Flash (ZCode)
…apache#5050)

select_text already returns a snapshot.selectedText payload, but the
shared observation type and renderer dropped it, so the model could not
confirm what was selected. Surface the field on CuObservation and write
it in the observation header, truncated like value and withheld when the
focused control is a secure field.

Generated-by: pi-coding-agent
test(runtime): cover selectedText length cap in observation header

The renderer already truncated like value. Review required a test that
fails if a 300-character selection is shown in full.

Generated-by: pi-coding-agent
Document the removed Trace database-path entry and current Markdown, diagnostic-report, and Session-bundle workflows in English and Chinese. Link the guide from the docs index.

Refs apache#4342

Generated-by: OpenAI Codex
Generated-by: OpenAI Codex
Signed-off-by: jjj-n <1357598741@qq.com>
docs(architecture): scope authority table to baseline and mark Windows evidence non-blocking
docs(architecture): refresh workspace schema version

Generated-by: OpenAI Codex
Signed-off-by: jjj-n <1357598741@qq.com>
docs(architecture): refresh workspace schema 18

Generated-by: OpenAI Codex
Signed-off-by: jjj-n <1357598741@qq.com>
Merge remote-tracking branch 'upstream/main' into codex/maka-3522-managed-workspace-doc-audit
docs(architecture): refresh workspace schema 20
…are live (apache#5957)

* fix(runtime-host): reject manual archive while WorkHub relationships are live

Manual archive committed isArchived without consulting two live
relationships. An active WorkHub delegation loses its visible target
because WorkHub candidate and result views exclude archived Sessions,
and a live ordinary linked child was left behind while the retirement
checks only covered turns, messages, interactions, goals, runtime
resources, derived effects, Agent Graph state, and scheduled tasks;
#readFamilySessionIds has never included ordinary linked children.

Guard the manual archive path (#setLifecycle with state 'archived')
before retirement admission: a batched active-delegation lookup per
revision family and a scan for live ordinary linked children of family
members now reject the archive with session_busy, naming the delegated
member or the parent of the live child. Removal keeps its existing
relationship-aware plan, and unarchive and no-op archives are
unaffected. The removal preview fixture and the revision UDS retention
flow now retire the live subtask before archiving its parent family.

Fixes apache#5956

Generated-by: GLM-5.3-Flash (ZCode)

* fix(runtime-host): treat retired WorkHub delegations and finished subtasks as archivable

The Astro-Han review of PR apache#5957 narrowed this guard: "live" was defined
as the Coordination ledger still holding the delegation row, but that row
stays until a delegation is superseded or stopped — finishing its Turn
retires the delegation without touching the ledger, so a Session that
ever received a delegation could never be archived again.

The archive guard now reuses the WorkHub delegation-retirement read (the
same one coordination actions run): pending, an active owned root, or a
recovering execution still blocks; cancelled, shared, and terminal
executions allow the archive. A returnResults delegation whose result has
not yet been delivered into the Coordination Session — no root admission
receipt — still blocks, because archiving would hide the result's only
visible target. A linked child now blocks only while it carries in-flight
work (an active or reserved root Turn, queued or in-flight messages, a
pending interaction); a finished subtask no longer pins its parent out of
the archive. Removal keeps its existing relationship-aware plan, and the
shared retirement read is hoisted so both consumers agree on one
definition.

Regressions cover a completed delegation and a closed subtask archiving
successfully; reverting the guard to the ledger/any-child form turns both
red again.

Fixes apache#5956

Generated-by: GLM-5.3-Flash (ZCode)

* fix(desktop): surface archive refusals and retry bulk archive once after the sweep

The archive toast swallowed the Host's refusal behind a generic "Could
not archive task": the session_busy reason crossed Electron IPC as a bare
message with its error class stripped. The sessions:archive handler now
re-keys the archive-guard refusals a user can act on — a live WorkHub
delegation, an undelivered WorkHub result, a live linked child — into
stable session_archive_refused tokens, and the rail maps each to
localized copy naming the action that unblocks the archive (stop or
replace the delegation / retry in a moment / archive the subtasks first)
for zh-CN, zh-TW and en.

The bulk sweep archived in selection order, so a parent picked before its
own subtask failed on the first pass and stayed failed. One retry after
the sweep settles ordering inside the selection without teaching the rail
about subtask links; a refusal that was never about ordering fails once
more and is reported exactly as before.

Focused tests cover the refusal mapping and the retry; the renderer
architecture ledger passes --strict-base against 7c90bac.

Generated-by: GLM-5.3-Flash (ZCode)

* fix(runtime-host): check every active WorkHub assignment, not just the newest

Second-round review of apache#5957 found that #assertNoLiveRelationships read
only the first row of each readActiveWorkHubAssignmentsByTarget page.
The store returns every active assignment for the page's targets, newest
first, so a target carrying two delegations was judged by its newest row
alone: an older delegation still in flight, or retired but still owing
WorkHub its result, was skipped and the archive was let through. Iterate
every assignment in the page and apply the same retirement and
undelivered-result checks to each; any hit refuses the archive.

Regression tests from the reviewer's scratch scenarios: with two
assignments on the same target, an older not-retired delegation next to
a retired newest one now refuses with the active-delegation error, an
older retired delegation with an undelivered result refuses with the
undelivered-result error, and all-retired with nothing owed still
archives. Red against the old first-row read, green after the loop, red
again when reverted.

Also rewords two archive-refusal lines the same review flagged, in en,
zh-CN and zh-TW: the undelivered-result line no longer suggests retrying
in a moment (delivery may be indefinitely pending, so it now points at
opening WorkHub or stopping the delegation), and the linked-child line
no longer tells the user to archive subtasks the Host itself refuses to
archive (it now says to wait for them to finish or stop them).

Generated-by: GLM-5.3-Flash (ZCode)
* feat(harness): add local preview capability preflight

Showing generated HTML locally depends on four independent things, and
each fails differently: the client may have no GUI surface, the embedded
browser refuses file:// by policy, the process that writes the file may
not share a filesystem with the surface that renders it, and the
conversation's own view may not be on screen. Attempting the preview and
reading the first error conflates all four. A refused loopback connection
is the worst of them — "nothing listening yet", "still starting", and
"separate namespace" are indistinguishable at the socket, yet the refusal
reads like proof of the last one.

preview_preflight answers each capability with verified, unsupported, or
unknown, and names the observation behind it. verified needs a positive
observation; unsupported is reserved for settled boundaries statable
without probing; everything else is unknown and explicitly disclaims a
sandbox or isolation cause. A connection outcome can therefore only ever
produce verified or unknown, never unsupported.

The honesty runs both ways: an endpoint that answers proves only that
something listens for this process, not that the embedded browser shares
that loopback namespace, and a staging root that round-trips proves only
that the main process reaches it. `surface` reports whether this client
can display a page at all and stays separate from `endpoint`, which is
not_checked when no origin was given; `ready` needs both, and is never
inferred from an absent error.

The loopback probe settles on every terminal path and owns an absolute
deadline. A server answering 101 Switching Protocols makes Node route the
exchange to `upgrade` and close the request without emitting `response`
or `error`, taking the socket-bound timeout with it; the probe previously
outlived both its stated bound and its caller's cancellation.

Published as its own desktop_preview offer rather than in the Browser
offer: every Browser tool is admitted against an Origin resolved from the
view's current URL, so a capability check published there would need a
page already loaded and would fail with the very "requires an HTTP
origin" error it exists to explain.

Generated-by: Claude Opus 5 via Claude Code
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(harness): narrow preview preflight to what it can prove

Drop filesystem_visibility. HTML Open always goes through
ManagedArtifactPreview, which never touches the staging root, so the
capability measured the non-HTML Show in Folder path rather than a
preview question. It also let a transient target loss during a reconnect
surface as `unsupported`, the status reserved for settled boundaries.
The artifactPresentationRoot export, its boot closure, the wiring test
and the mkdir side effect inside a `read` tool go with it.

Drop `ready`. A listener's answer — a 404 included — proves a listener,
not that the page exists or loads, and a readiness flag built from it
claimed more than the evidence held. `surface` and `endpoint` stand on
their own; page load remains apache#5235's question.

Probe with HEAD / against the origin instead of GET on the caller's
path, so a capability check cannot trip a dev-server route with side
effects. The absolute deadline is now the single catch-all, replacing a
`close` arm no real server reaches.

Publish the preflight inside desktop_artifact_preview, so the
ArtifactPreview route it hands off to is always granted with it, and
defer to that tool's description instead of paraphrasing a copy that had
already drifted. Report a failed probe as "no usable HTTP response"
rather than "nothing answered", since a TLS failure or a reset after
accept means something did listen.

Rename browser_reachability to browser_view and split the host-less case
out as a constant, removing a branch the tool could never reach.

Generated-by: Claude Opus 5.5 via Claude Code
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(harness): publish the preview offer through one tested seam

Build the desktop_artifact_preview tool set in
buildArtifactPreviewOfferTools, so the preflight and the ArtifactPreview
route it hands off to are assembled in one place a test can see. A test
pins both tools in the offer and that the handoff names only a tool the
same offer grants; dropping either tool from the array now fails it.

Remove categoryHint and recoveryMode from preview_preflight. The tool is
published as a client capability, whose descriptor carries neither, and
the Runtime Host assigns both itself, so the declared `read` and
`replay_safe` were never in effect and only misled a reader.

Say why the headless branch stays although Desktop, the only publisher,
never reaches it: GUI availability is part of the classification apache#5235
builds on, and it must answer for a publisher without a view host
rather than throw.

Generated-by: Claude Opus 5.5 via Claude Code
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…pache#6028)

* fix(runtime): keep tool_search activations for the Session lifetime

tool_search activations lived on the per-send Turn, so the next Turn
started with an empty provider tool list and missed the prompt cache.
Share the activation map on AiSdkBackend. tool_search only adds names;
Turn end and compaction do not clear them. A rebuilt backend starts
empty; activations are not persisted.

Refs apache#5879

Generated-by: Cursor

* fix(runtime): document shared tool_search activations across concurrent Runs

A Session backend can hold several concurrent Runs. They share the
activation map, so a search in one Run is visible on a sibling Run's next
step. Mention that in the field comment and rename the prepare() isolation
test so it no longer describes backend Turns.

Refs apache#5879

Generated-by: Cursor

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…ions (apache#6029)

* fix(runtime): keep request_sandbox_boundary visible in sandboxed Sessions

Host binds the widening tool only when the Session is not Full access, but
tool_search still deferred it. Models then searched for it, activated
neighbors, and missed the prompt cache. Pass it through requiredToolNames
whenever it is bound so the schema stays on the provider list for the
whole Session, without depending on a live boundary prompt.

Refs apache#5879

Generated-by: Cursor

* fix(runtime): omit required tools from the tool_search inventory

Required names are already on the provider list. Leave them out of the
search inventory and ranking so an exact-name search does not activate a
tool that is already visible.

Align the deferred-tools backend helper with apache#6028 so the two PRs rebase
without a test-file conflict.

Refs apache#5879

Generated-by: Cursor

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
fix(runtime-host): use canonical ASF headers
fix(runtime-host): match ASF header policy
refactor(retention): share host maintenance guards
fix(retention): keep canonical ASF source header
style(retention): format maintenance guard
docs(runtime): explain fresh process clock floor
…apache#6041)

Native Anthropic requests use request-level automatic caching, which
places the breakpoint on the last cacheable block. Since apache#5740 that
block is the request-only Turn context appended after history, which no
later request repeats at that position. Each request therefore paid a
cache write that the next step and the next Turn could not read.

When the resolved provider options enable Anthropic automatic caching,
the model adapter now marks the last replayed history message with the
same cache control, so the entry lands on a prefix the next request
repeats. The Turn context stays request-only, connections without
automatic caching get no marker, and durable message identities are
unchanged because the marker is applied to the dispatch copy.

Refs apache#6040

Generated-by: Devin

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
refactor(runtime): drop redundant guards from the Anthropic history breakpoint

Ablation showed the non-positive count guard and the intermediate locals
change no behavior: a count of zero matches no message index. The test
now checks that the block marker uses the request-level cache control
instead of repeating the request-level assertion that provider
conformance already covers.

Refs apache#6040

Generated-by: Devin

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
test(runtime): tighten the Anthropic cache breakpoint assertions

Generated-by: Devin
Refs apache#6040
…he#6046)

Second pass over docs/architecture/bot-onboarding-runtime.zh-CN.md,
read against the current implementation. All 18 checked claims hold:
the BOT_ONBOARDING_PROVIDERS enum (dingtalk/feishu/wecom/wechat/qq,
Lark as a feishu brand), the IPC channel names, BotOnboardingService,
SettingsStore.updateIf, applySettingsRuntimeEffects,
BotRegistry.applySettings, the Feishu and WeCom bridge handshakes and
cleanup order, the 15s/10s/1-30s/5s timing figures, the
saved_not_connected warning and retryHealth snapshot fields, the
masked token/appSecret settings projection, the qrcode dependency, the
settings-bots-onboarding e2e fixture, and the apache#4327 tracking link.

No drift; the pass only refreshes last_verified and the in-body status
date to 2026-10-10.

Refs apache#3522
Generated-by: GLM-5.3-Flash (ZCode)
… sites (apache#6003)

Two call sites build their own proxy dispatcher and dispose it themselves,
so the abort-before-close teardown pattern that the shared transport fix
reordered (apache#5900) survives in them:

- testProxyConnection's finally aborts the controller and then awaits the
  dispatcher's graceful close. Behind a real proxy that close never
  settles, so the call stays pending long past its 8s probe timeout and
  wedges the serialized settings lane it occupies on the Desktop.
  Start the dispatcher teardown before the abort and race it against a
  1s grace, mirroring the transport pattern.
- proxiedFetch's error path awaits the same graceful close after
  aborting, so a stalled close delays the rejection the caller is owed.
  Apply the same ordering and bound there. The success path keeps its
  fire-and-forget close, which owns the streaming body lifecycle and is
  intentionally not bounded.

Regression tests stall ProxyAgent.prototype.close (the same
Undici/Node-version-independent technique the transport tests use) and
assert both calls still settle; both tests fail on the previous ordering.

Refs apache#5978

Generated-by: GLM-5.3-Flash (ZCode)
fix(runtime): escalate proxied dispatcher teardown to destroy after the grace expires

Root cause: the bounded dispatcher teardown in testProxyConnection and the
proxiedFetch error path raced the graceful close against a 1s grace, but
when the grace won, the dispatcher was simply abandoned with its close()
still pending, leaking the agent (flagged as a P3 inline on apache#6003).

Fix: track who wins the grace race and, when it expires, fire-and-forget
destroy() with a distinguishing reason. The destroy error is swallowed so
teardown can never replace the result or rejection the caller is already
owed. The grace duration and its external semantics are unchanged; the
success path of proxiedFetch keeps its unbounded close, which owns the
streaming body lifecycle.

Verified: new regressions in proxy-test.test.ts and proxied-fetch.test.ts
stub ProxyAgent#close to never settle and assert destroy is called; both
failed before the fix (callCount 0) and pass after. runtime network+bots
suites 167/167, biome check clean, check:asf-headers clean.

Generated-by: GLM-5.3-Flash (ZCode)
…d fixture (apache#5804)

Why: apache#4677 item 4 asks for a Usage settings tab-entry median/p95 number plus
renderer-side evidence that the activity table mounts a bounded page instead
of every row.

Fixture (test infra only):
- scenarios-usage.ts pads the 5 handcrafted turns with 394 zero-tool turns
  (132/131/131 across the three sessions) so the ledger holds exactly 409
  activity rows: 399 model rows + 10 tool rows. Synthetic turns reuse the
  handcrafted generators on the fixed clock (60..453 minutes ago).
- e2e-fixture.ts: one catchUpModelCallProjection() pass bounds how many
  lagging runs it folds, so a padded seed with hundreds of queued runs left
  most of them unfolded after seeding. Fold in a loop until pendingRuns
  reaches 0, which is generic for any large canonical seed.

Spec (perf/usage-tab-entry.spec.ts): a custom usageWindow fixture injects
the settings-usage scenario; the test asserts the bounded mount (DOM rows
<= 50 with the tab badge at 409) and then samples 12 warm re-entry rounds
entirely inside the renderer with rAF — the clock runs from the synthetic
click to the activity table's next painted frame, excluding Playwright
round-trips. Results land in $TEMP/usage-tab-entry-results.json.

Numbers (this branch, apache/main base): median 103.9 ms, p95 142.6 ms
(n=12, p95 = max), first-entry DOM rows 50/409. Reference numbers from
apache#4677 (comment issuecomment-5880047554, measured on a feature branch):
median 134.1 ms, p95 201.3 ms — same magnitude; the branch difference is
noted rather than compared.

Generated-by: GLM-5.3-Flash (ZCode)
fix(desktop): carry the poll budget into the renderer and fix the even-sample median

Two review fixes on the usage-tab-entry perf spec:

- Playwright serializes page.evaluate callbacks without their Node-side
  closure, so the MOUNT_POLL_FRAMES guard referenced an undefined constant
  inside the renderer: the first poll reaching it threw a ReferenceError
  from a requestAnimationFrame callback, leaving the surrounding Promise
  pending forever instead of rejecting after 600 frames. Both the unmount
  and the entry poll now receive the budget as an explicit evaluate
  argument, so the slow path rejects as designed.
- The 12-round summary picked sorted[n/2], the upper middle value, as the
  median. For an even-sized sample the median is the mean of the two
  middle values; the summary now averages sorted[n/2 - 1] and sorted[n/2]
  for even runs (odd runs keep the single middle value).

The PR body's measured numbers were refreshed from a new run of the
corrected spec.

Refs apache#4677
Generated-by: GLM-5.3-Flash (ZCode)
ci: retrigger the cancelled test run
Merge remote-tracking branch 'upstream/main' into perf/usage-tab-entry-spec
)

The transcript data-plane benchmark called
store.readTranscriptPageSnapshot, which apache#4879 removed when Session
transcripts moved to the RuntimeEvent ledger; the storage contract
kept only readTranscriptHighWaterSnapshot, so the script has crashed
on main ever since.

Retire rather than repair: the script seeded its fixture through
store.appendMessages, and rows written that way never reach the
RuntimeEvent ledger the replacement reader pages from, so pointing the
same harness at the new reader would page an empty Session. A repair
means rebuilding the fixture through the composition's turn pipeline -
which is exactly what apache#5800's benchmark:hydration-e2e already measures
end to end (subscription.open tail bootstrap, session.transcript.page
paging through real handlers, production ClientSessionSubscription
decode). Drop the script and its dead benchmark:transcript entry
rather than duplicate that harness.

Refs apache#4677

Generated-by: GLM-5.3-Flash (ZCode)
chore(ci): retrigger the release lanes after the undici advisory

The tarball and audit lanes failed on npm resolving undici@7.29.0 for
the CLI release tree - GHSA-3wwx-pv8p-q78v was published against that
version, and undici 7.29.1 (the fixed release) is out. Tree-identical
retrigger so the installer resolves the patched version.

Generated-by: GLM-5.3-Flash (ZCode)
fix(deps): move undici past GHSA-3wwx-pv8p-q78v

The release/audit lanes fail because the lockfile pins undici@7.29.0
(three nested copies) and undici@6.28.0, both in the advisory's range;
the release tree installs from the lockfile, so no retrigger helps.
Overrides pin each consumer to the fixed release of its own major
(7.30.0 / 6.29.0 / 8.11.2), and packages/runtime is pinned to exactly
7.30.0 because @slack/socket-mode's peer demands ^7 while runtime code
needs a working WebSocket/fetch/buildConnector (gateway and
plugin-client-bridge tests pass on 7.30.0).

Generated-by: GLM-5.3-Flash (ZCode)
fix(deps): restore lockfile entries dropped by the undici re-resolution

The undici overrides update left the lockfile with six dangling
dependency edges: gpt-tokenizer, @stylexjs/stylex (plus its styleq /
css-mediaquery / loose-envify peers) and invariant were still declared
by the astryx packages but their node_modules resolution entries had
been dropped, so `npm ci` refused to install at all. A full
`npm install` re-resolved them; `npm ci` now passes locally and the
undici pins are unchanged.

Generated-by: GLM-5.3-Flash (ZCode)
fix(deps): declare the squirrel-windows peer the desktop build needs

app-builder-lib declares electron-builder-squirrel-windows in its
devDependencies and peerDependencies (exact 26.16.1), and npm's tree
validation refuses the desktop workspace while that peer has no
resolution entry - every npm lane (audit, test, package, tarball) fails
with ELSPROBLEMS. The entry was silently dropped by the same re-
resolution that trimmed the undici lockfile, and being a peer it never
regenerates on its own. Declaring it explicitly in the desktop
devDependencies satisfies the peer durably; npm ci passes with the
workspace tree clean.

Generated-by: GLM-5.3-Flash (ZCode)
fix(deps): pin ip-address past its SSRF advisories and re-key the stylex notice

The shipped-closure audit now flags ip-address@10.4.0 (GHSA-rpw4-54j3-
4h4q / GHSA-2vr4-cq9g-pvrc, SSRF); an override pins 10.7.2, which both
declaring ranges accept. The third-party-notices generator's MIT
copyright override for @stylexjs/stylex tracked 0.19.0 while the tree
resolves 0.19.1 (same upstream, same copyright line) - the key follows.

Generated-by: GLM-5.3-Flash (ZCode)
chore(deps): regenerate the third-party notices for the bumped closure

The ip-address and undici pins moved the shipped closure; the
committed notices files now match it (stylex 0.19.1, ip-address
10.7.2, undici 6.29.0 / 7.30.0 / 8.11.2).

Generated-by: GLM-5.3-Flash (ZCode)
fix(deps,ci): re-key the release closure and clean the lockfile registry

The undici re-key left two references behind: the release packager
hardcoded undici@8.10.2 when copying the eval closure and writing the
CLI manifest (the tree now resolves 8.11.2), and the regenerated
lockfile carried registry.npmmirror.com resolved URLs from a local
mirror config where main's entries all use registry.npmjs.org. knip
also gained an exemption for electron-builder-squirrel-windows: it
satisfies app-builder-lib's peer and is never imported by renderer
code, same as the existing fontsource entries.

Generated-by: GLM-5.3-Flash (ZCode)
style(knip): wrap the ignoreDependencies array for the formatter

Generated-by: GLM-5.3-Flash (ZCode)
fix(deps): walk the undici bumps back to behavior-preserving versions

The aggressive undici re-key broke two real consumers: the eval suite's
initialization proxy test saw an unexpected CONNECT through the 7.30.0
ProxyAgent, and the runtime scoped-fetch-transport suite hung on
7.30.0. Walk @ai-sdk/provider-utils back to 7.29.1 (the minimal fixed
release) and packages/runtime back to its previous 8.10.2 (already past
the advisory line); @electron/get keeps 7.30.0 and the 6.x/8.x leaves
keep their resolved versions. Both previously failing suites pass
locally (42/42 and 1/1).

Generated-by: GLM-5.3-Flash (ZCode)
fix(deps): drop the @slack undici override in favor of the nested entry

The `@slack/socket-mode` override demanded undici@7.30.0 while the
runtime tree deduped to 8.10.2, and npm flagged the mismatch as
invalid - which every npm ls consumer (audit, release closure, notices)
turned into ELSPROBLEMS. The lockfile's nested
`@slack/socket-mode/node_modules/undici@7.30.0` entry already satisfies
the ^7 peer with the fixed release, so the override is redundant;
dropping it leaves a fully valid tree.

Generated-by: GLM-5.3-Flash (ZCode)
chore(deps): regenerate the third-party notices for the walked-back closure

The undici version walk-back changed the shipped closure again
(+7.29.1, +8.10.2 after the @slack nested entry re-landed); the
committed notices files now match it, per the lockfile-chain checklist.

Generated-by: GLM-5.3-Flash (ZCode)
Merge branch 'main' of https://github.com/apache/maka into fix/transcript-benchmark
Merge remote-tracking branch 'origin/fix/transcript-benchmark' into fix/transcript-benchmark
Merge upstream/main (8648258)
Merge remote-tracking branch 'upstream/main' into fix/transcript-benchmark
faga295 and others added 25 commits October 10, 2026 20:49
Generated-by: OpenAI Codex
refactor(storage): simplify git-dir validation and share broken-ancestor fixture
fix(storage): reject HEAD symrefs whose target is not under refs/

An ancestor with valid .git/objects and .git/refs but HEAD set to
`ref: gk` passed isGitDirectory(), while git rev-parse exits 128 on it.
resolveProjectLocation() then threw and resolveWorkspaceIdentity()
returned workspace_io_failed, so the malformed ancestor still blocked
the nested-folder workflows this change is meant to recover.

Git itself only accepts HEAD symrefs pointing under refs/ (verified:
ref: gk, ref: HEAD, ref: gk/x all exit 128 even with objects/ and
refs/ present; ref: refs/... passes even when the branch does not
exist yet), so the check matches exactly that and no more.

The case is added to the shared broken-metadata fixture as
head-symref-no-refs-prefix, covering both the project-catalog and
workspace-identity broken-ancestor tests.
feat: resolve git dir with  git rev-parse --resolve-git-dir
fix: git file no read access
fix(storage): re-probe rejected .git ancestors under a readable stable snapshot

Git's exit-128 rejection of an ancestor .git directory conflates a
format verdict with metadata it could not actually read. Trust the
verdict only after auditing the inputs is_git_directory consults
(HEAD readable, objects/refs searchable) inside a stat snapshot,
then re-probe and require the snapshot to be unchanged so repaired
or replaced metadata cannot inherit a stale rejection.

Entries that cannot be classified — commondir or
GIT_OBJECT_DIRECTORY redirection, special files, gitfile/symlink
indirection, and any failure on the selected directory's own .git
— now surface as errors instead of silently downgrading to
folder identity.

Generated-by: OpenAI Codex
docs(storage): record new git marker and workspace identity Windows exclusions

Generated-by: OpenAI Codex
fix(storage): anchor git entry probes on the entry directory

assertGitEntry ran git -C process.cwd() merely to give Git a place to
start; --resolve-git-dir judges gitPath itself. When the host's cwd has
been deleted or is unsearchable, -C fails before the probe runs and the
generic exit 128 is taken as Git's verdict on the entry. The audit
re-probes under the same broken anchor, a valid enclosing repository is
skipped, and resolveProjectLocation silently downgrades it to folder:.

Anchor -C on parse(gitPath).dir instead — lstat already proved that
directory exists — and resolve() the walk start so a relative path can
never hand the ambient-cwd dependency back through a relative -C.

Generated-by: OpenAI Codex (swe-2)
… directory (apache#4936)

Offer to restore archived projects from task entry, project settings, and remote directory registration. Preserve project naming, show loading state, and report add, restore, and refresh failures.

Remove the unused app-shell add action and route runtime-host catalog failures through redacted diagnostics. Add regression coverage for archived-project restoration and error handling.

Generated-by: Codex
fix(desktop): preserve task-entry project restore results

Reuse the result-bearing newTasks restore bridge and remove conflicting service and fake definitions. Cover restored-project selection through the Desktop adapter.

Generated-by: OpenAI Codex
fix(desktop): share archived-project restore across registration callers

Extract the register/confirm/restore transaction into
resolveProjectRegistration and a render-prop ProjectRegistrationBoundary
owning single-flight pending state and staleness fences. Settings and
the remote directory picker route through it, so the confirm dialog no
longer awaits inside Astryx clickAction transitions. Session workspace
recovery moves to useSessionWorkspaceRecovery with request-identity and
attempt fencing, and toast confirm gains an onConfirm hook that keeps
the dialog open while the accepted operation runs.

Generated-by: Codex
fix(desktop): preserve archived project names during remote registration

Extract shared project registration recovery into a hook and carry the restored result through the remote directory handoff. Align workspace recovery copy with task terminology and expose archived add results in the preload bridge.

Generated-by: Codex
Review follow-ups (apache#5274 review):

- prepareRevisionSend re-keyed the edit-start snapshot, so quotes the user
  removed or re-annotated during the edit reappeared on the branch and
  could be sent. The re-key now lands the plate's current quotes, and the
  send reads them live (the before-send snapshot retires — the re-key
  covers its case).
- Drops the stale-base footer copy block and a dead regenerate key: no
  production code on current main references the old footer shape that
  survived in this branch's base.

The no-op gate keeps counting the model-facing comment.

Generated-by: GLM-5.3-Flash (ZCode)
Merge upstream/main into fix/desktop-revision-structured-context
fix(desktop,ui): preserve the quote plate across the in-flight revision send

The revision send awaited prepareRevisionSend before reading the plate,
and the lifecycle re-keys it mid-send: the plate's current quotes are
copied onto the branch child and the source bucket is emptied in place.
The still-running sendWithAttachments invocation holds quotesForSend
from the render that started the send, so it read the emptied source
bucket, got undefined, and submitted the edited replacement without its
quotes or their edited annotations (apache#5274 review).

Read the plate through the re-keyed owner explicitly: quotesForSend now
resolves the bucket by owner key (defaulting to the live draft key for
the non-revision send paths), and the revision send passes the prepared
draft's session id. The resumed send therefore reads the child bucket
the lifecycle just wrote — removals and re-annotations included.

The regression test drives beginEditUserMessage → an in-render
quotesForSend capture → prepareRevisionSend and asserts the re-keyed
plate with its edited annotation still reaches the send; it fails on
the previous head with `actual: undefined`.

Generated-by: GLM-5.3-Flash (ZCode)
fix(desktop,ui): keep quotes added during a cancelled edit

Cancelling a revision draft cleared both draft keys wholesale, so quotes
the user staged during the edit were lost together with the edit's own
restaged set (apache#5274 review: "Cancelling an edit deletes quotes added
during it" — on main they stayed in the composer, and adding quotes
during an edit is supported).

clearQuotes now returns the entries it removed, and
clearRevisionStagedContext re-stages whatever lies beyond the edit's
beginEdit snapshot (matched per QuoteRef field set, counted as a
multiset) onto the source Session the cancel returns to — whether the
user added them before or after the branch child was prepared. The
edit's own items are still dropped wherever the commit left them.

The regression tests drive a cancelled edit with a user-added quote on
each side of the prepare step and assert the addition survives on the
source key while the composer text rolls back; both fail on the
previous head, the first with the edit's restaged quote returned
instead of the user's own, the second with no plate restore at all.

Generated-by: GLM-5.3-Flash (ZCode)
fix(ui): treat negative owned counts as fully user-owned

clearRevisionStagedContext drops the edit's own quotes by per-key count:
the first N entries matching the beginEdit snapshot are the edit's, and
everything past that count is the user's own staging. The kept check
compared the remaining count with `=== 0`, so once the counter ran
negative the surplus stopped matching: when the user added a second copy
of a quote the edit had also staged, the third identical plate entry was
dropped despite being the user's.

Treat any non-positive count as fully user-owned (`<= 0`), so every
entry past the edit's own count survives the cancel.

Fixes a P3 raised inline on apache#5274.

Generated-by: GLM-5.3-Flash (ZCode)
Merge upstream/main (8648258)

Brings in the composer-submit extraction (apache#5815) and the allow-unchanged
edit-and-resend decision, merged into the revision staged-context feature:

- app-shell.tsx keeps the upstream extraction; the staged-context wiring and
  the apache#5274 re-keyed quotesForSend/clearQuotes semantics move into
  features/conversation/controller/composer-submit.ts (owner-keyed ports).
- app-shell-revision-actions.ts keeps the @maka/ui delegation;
  TurnRevisionDraftBase drops originalText and the unchanged gate
  (revisionStagedContextUnchanged) so unchanged resends pass through per
  upstream apache#5815, while the mixed-context conflict gate stays.
- conversation-copy.ts keeps both new locale keys
  (revisionDraftQuoteConflict, revisionMixedContextUnsupported) on top of
  upstream's revisionUnchanged removal and apache#5815 copy updates.
- renderer-architecture.json regenerated via check-renderer-architecture
  --write; app-shell.tsx budget drops 12091 -> 11154.
Tests: upstream app-shell-revision-resend gains a stagedContext fake; the ui
revision suite expectations updated to the allow-unchanged behavior.

Generated-by: GLM-5.3-Flash (ZCode)
fix(desktop): assemble the revision staged context outside the frozen shell

Root cause: upstream apache#5546 extracted app-shell helpers and drove the frozen
shell's nonTriviaTokens baseline down to 11129 with zero headroom, so the
merge that re-attached this PR's stagedContext wiring (a 7-line factory in
the createAppShellRevisionActions ports object, +25 tokens) broke the
renderer architecture ratchet: 11129 -> 11154.

Fix: assemble the RevisionStagedContext closure inside the desktop
useComposerAttachments controller (a feature file outside the debt
ledger), right beside the hooks that own the quote/attachment buckets.
The frozen shell now forwards one member (stagedContext,) instead of
building the factory inline. The restoreAttachments entry the shell only
carried for that factory is dropped from its destructure: its sole
consumer was the deleted block (RevisionStagedContext declares no such
member and nothing in @maka/ui reads it from the staged context).

Verification:
- check:renderer-architecture --base 6e21e61 --strict-base: red
  (11129 -> 11154) before, green (11129 == base) after; ledger diff is
  the single app-shell.tsx nonTriviaTokens line, no other entry moved.
- desktop focused suites (app-shell-revision-actions,
  use-composer-quotes-revision-send, app-shell-revision-resend,
  new-task-staged-content): 18/18 pass.
- packages/ui full suite: 710/710 pass.
- typecheck green: ui tsconfig + desktop preload/main/renderer/storybook.
- biome lint clean on both touched source files; check:asf-headers green.

Generated-by: GLM-5.3-Flash (ZCode)
Merge upstream/main (28cc4e6)

Resolve the renderer-architecture.json conflict by re-pricing the ledger
from the merged tree via check-renderer-architecture.mjs --write (all
entries at real merged-tree values, within baseline); drop the trailing
blank line at EOF in conversation-copy.ts flagged by diff-check.
Merge upstream/main (0aa2707)

Resolve renderer-architecture.json by re-pricing from the merged tree via check-renderer-architecture.mjs --write.
Merge upstream/main (d6876d7)

Resolve the renderer-architecture.json conflict by taking main's ledger
(new apache#5851 file entries) as the base and re-pricing every entry from the
merged tree via check-renderer-architecture.mjs --write; the check passes
against the ratchet baseline.
Merge upstream/main (d7dffca)
Merge upstream/main (c838e1f)

Fourth structural catch-up for apache#5274: carries apache#5868 (Composer staging
moved into a persistent ComposerStagingProvider owner) and apache#5884 (task
archive timestamps).

Conflict resolution ports the revision staged-context semantics onto the
new owner structure: ComposerStagingSubmission gains optional owner-key
reads/clears (snapshot for captured submissions, live plate for the
revision re-key) and ComposerStagingCommands gains stagedContext(); the
frozen shell forwards the one composerStaging handle and the revision
assembler derives the gate probe and plate reads from it.
Merge upstream/main (1e80e3b) into fix/desktop-revision-structured-context

# Conflicts:
#	apps/desktop/renderer-architecture.json
Merge upstream/main (c7fa6bb) into fix/desktop-revision-structured-context

# Conflicts:
#	apps/desktop/renderer-architecture.json
Merge upstream/main (255ae23) into fix/desktop-revision-structured-context
Merge upstream/main (229e1b4) into fix/desktop-revision-structured-context
Merge upstream/main (f7633c3) into fix/desktop-revision-structured-context
Merge upstream/main (7c90bac) into fix/desktop-revision-structured-context
Merge remote-tracking branch 'upstream/main' into fix/desktop-revision-structured-context

# Conflicts:
#	apps/desktop/renderer-architecture.json
fix(desktop): drop the stale regenerate footer copy

apache#5468 removed the Regenerate feature (87ff279), deleting the footer's
`regenerate` label and the `regenerateRunning` / `regenerateAgain` /
`regenerate` / `requestRegenerate` keys from the conversation copy. This
branch's stale base still carried the block, so the merge-base diff
re-adds those keys across the contract type and all three locales
(including the zh-TW 本輪迴答 wording) while nothing on current main
references them — merging would resurrect dead strings.

Drop the block from the type and the zh-CN / zh-TW / en entries, matching
the upstream shape exactly; the `lineage.regenerated*` keys are a
different, live feature and stay.

Generated-by: GLM-5.3-Flash (ZCode)
Generated-by: Codex
fix(cli): resume the selected interrupted session

Generated-by: Codex
fix(cli): separate session attach and resume discovery

Generated-by: Codex
style(cli): format resume availability flow
fix(cli): reject unavailable sessions before switching
fix(cli): keep unavailable sessions attachable
Merge branch 'main' into fix/cli-resume-discovery
Merge branch 'main' into fix/cli-resume-discovery
Merge branch 'main' into fix/cli-resume-discovery
fix(cli): keep session attachment independent from resume
Merge origin/main into fix/cli-resume-discovery
fix(cli): preserve no-cwd session guard

Generated-by: Codex
fix(cli): exclude foreign sessions from resume picker
Merge branch 'main' into fix/cli-resume-discovery
fix(cli): guard resumable session selection
merge: sync resumable session PR with main
:bug: bound resume discovery to visible sessions
Merge upstream/main into fix/cli-resume-discovery to resolve PR apache#3582 conflicts
fix(cli): bound resume candidate discovery

Generated-by: Codex
Merge upstream/main and fix bounded resume discovery

Generated-by: Codex
fix(cli): bound resume catalog reads

Generated-by: Codex
fix(cli): preserve cwd resume candidates
fix(cli): filter bounded session catalog results

Generated-by: Codex
fix(cli): surface incomplete resume discovery

Generated-by: Codex
fix(cli): guard external imports during active turns

Generated-by: Codex
fix(cli): preserve partial session discovery results

Generated-by: Codex
fix(cli): preserve resume candidates at scan limits
fix(cli): scope incomplete catalog notice to picker

Generated-by: Codex
fix(cli): keep resume catalog bounds in sync
fix(cli): retry session list after catalog failure

Generated-by: Codex
Retry remaining UTF-8 bytes at explicit offsets and report stalled writes as unknown outcomes.

Fixes apache#5370

Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
refactor(runtime): clarify complete descriptor write contract

Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
docs: explain regression invariants in English

Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
fix(core): preserve indented memory metadata examples

Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
docs: explain regression invariants in English

Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
test(core): document calendar DST policy across consecutive fires

Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
docs: explain regression invariants in English

Generated-by: OpenAI Codex
Signed-off-by: seekskyworld <djh1813553759@gmail.com>
executeTool() spelled out the argument-projection policy inline: raw
snapshot, permission projection on a private clone, the Computer Use
persisted/model views, and two handwritten common-field recipes for the
tool_start event and the persisted tool_call message — all inside a
method that also owns admission, dispatch identity, T1/T2 and
publication (apache#4908, slice of the A06 direction in apache#4726).

The call-data rules move to tool-call-snapshot.ts: the recursive
snapshot helpers, schema validation, one named buildToolCallArgs()
operation that produces the four argument views with the existing guards
(direct-only rejection skips validation and projection entirely; an
unavailable sandbox-boundary surface defers validation; projection
failures come back as permissionArgsError for the refusal path instead
of throwing), and one buildToolCallCommonFields() recipe invoked once per
record so the event and the message each receive privately owned clones
of args and providerOptions. The consolidated recipes were verified to
fail the argument-ownership isolation test when a single shared args
object leaked across the two records — the exact break the duplication
used to make impossible.

No public API, event, message or durable format changes. The four
argument-view consumers (managed mutation admission, durable
preparation, result projection, telemetry/artifacts) read the same named
views; their algorithms are untouched.
fix(runtime): keep the shared call-field recipe inside the persisted message shape

The shared common-field recipe emitted toolUseId as its own key. The
tool_start event carries that field, but the persisted tool_call message
names the same value `id` and its stored-message schema rejects unknown
keys — appendMessages threw 'Invalid stored message schema', the
Computer Use history guard generalized it to 'Operation failed', and the
remote TUI turn failed before its fixture tool could run
(tui-mcp-remote-integration).

The recipe no longer emits the call id: the input still carries it, and
each record names it at its own site — `toolUseId` on the event, `id` on
the message. Reproduced locally through the failing integration test
(failed on the refactored build, passed on the pre-refactor module);
passes again after the fix along with the four argument-ownership,
settlement, model-loop and privacy suites (21/21).
refactor(runtime): scope call-data extraction to argument projections

Keep call-event construction in executeTool, its sole production owner.
Remove the unused common-field wrapper, mirrored identity types, and
modelFacingArgs alias. Persistence and model replay retain persistedArgs
as their canonical projection.

Generated-by: ZCode
…ache#5744)

npm's install-scripts approval gate flags seven packages on a fresh
clone: two allowlist entries trail the lockfile (esbuild 0.27.7 vs the
pinned 0.28.2, @jackwener/opencli 1.8.4 vs 1.8.7), and five packages
with install scripts were never allowlisted at all (electron-winstaller,
protobufjs, tree-sitter-javascript, @astryxdesign/cli,
@astryxdesign/core). Every entry is now the exact version pinned in
package-lock.json, verified against node_modules, and a fresh install
completes with no unreviewed-script warnings.

Generated-by: GLM-5.3-Flash (ZCode)
chore: deny the four nonessential install scripts

Review follow-up: the @astryxdesign/cli and @astryxdesign/core
postinstalls only print setup nudges, protobufjs only checks its version
notation, and @jackwener/opencli skips its shell-completion postinstall
on this repository's installs and in CI — none produces an artifact
Maka needs. Record an explicit deny (which also clears npm's
unreviewed-script warning without executing the scripts) and keep
approval only for the scripts installation genuinely requires
(esbuild's binary install, tree-sitter-javascript's native build,
electron-winstaller's arch selection, node-pty's native build).

Generated-by: GLM-5.3-Flash (ZCode)
chore: allowlist fsevents@2.3.3 install script

fsevents@2.3.3 is a darwin-only optional dev dependency with
hasInstallScript: true in the lockfile; a fresh install on macOS trips the
approval gate exactly like the eight entries this PR already refreshed.
The allowlist was scanned on Linux, where the os: ["darwin"] guard keeps
the package out of node_modules - that is how it was missed.

Generated-by: GLM-5.3-Flash (ZCode)
chore(ci): guard allowScripts against lockfile drift

npm's install-script approval gate keys approvals on exact name@version,
so a routine bump silently stales the map and a new optional script-
carrying dependency (fsevents) escapes it entirely. The new
check-allow-scripts script fails on any script-carrying lockfile entry
the map does not key, any map key trailing the lockfile, and any
non-boolean decision; the dependency-audit workflow runs it beside the
shipped-closure audit so the drift cannot land.

Generated-by: GLM-5.3-Flash (ZCode)
Merge remote-tracking branch 'upstream/main' into fix/allow-scripts-versions
chore(ci): retrigger after a flaky code-mode run

The affected-test lane failed on a single timing-sensitive assertion in
code-mode.test.js ("excludes host waiting from the execution budget and
preserves dependent Promise.race progress", false !== true). The same
test file passes three consecutive local runs (33/33 each) on this
exact tree; nothing in this PR touches the runtime. Tree-identical
retrigger.

Generated-by: GLM-5.3-Flash (ZCode)
chore(ci): retrigger the windows packaging lane again

The Release Windows package job crashed twice at the same point: the
electron-builder icons@1.2.3 icon-tool child process died with
0xC0000005 on the Windows runner (the ico output had already been
written). main's own package lane passes on the same sources, so this
is runner-side, not this PR. Third attempt; if it crashes at the same
point again I will file an issue with the three logs.

Generated-by: GLM-5.3-Flash (ZCode)
Merge upstream/main (8648258)

Brings in the dependency-refresh train through apache#5839/apache#5860. The single
conflict was the root allowScripts block: upstream mechanically carried
"@jackwener/opencli@1.8.8": true forward from the dependabot bump, while
this branch holds the reviewed deny (opencli's skipped shell-completion
postinstall produces no artifact Maka needs). Keep the deny decisions and
re-key the three bumped versions to the new lockfile:
@jackwener/opencli 1.8.7 -> 1.8.8, @astryxdesign/cli and
@astryxdesign/core 0.6.2 -> 0.6.3. The other six entries already match.
scripts/check-allow-scripts.mjs passes against the merged lockfile
(9/9 install-script packages covered) and its suite stays green.

Generated-by: GLM-5.3-Flash (ZCode)
…#5820)

Issue apache#5537 reported capCodePoints returning nearly the whole string
for maxCodePoints <= 0; that helper (packages/core/src/thread-search.ts)
was removed with the TypeScript thread-search stack by 8d82007 before
the fix could land. The same boundary bug survives in the sibling the
issue cites as correct: sanitizeUnicodeText treats a negative cap as
uncapped - the loop's early break never fires, the whole input is
expanded into the points array, and slice(0, negative) drops only a
couple of trailing code points, so 'hello' @-2 came back as 'hel...'.
An empty input even came back as the ellipsis suffix, contradicting
the "empty input returns ''" contract.

Clamp the cap with Math.max(0, ...) so negative budgets behave exactly
like 0 (suffix-only for non-empty input, '' for empty), matching
truncateUtf16Safe's `maxUnits <= 0` guard; drop the now-always-true
`>= 0` check in the loop. No caller passes a negative cap, so existing
paths are unchanged.

The sanitizeUnicodeText cap boundary had no test coverage; new tests
pin the zero-cap and negative-cap cases (both red on the previous
code), plus the positive-cap and surrogate-pair paths. Core suite
918/918 after a clean rebuild (a stale cross-branch dist initially
failed unrelated files); biome on both touched files and
check:asf-headers clean.

Fixes apache#5537

Generated-by: GLM-5.3-Flash (ZCode)
… not the set (apache#5862)

The digest behind every candidateRef included each eligible Session's `name`
and `updatedAt`, and it was taken in presentation order (`updatedAt`
descending). Any Session's automatic title commit or timestamp bump therefore
rotated every ref in the page, so a delegation into a target that had not
moved was rejected with `candidate_set_stale`. That is the apache#4785 flake, and
the same race a user loses by opening WorkHub and clicking delegate while an
unrelated Session is renamed.

`candidateSetId` now digests only the menu: Session id, workspace projection
and derived state, in id order. `candidateRef` keeps the full per-Session
identity, so a target that really did move still fails closed. The three
set-wide equality checks stay, so the tested contract that a stale
`candidateSetId` is rejected is untouched.

One expectation in the gate suite moves from `candidate_set_stale` to
`candidate_unavailable`: `rejects stale candidates before assignment` mutates
the target's own `lastMessageAt`, which is now visible only through its ref.

Measured at 6e21e61: gate, coordinator, protocol and tool-profile suites
86/86; the named flaky test 3/3; the new regression test passes with the
change and fails with only the gate file reverted, emitting
`candidate_set_stale`. Sunrioa's second reproduction, where the target's own
`updatedAt` catches up after its turn reports completed, is deliberately not
addressed here: `updatedAt` is still in the ref, so that window still
conflicts, and clearing it needs a call on whether a completing turn should
invalidate a delegation into it.

Generated-by: Qoder (AI assistant)
…rift (apache#5867)

Re-creation of apache#4899 without the disclosed Grok-generated content, per
the maintainer guidance on that PR. apache#5172 already landed the bulk of the
Computer Use audit on main; two code-anchored corrections from that
review remain outstanding:

- foundation-contract: the intervention/lock/stop matrix row still said
  FAIL with no Desktop production event producer, but the idle-input and
  screen-lock guards are wired via
  apps/desktop/src/main/desktop-native-capability-assembly.ts and mapped
  to user_intervened/screen_locked in
  packages/computer-use/src/maka-cu-backend.ts. The row is PARTIAL now;
  there is still no attributable macOS event-tap producer.
- model-loop-foundation: keyboard actions were listed as disabled and
  fail closed alongside the coordinate and pixel paths, but press_key is
  a semantic action and type/key are implemented in maka-cu-backend.run
  under the 6.4 focus-binding rules.

Refs apache#3522
Refs apache#4899

Generated-by: GLM-5.3-Flash (ZCode)
…to the protocol codec (apache#5887)

turn.ts、message.ts、session-continuity.ts 各自维护一份逐字相同的
requireEncodedByteLimit 私有副本,与 protocol/codec.ts 的公共实现并存。
三处改为从 codec.js 导入公共实现,删除私有副本(3 files, +3/-36)。

行为披露:超限时的协议错误 message 文本从 `Invalid <label>` 变为
`<label> exceeds byte limit`(codec 公共实现的既有文案)。接受/拒绝
判定、错误类型(RuntimeHostProtocolError)与 code(invalid_frame)均
不变;全仓无测试或消费方断言旧文案。JSON.stringify 失败与 undefined
两条路径两版本文案本就逐字相同。

apache#5038 读写放大清单的 byte-limit helper 整合项。packages/core 的
skill-invocation.ts 存在变体副本(无 label 参数、UTF8.encode 计长),
因依赖方向(core 不依赖 runtime-host)不在本单范围。

Refs apache#5038
Generated-by: GLM-5.3-Flash (ZCode)
Merge remote-tracking branch 'upstream/main' into refactor/byte-limit-integration
chore(runtime-host): declare the byte-limit consolidation as a protocol-compatible change

The epoch guard counts the three touched protocol files as a protocol
change and demands an epoch past 201. The change deletes three private
requireEncodedByteLimit copies in favor of the shared codec
implementation: no wire shape, field, key, error code, or accepted
value changes; the only observable difference is the over-limit error
message wording, which no test or consumer pins. Declared per the
protocol-compatible-changes README instead of burning an epoch.

Refs apache#5038
Generated-by: GLM-5.3-Flash (ZCode)
Preserve immediate socket cancellation while bounding dispatcher destruction to avoid wedging shared transport callers. Cover successful HTTPS CONNECT and stalled dispatcher teardown.

Generated-by: Codex (gpt-6.1-sol)
fix(runtime): retire dispatchers before aborting h2 tunnels

Exercise negotiated HTTP/2 CONNECT teardown and retain the bounded destroy safety net.

Generated-by: Codex (gpt-6.1-sol)
…lure (apache#5922)

A candidate that loses the launch election proves another candidate owns the
root and is still coming up. The client reported that loss as the Host's own
failure instead, and stopped probing. With a transcript store large enough to
keep the winner out of registration past the moment it is asked about, the
desktop saw a failure about a second after it asked, and every retry saw the
same thing - the permanent unavailability in apache#5843.

A lost election is now evidence to keep waiting on: the election runs its
window and keeps reconnecting to the candidate that won. The launch throttle is
left alone, so a root whose winner dies is still rescued by the next attempt
rather than stranded, and the loser's startup diagnostic stays on disk because
that trace is what made the storm visible in the first place.

Generated-by: Qoder (AI assistant)
fix(runtime-host): stop the lost election from ending the window path too

CI on 807a9b4 failed exactly one test, and it was the new one: "a candidate
that loses the launch election is waited on, not replaced". The in-loop guard is
not the only exit — once the window is spent, the end-of-election path returns
whatever startup failure was recorded without asking what it was. On Linux the
loser's report resolves after the deadline, so the in-loop guard never sees it
and the terminal path still reported launch_election_lost.

Both exits now go through one predicate, and the test asserts only the property
that must hold on either: a lost election never decides the outcome. Three local
repeats exercised just the in-loop exit; this case is CI's, not mine.

Generated-by: Qoder (AI assistant)
fix(desktop): wait the client's election window before replacing a candidate

The Desktop gave up on a starting Host at 45s while the client elects for 75s
(connect-or-spawn.ts DEFAULT_ELECTION_DEADLINE_MS, raisable through
MAKA_RUNTIME_HOST_ELECTION_DEADLINE_MS). With a 45s manager-side window under a
75s election window the manager can declare a candidate dead while the election
it lost is still inside its legitimate window, which is the replace-and-
reshuffle amplifier in apache#5843: a large transcript store keeps the winner out of
registration past 45s, the manager swaps, and every replacement loses again.

The 45_000 arrived with the parity sweep apache#2216 as a plain fallback carrying no
alignment argument. Rather than add a second magic number, the Desktop fallback
now resolves through the channel the client already honours - caller value, then
MAKA_RUNTIME_HOST_ELECTION_DEADLINE_MS, then the client's own default, which is
exported for that purpose - so both windows are derived from one constant and
cannot drift apart again.

Verified on this host (Windows, scoped suites, no Electron runtime launched):
- tsc -b packages/runtime-host and tsc -p apps/desktop/tsconfig.main.json: rc=0,
  zero errors.
- dist/main/__tests__/runtime-host-desktop-candidate.test.js: 27 pass / 0 fail.
  Baseline measured before the change was 26 pass / 0 fail.
- Counterfactual: putting `electionDeadlineMs ?? 45_000` back in the resolver
  body makes the new test fail with `45000 !== 75000` at 1 fail / 26 pass, so it
  discriminates on the value, not on a missing symbol.
- packages/runtime-host scoped set (connect-or-spawn-env, managed-deployment,
  wait-for-ready, startup-error, host-profile, candidate-startup-failure):
  60 pass / 0 fail.
- biome check on all four changed files: exit 0, no diagnostics.
- Hook payloads run by hand, because spawnSync on node_modules\.bin\biome.cmd
  dies with EINVAL here (errno -4071): asf-license-headers check-staged rc=0,
  protocol-epoch-check --staged rc=0 at epoch 202, git diff --cached --check
  rc=0. biome-staged-check.mjs crashes on that spawn rather than reporting a
  finding; its substance is the biome check above.

Deliberately unchanged, and measured rather than assumed:
- host-profile.ts:595 and wsl-environment.ts:57-58 keep their own 45s defaults.
  connectRuntimeHostProfile has exactly one production caller, the Desktop
  candidate at :501, and it now always supplies readyTimeoutMs, so neither
  fallback is on the Desktop path any more. Widening a shared library default for
  callers that do not exist today is a bigger call than this issue.
- candidate-startup-failure.ts:27 still classifies launch_election_lost as
  permanent, per the reasoning on apache#5843.
- startup-diagnostic.test.js fails `54 !== 0` here both with and without this
  change, proven by rebuilding from HEAD's copies of the two client files, so it
  is a pre-existing host failure and not a regression. The full runtime-host
  suite was not run and no full-suite number should be read from this commit.

Generated-by: Qoder (AI assistant)
apache#5938)

Prepare and decode the sidebar-preview backdrop before yielding the native WorkHub view, and retain it until native restoration is acknowledged. Cover cancellation, stale responses, and popup removal without blocking presentation commands.

Generated-by: OpenAI Codex
test(desktop): stabilize WorkHub preview and wheel interactions

Restore sidebar row hover after preview dismissal before clicking its hover-only action. Compare native floating-window bounds during wheel dragging, rather than intrinsic button widths that legitimately change when the model label updates.

Generated-by: OpenAI Codex
- Bold closed right after a full-width colon and before a letter is not
  right-flanking under CommonMark, so GitHub and npm print the asterisks.
  Move the colon outside the emphasis in the CLI Chinese README, the
  agent-graph scheduling draft, and the Chinese Nightly npm notice.
- ARCHITECTURE.zh-CN.md: `Runtime Runner` -> `RuntimeKernel`, and
  `[ENGLISH]` -> `[English]`.
- agent-swarm.md: `AiSdkTurn`, not `AiSdkBackend`, appends the swarm prompt.
- README native/ entry: name the Windows Runtime Host task launcher.
- DESIGN.md: drop `info-light`/`info-dark` (the document states there is no
  info colour), correct `surface-overlay-dark` to the derived 0.223, and
  repair the unparseable `--warning` sentence.
- ui README: five export surfaces, not four.
- cli-npm-release: drop the stray `version=0.1.0` from the dist-tags step.
- ACP README: state the 175 -> 176 epoch bump as past (apache#5386).
- eval README: `machine path ... is unavailable` means the env var is unset;
  refresh stale install-preflight line references.
- website README: the published .asf.yaml also sets `staging.profile: ~`.

Fixes apache#5624

Generated-by: Claude Code
Co-Authored-By: Claude Code <noreply@anthropic.com>
…pache#6045)

Part of the apache#3522 docs-verification campaign (Sandbox and privacy group).
All four documents were checked claim-by-claim against the code; the group
is in good shape and needed only targeted corrections:

- workspace-privacy-context: the cited example file
  runtime-host-search-ipc-main.ts was removed by apache#5531 — replaced with the
  live queryRuntimePolicy consumer runtime-host-settings-ipc-main.ts;
  last_verified refreshed (content changed via apache#5742).
- permission-onboarding-plan: the permissions-actions.ts line reference
  pointed at a pre-ASF-header line number (now a type-union member); it now
  cites the deep-link/openExternal block (lines 61-90).
- windows-sandbox-rfc (EN+ZH): the §6.5 status date corrected to the apache#3722
  merge date (2026-08-25); the "typed unavailable reasons are deferred"
  wording now distinguishes the readiness-probe-level collapse from the
  transform layer, which already emits unsupported_platform /
  backend_not_available / invalid_request; last_verified refreshed.

Refs apache#3522

Generated-by: ZCode
Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/squirrelchat/smol-toml/releases)
- [Commits](squirrelchat/smol-toml@v1.8.0...v1.9.0)

---
updated-dependencies:
- dependency-name: smol-toml
  dependency-version: 1.9.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
… (apache#6042)

Part of the apache#3522 docs-verification campaign (Tools and capabilities group).
All three documents were checked claim-by-claim against the code; the stale
ones are corrected:

- MCP architecture draft: the suggestion catalog with third-party brand SVGs
  is live on the MCP page (apache#5603/apache#5638), so the "no catalog or brand assets"
  passage is replaced; ToolDiscoveryState -> ToolRefreshState and
  ToolSnapshot -> McpToolSnapshot (the shapes were right, the names were
  not); successful tool results are bounded and fail-closed (apache#4915).
- Skill catalog policy: the "Context Inspector" per-project prompt-build
  cache does not exist — the catalog snapshot reports contextStatus unknown
  before a prompt is built and surfaces render the enabled fallback; the
  Needs review marker renders in the Skills panel over IPC; deletion runs
  through the durable catalog transaction (intent, tombstone, deletionFacts).
- Web search capability: DeepSeek native execution is disabled
  (implemented: false since apache#2972; replay-only via apache#5824; re-enablement
  tracked in apache#3689), so the matrix row and the dual-wire narrative are
  marked historical; the removed anthropic-compatible provider type is
  replaced by custom + resolved apiProtocol (apache#5684).
- last_verified refreshed to 2026-10-10 on all three.

Refs apache#3522

Generated-by: ZCode
docs: scope the deepseek status note and sharpen two pass-3 wordings

Automated-review P3s: the native-search disablement applies to the deepseek
provider type on the Responses wire (a custom anthropic-messages connection
still executes), apache#2972's original reason was the open-responses function-tool
serialization with the web_search-ignore reason arriving via apache#5824, and the
catalog snapshot never carries a prompt-build rank at all (contextStatus
'unknown' is only for enabled/valid/unshadowed items). Also credits the
McpToolCallError fail-closed path and places both budget checks inside
callTool.

Generated-by: ZCode
Mount the existing WorkHub enablement provider instead of passing the retired enabled prop. This restores Desktop compilation after apache#5938 met the provider-owned switch from apache#5936 without changing production behavior.

Generated-by: Maka (gpt-6.1-sol)
…pache#6051)

* refactor(workhub): simplify coordination and execution configuration

Use the coordinating agent directly, persist WorkHub model settings, scope delegated permissions to executions, and separate steering from stop and delegation. Preserve historical recovery records and return worker missing information through ordinary follow-up messages.

Generated-by: Codex

* fix(workhub): propagate delegated execution policy and steering questions

Preserve delegated permissions across child and graph execution lineage, restore scoped policy from durable Host facts, and update question handling when steering an existing backend.

Generated-by: Codex

* fix(runtime): support frozen stores in delegated execution policy

Use an independent proxy facade so bound methods and permission read overrides do not violate invariants on frozen Host stores. Exercise the production frozen facade shape in the scoped-policy regression test.

Generated-by: Codex

* feat(desktop): route remote chats through shared WorkHub

Add remote message handling settings, durable source-aware replies, and compact interaction choice answers.

Generated-by: Codex

* fix(core): simplify remote message source labels

Generated-by: Codex

* fix(desktop): keep message copy actions visible

Generated-by: Codex

* fix(desktop): size WorkHub rails to message bubbles

Generated-by: Codex

* feat(workhub): preserve intent and show clarification handoffs

Generated-by: Codex

* fix(workhub): link result replies to their source task rails

Result notification turns had no links unless they delegated again. Derive their Host-scoped task identity from the durable result origin so rails and filters also cover tool-free result summaries.

Generated-by: Codex

* feat(workhub): add wn current-work and next-prompt shortcut

Generated-by: Codex

* fix(workhub): show progress and next steps for three recent works

Generated-by: Codex

* feat(workhub): place concise wn suggestions in composer draft

Generated-by: Codex

* fix(workhub): simplify answer summaries and balance result spacing

Generated-by: Codex

* style(workhub): reduce spacing before result notices to 4px

Generated-by: Codex

* refactor(ui): share transcript disclosure for selected answers and completion

Generated-by: Codex

* style(workhub): remove handoff content disclosure

Generated-by: Codex

* style(workhub): remove gap before result notices

Generated-by: Codex

* style(ui): highlight focused choice instead of picker outline

Generated-by: Codex

* fix(ui): confirm question answers on option click

Generated-by: Codex

* fix(desktop): localize WorkHub remote handling settings through catalog

Generated-by: Codex

* fix(ci): refresh surface inventory for shared transcript disclosure

Generated-by: Codex

* test(desktop): retain pending choices with keyboard selection

Generated-by: Codex

* fix(desktop): align story geometry with rails and timestamp sizing

Check WorkHub rails against bubble bounds after the shorter-rail design. Give localized usage timestamps 8px more room so Linux font metrics fit without weakening the overflow assertion.

Generated-by: Codex

* fix(workhub): preserve steering policy across physical successors

Resolve the latest durable policy for each physical continuation, including steered ordinary admissions with no sourceMessages. Add production composition coverage proving successor inheritance and restoration on a later user Turn. Use compatibility epoch 214 to distinguish this contract from the open epoch-213 changes.

Generated-by: Codex

* test(desktop): reconcile WorkHub fixtures after main rebase

Provide the shared WorkHub enablement context to the new dock backdrop fixture and refresh the merged UI inventory totals.

Generated-by: Codex

* fix(protocol): assign WorkHub an unused compatibility epoch

Epoch 214 is also declared by open PR apache#5548. Verified all 188 current open PR heads: apache#5394 and apache#5969 use 213, apache#5548 and this PR use 214, and 215 is unused. Move WorkHub to 215; allocation must be rechecked at integration because the base-only CI guard cannot reserve numbers across open branches.

Generated-by: Codex
…pache#6043)

Part of the apache#3522 docs-verification campaign (Managed workspace group).

Scope note: three of the group's five documents
(runtime-managed-workspace-baseline-open-v1, -execution-admission-v1,
-owner-v1) no longer exist — they were removed intentionally by apache#4174
(refactor: remove dormant Git workspace write path) together with the
GitWorkspaceService/ManagedWorkspaceOwner implementation they described.
This PR therefore covers the two surviving documents; the group checklist
on the tracking issue needs the same correction.

Both surviving documents verified clean against main: every symbol, SQLite
table (runtime_workspace_epochs/versions/heads, reservations, storage root
binding), schema 7-20 narrative, failpoint set, and CI non-blocking claim
checked out. Metadata only:

- runtime-workspace-version-authority-v1: add front matter
  (translation_status, last_verified) and refresh the update date (apache#4718
  changed content after it).
- managed-dependency-storage-authority-v1: last_verified 2026-09-04 ->
  2026-10-10 (apache#4718 updated the document without bumping it).

Refs apache#3522

Generated-by: ZCode
docs: place the ASF license comment directly after the frontmatter

Same ASF source-header policy fix as the recovery-resume pass: no blank
line between the closing frontmatter delimiter and the license comment.

Generated-by: ZCode
…pache#3522) (apache#6038)

Part of the apache#3522 docs-verification campaign (Recovery and resume group).
Every claim in the group was checked against the code; the stale ones are
corrected:

- PR B has landed: runtime_continuation_claims, continuation_start_v2,
  AgentRunLineage, and replayManifestDigest replace the planned
  events.length high-water, in-process claim, and continuationSource
  header (which survives only in the legacy run-header decoder). The PR
  ladder and follow-up gaps now say so.
- Storage is SQLite-only: the phase0/phase1 contracts and the main
  architecture doc no longer describe JSONL as current or SQLite as
  future; the retired legacy JSONL import is called out as retired.
- Runtime Host compatibility epoch 57 -> 206; the desktop code-reading
  map path to use-shell-resume.ts fixed; the Chinese-only sentence
  claiming a system reminder after outcome_unknown removed (the system
  prompt stays byte-stable); the EN doc now mentions the supervisor-wake
  recovery its translation already described.
- The phase1 storage-boundary section, phase3-4 design notes, and the
  extraction ledger no longer describe file-backed stores as current;
  the ledger carries a dated erratum for its historical tables.
- last_verified refreshed to 2026-10-10 (main docs updated; phase0 and
  phase1 gain the marker).

Refs apache#3522

Generated-by: ZCode
docs: address automated review findings on the recovery docs pass

- Restore the compatibility epoch's historical value (57 at the apache#3506
  change) instead of naming a live epoch that drifts.
- Keep PR C in the PR ladder and follow-up gaps; only PR B is marked
  merged, and the EN/ZH pair stays in sync.
- Phase 1: T1/T2 bracket the side effect without making it transactional;
  operationId is live, not a later phase.
- Phase 0: the committed prefix is a runtime_events prefix, and the store
  the contract runs against is the SQLite RuntimeEvent store.
- Ledger erratum: AgentRunLineage lives in packages/runtime; the ADR
  implementation note names RuntimeBoundaryCursor (V1|V2).

Generated-by: ZCode
docs: fix the phase0 future-work attribution and sync the zh flowchart

The former future work this contract narrated was the SQLite tool journal,
not the RuntimeEvent store; phase0 now says it runs against the canonical
SQLite RuntimeEvent store with the tool journal since landed. The zh flowchart
gains the merged status label its EN counterpart has, and phase0/phase1 move
last_verified into front matter.

Generated-by: ZCode
docs: place the ASF license comment directly after the frontmatter

The ASF source-header policy check requires the license comment to
immediately follow the closing frontmatter delimiter, without a blank
line, matching the convention of the docs that already carry frontmatter.

Generated-by: ZCode
Creating a project required typing a name before Choose folder would
enable. An empty name is a valid choice: the catalog already names the
project after the folder when projects.add is called without one.

Leave the name field optional, keep Choose folder available, and pass a
trimmed name through only when one was typed. Update en / zh-CN / zh-TW
copy to match.

Fixes apache#6047
@cursor
cursor Bot force-pushed the cursor/optional-new-project-name-e179 branch from 760b9c6 to db21e8f Compare October 11, 2026 06:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New project requires a name before the folder can be chosen