Skip to content

chore(fork): sync upstream multica v0.6.5 (CHE-1900) [merge] - #325

Merged
congvc-dev merged 90 commits into
mainfrom
agent/x99-claude-sonnet/529e13bdbf3b
Oct 22, 2026
Merged

congvc-dev merged 90 commits into
mainfrom
agent/x99-claude-sonnet/529e13bdbf3b

Conversation

@congvc-bot-x99

@congvc-bot-x99 congvc-bot-x99 Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Status

Source integration only. Sol reconciled the PR-packet finding on 22 October 2026. It is not deployment approval. Head 0d0ac595cadd97625de7972110fe8406bf0ea5a0 (merge commit of main 571008ce5d17f5c422d02ad1ee4c1e46b79bbba4; earlier heads 2b765cd05 (merged main 462e0ac93) and 77e0b0cc5 (merged main 0134372945)). CI on this head: 20 pass, 2 skipped, none failed or pending (read after all runs finished, 16:36 ICT; read again at 19:10 ICT on 22 October 2026: 20 pass, 2 skipped, none failed or pending). Exact-head whole-suite E2E on this head: FAIL, 84 passed and 8 failed (see the last bullet before the OCR note); The run stays FAIL and is not accepted as PASS. Eight cases carry a scoped waiver (see the Waiver and signed review section below). Previous head 2b765cd05: 20 pass, 2 skipped. History kept: on a8a04f350 CI was 14 pass, 2 skipped, 1 fail (close-target, a main-side workflow defect fixed by #352 and #351); on b84cebd9e it was 14 pass, 2 skipped, none failed. Base main 571008ce5d17f5c422d02ad1ee4c1e46b79bbba4 (merged into this branch; the previous merged main commits were 462e0ac93 and 0134372945; earlier merged main commits were 1de88837, 3c84240ed and a8a04f350's base 677c664ac; no rebase, no force-push). The original base was 17a8fae0d5dd25380eb39bd7d7bffc7e4e361ce0. Upstream tags merged: v0.6.4 47120a4131564175da044d63d897ed9b5d76979a and v0.6.5 (tag object 8a4d3eeb521e699eb977a6a5709bc834bb1927bb, peeled commit 654b8ca954034f8181eb6bbb4fc298dede760156). UPSTREAM_VERSION is 0.6.5. Review history: the independent review and OCR on 29ea639e8 confirmed the four P1 fixes; on 47e09d604 Luna found no code P0/P1 and kept readiness at CHANGES REQUESTED for the open gates. Since then the branch took two merges of main (1de88837, 3c84240ed) and test, e2e and one small app fix (see below). At that time the head had changed and a fresh review was NOT-RUN. That is superseded for head 0d0ac595c by the signed review recorded below. Cheese chose B on the E2E gate (retain, repair; comment 01a15c2c-ca66-7ed6-b6dc-0a98dee37a94). The scoped waiver below replaces B only for the eight listed cases.

Changelog: none

Conditions

  • Release-tags-only policy (CHE-1054 / CHE-1380). Same flow as v0.6.3 (CHE-1835 / CHE-1837, PR chore(fork): sync upstream multica v0.6.3 (CHE-1835, CHE-1837) [merge] #296).
  • Cheese merges. Tags v0.6.3 and v0.6.4 are not pushed. They are pushed only after this PR merges. The deploy and the daemon update are separate Cheese gates.
  • Upstream tags checked against upstream (multica-ai/multica): v0.6.3 peels to 18fa9f728a9e25f2465df2789631edb2a75442ad (annotated tag object 61bf2402edf4a585f8356f9b68b03f9e1bdabafe). v0.6.4 is lightweight at 47120a4131564175da044d63d897ed9b5d76979a. Fork origin has tags only up to v0.6.2.
  • Open PRs that claim migration numbers 671 or later renumber after this PR merges.

Change

Merge of upstream v0.6.4 (41 commits). Seven files conflicted:

  • packages/core/api/client.ts: kept the fork wakeup-definition imports. Took upstream ArchivedInboxFilters.
  • packages/views/chat/components/chat-window.tsx: took upstream ChatWindowMessages. Ported the fork continue/fork links, the session list and the interactive-login banner into it. The banner shows when the session list fails with interactive_login_required, and also when the message page fails with it. Limit: a message-page-only failure does not disable the input any more. The old code disabled it. The session list fails first in practice, and it still disables the input.
  • server/cmd/multica/main.go: kept both imports.
  • server/pkg/agent/agent.go: kept the fork credential-isolation fields. Added muse to the runtime comment.
  • server/internal/integrations/ghsnapshot/snapshot.go: upstream replaced GraphQL with REST. Took upstream. Ported the fork BaseRef (CHE-1417) to REST base.ref.
  • server/internal/integrations/ghsnapshot/refresh_db_test.go: kept the fork github-enabled tests and the upstream REST test.
  • server/internal/integrations/wecom/relay_outbound.go: took the upstream caller-deadline check. Kept the fork bounded sleep and the ctx.Done() select.

Two merge-level defects found after the textual merge:

  • server/pkg/db/generated/runtime.sql.go was stale. Three task.* queries lacked columns. Regenerated with sqlc generate v1.31.1 (sqlc diff is empty now).
  • Upstream migrations 583_runtime_profile_add_muse and 584_quick_action_optional_target collided with fork 583_wakeup_default_capacity and 584_wakeup_default_scope_index. They are now 671_runtime_profile_add_muse and 672_quick_action_optional_target (first 669 and 670, then 670 and 671; main took 669 for chat_fork_source_message and later 670 for run_gate_event, and CI failed TestMigrationNumericPrefixesAreUnique on a merge ref until the renumber), SQL bytes unchanged (precedent: CHE-1837). server/cmd/migrate/README.md records it.

Waiver and signed review (head 0d0ac595c)

Recorded 22 October 2026 on the Sol/Luna-2 results; this section replaces the earlier "not waived" and "NOT-RUN" wording for this head only.

  • Signed review: comment 01a1637b-253a-73d0-b12c-5183016efc45, x99-codex-luna-2 (gpt-6-luna, OpenAI), independent of the Anthropic author, signed 18:58 ICT. Exact pair: head 0d0ac595cadd97625de7972110fe8406bf0ea5a0, base and current main 571008ce5d17f5c422d02ad1ee4c1e46b79bbba4. Verdict: CHANGES REQUESTED for PR-packet consistency only, no P0 and no new code P1. The request was to refresh this body, which this section does. OCR (open-code-review v1.13.3) ran on the exact pair: 239 of 456 paths selected, 217 excluded; the receipt is attached to that comment and carries the marker ocr-receipt:0d0ac595cadd97625de7972110fe8406bf0ea5a0. That review ran no local tests.
  • Observed E2E result, unchanged: FAIL, 84 passed and 8 failed of 92, exit status 1. The eight cases stay FAIL, not PASS: agent-mcp:164, auth-callback-locale:19 (2 cases), auth:22, local-search:112, navigation:32, quick-actions:13, settings:41.
  • Scoped E2E waiver: Cheese answered "A" to the question of 22 October 2026, 17:36:17 ICT (stop the author run and split the eight failures to CHE-1968). Provenance is comment 01a16347-192d-7d07-abf3-0a5683bec6ce, recorded through the CHE-1838 policy-only relay route. It is not a native member decision record and not an independent check of Cheese's member ID; the native chat message ID is unavailable. The waiver applies only to PR chore(fork): sync upstream multica v0.6.5 (CHE-1900) [merge] #325 and only to those eight cases, which are named from the pinned failure receipt and not from a verbatim human list. It replaces B only for these cases. It diagnoses no failure and waives no other gate. CHE-1968 owns the repair work; the uncommitted partial fixes stay outside this PR.
  • Gates not waived: Cursor real smoke stays unauthorized and NOT-RUN. The historical unnamed pkg/agent failure stays UNDIAGNOSED. At the author/reviewer snapshots, no ready, merge, tag, deploy or rollout happened. The author/reviewer left the PR draft. Sol's later readiness handoff records the current PR state. Cheese merges.

Evidence

Test-first, observed:

  • Red, before the fix: TestFetchPRSnapshotRESTPagination failed with BaseRef = "", want the PR's base branch (CHE-1417). Green after porting base.ref.
  • Red, before the fix: three tests in chat-window-messages.test.tsx failed (continued empty session shows the list, banner from interactiveLoginRequired, banner from a message-page error). Green after the port: 8 of 8 pass.
  • Red, before the renumber: TestMigrationNumericPrefixesAreUnique failed with migrations 583_runtime_profile_add_muse and 583_wakeup_default_capacity share numeric prefix 583. Green after the renumber.
  • Red, before the fix (CI run 39418324719 on the first head, reproduced locally): scripts/check.test.sh failed with PORT: unbound variable when a PATH entry held its own .env, because . .env searches PATH first. The new case env file is not looked up on PATH failed with status 1, want 0. Green after check.sh sources ./.env.
  • Floating-chat lifetime flake (frontend-views-test (2/2) on 785f8e70b, expected 0, received 1; seen locally in a full parallel run as expected 1 to be +0). Cause, shown with a scratch test: the message observer unsubscribes in a passive effect cleanup, so at the instant the list leaves the DOM the observer count is still 1. The upstream test read the count right after waitFor(list gone). Fix: wait for the count to reach 0 (two places). No deterministic red exists for the race itself. After the fix, views shards --shard=1/2 (3401 tests) and --shard=2/2 (3285 tests) pass locally at default parallelism.
  • The sqlc regeneration is not test-first. sqlc diff showed the drift, and it is empty after sqlc generate.

P1 dispositions (Luna, CHANGES REQUESTED on 3a13e0855). All three sit in code that arrived from upstream v0.6.4. Each is fixed here, with a test written first, observed red, then green:

  • server/pkg/agent/cursor_supplement.go: a hooks.json counted as managed when it merely contained the helper argument, so Multica overwrote it and deleted it at cleanup, and a user hook beside a managed one was lost. Now a file counts as managed only when every top-level key, event, entry field and command is Multica's. Red: TestCursorSupplementKeepsMixedHooksFile failed 4 of 4 subcases (CursorHooksInstallable accepted a hooks.json with user content). Green after the fix. The existing stale-managed-file test still passes.

  • server/internal/integrations/channel/engine/session.go: quoted InlineIDOnly media went into the issue Markdown through the append fallback, as if the reporter attached it. Now InlineIDOnly media is left out of the Markdown, replacements and fallback alike, and the issue is not rewritten when nothing is left to add. The attachment row is still created. Red: TestBindMediaRefs_IssueMarkdownLeavesOutQuotedIDOnlyMedia (the quoted image was in the Markdown) and TestBindMediaRefs_QuotedIDOnlyMediaAloneLeavesTheIssueUntouched (1 rewrite, want 0). Green after the fix.

  • server/internal/integrations/wecom/ws_sender.go: writeCtx checked the caller's context before the write guard but not after, so a cancellation during the guard's permission lookup still wrote the frame. Now it checks again after the guard and returns an errNotAttempted-wrapped error. Red: TestRequest_ACallerThatGivesUpDuringTheWriteGuardWritesNothing failed with request = <nil>, want errNotAttempted wrapping the cancellation. Green after the fix.

  • Cursor command ownership (Luna's remaining P1 on c3be0638e, cursor_supplement.go:191): the first fix still accepted any command that contained the helper argument, so '<binary>' '<helper>'; './audit.sh' passed as managed, was overwritten and was deleted at cleanup. Now a command counts as managed only when it is exactly what cursorHookShellCommand renders for <binary> <helper> (two single-quoted words, nothing else; the daemon builds it from exactly that argv). Red, observed before the fix, on c3be0638e: TestCursorSupplementKeepsHooksFileWithModifiedCommand failed 7 of 7 subcases (chained after, chained before, &&, extra argument, substitution, unquoted, helper only inside a path). Green after the fix. The positive test TestCursorSupplementReplacesLeftoverManagedCommand (3 binary paths, among them a path with a space and one with a quote) passed before and after, so a leftover managed file is still replaced.

  • Post-fix counts on 29ea639e8 (go test -run ... -count=1 -v, observed this round): pkg/agent Cursor hook tests, 4 top-level tests and 18 PASS lines in total (mixed-file 1+4, modified-command 1+7, leftover 1+3, unmanaged 1), 0 FAIL. channel/engine quoted-media tests, 2 of 2 PASS. wecom write-guard cancellation test, 1 of 1 PASS. 0 FAIL in all three. scripts/test-go.sh --only agent passed 3 of 3 reruns (46 s each).

  • pkg/agent test flake, diagnosed this round (commit 47e09d604, test files only). Luna's run on 29ea639e8 failed with read-only file system under /home/congvc/.multica/pi-sessions (Pi tests, her sandbox had a read-only HOME) and TestQwenpawWorkspaceArgs: text file busy. These are separate classes. (a) Read-only HOME is an environment limit: the Pi tests write under $HOME; with a writable HOME and TMPDIR they did not fail in 11 runs. (b) text file busy is a code defect in the tests. I reproduced it with writable HOME/TMPDIR and GOCACHE outside /tmp, default parallelism, go test ./pkg/agent -count=1 -v: 2 failing runs of 5. Run 1 failed TestMuseExecuteStreamsTurn (fork/exec .../muse: text file busy), run 3 failed TestDimCleanupKillsHangingChild (fork/exec .../dim: text file busy). Cause: 13 fake CLIs were written with raw os.WriteFile and run at once, while the repo helper writeTestExecutable (which holds syscall.ForkLock) exists for exactly this. Red: the new guard test TestFakeExecutablesGoThroughWriteTestExecutable failed listing all 13 sites, among them qwenpaw_test.go:73. Green after moving every site to the helper. After the fix: 6 runs, 0 text file busy; 1 run failed TestCodexExecuteSemanticInactivityAllowsContinuousMessages (complete delta JSON became visible 0s after turn/started), a timing assertion at X99 load average about 36, which is a different class and is not fixed here. The historical unnamed failure from the earlier round is not diagnosed from these runs; the reproduced text file busy is a candidate only.

  • Resync with main (Sol's ask on 47e09d604, DIRTY against 1de88837): main merged into the branch, one conflict in chat-window.tsx between upstream ChatWindowMessages and main's DM annotations (CHE-1876). Resolution: keep ChatWindowMessages and call useChatAnnotations with the messages the list reports upward (onMessages), so the message observer still leaves with the closing window. Red: the new test reports the loaded messages while mounted and an empty list once it leaves failed (expected undefined to deeply equal [ 'a1' ]). Green after the change; the 38 chat test files pass (515 tests). A later merge took main 3c84240ed, which also took migration 669 (see above). Upstream ancestry is kept: v0.6.4 and origin/main are both ancestors of the head. GitHub reports the PR MERGEABLE and CLEAN.

  • Codex timing, diagnosed from the captured failure (not from passing reruns). The log of the failing run shows TestCodexExecuteSemanticInactivityAllowsContinuousMessages at codex_test.go:4238: status="timeout", codex app-server no progress timeout after 72ms: received turn start but no item, phase first_item_wait, latency=72ms. The fixture emits turn/started and then runs sleep 0.05 before its first item. The first-item timeout is 80% of the test's 90ms, so the margin was 22ms, less than the cost of starting one sleep process when many parallel tests spawn processes. The adapter behaved correctly; the test's margin was too small. The sibling ...ContinuousDeltaProgress had 150ms against the same 50ms pauses. Fix (test only): both tests now use a 2s no-progress timeout and a 10s overall timeout, with the 50ms pauses unchanged. I could not reproduce the failure on demand (60 repeats and 6 repeats of all TestCodex* at -parallel 64, with 48 busy loops, all passed), so there is no new observed red; the red is the captured failure above. After the fix, scripts/test-go.sh --only agent passed 3 of 3 (and 2 of 2 more after the merges).

  • E2E repairs (Cheese's B). Each spec below failed before and passes serially after the change; red is the observed failure from the earlier runs. chat-attachments: the session row needs explicitly_created_at. auth, local-search:107: accept /login?next=. auth-callback-locale: allow the configured API origin, and a real app fix: apps/web/app/auth/callback/page.tsx exchanged the single-use Google code twice under React Strict Mode (red: new test exchanges the single-use code once under React Strict Mode failed, expected "vi.fn()" to be called 1 times, but got 2 times; green after a useRef guard; 23 of 23 callback tests pass). agent-mcp: enable the composio_mcp_apps flag, entries sit under Capabilities as tabs. html-preview-fullscreen:50: unfold the folded description first. comments, comment-agent-routing, comment-steering, issue-content-disclosure:716: a click on a composer shell before React hydrates is dropped, so the specs now click after hydration (helper clickWhenHydrated), find drafts by their text, refocus the draft before sending, and use .first() where the text appears twice. navigation (3): wait for the hydrated sidebar instead of networkidle, which never arrives, and settings entries are links. settings:5: the workspace details form autosaves, so there is no Save button. chat-fork (4) and chat-continuation need a _test database name (setup, no change).

  • E2E still failing after the repairs (serial, --workers=1, _test database, head d66f68c96): issue-content-disclosure 279, 716 (now reaches its last assertion: after Fold All, a draft reply is sent and the thread should reload compact with a "Show N more replies" button, which is not visible), 817, 867; issues.spec.ts:68 (date filter, times out); issue-table 86, 175, 269 (Loaded N of 1001 text not found and related); issue-wakeups.spec.ts:89 (text When this issue moves to In Review · woke Condition Grok not found); local-search.spec.ts:99 (logout does not leave the page); project-resource-ref.spec.ts:14 (release/2026-09 text not found). That is 11 tests, all of which also failed on origin/main before this sync or are fork features I did not trace to a cause. I did not find the cause of these 11 within this round.

  • Full parallel E2E run on head d66f68c96, 4 workers: 22 failed, 64 passed (86 tests), down from 31 failed. The failing set in a parallel run is load-dominated: navigation, settings, chat-fork and auth-callback-locale fail in parallel and pass serially.

  • Cursor hook metadata and install/cleanup races (Luna's open P1 on 5a40d604e). Dispositions: all confirmed by deterministic tests, all fixed in 01fde4357. (a) version and timeout were accepted with any value; now a present version must be 1 and a present timeout must be 10, so a file with other values is the user's. Red: TestCursorSupplementKeepsHooksFileWithForeignMetadata failed 4 of 5 subcases (the fifth, an empty event list, passed before and is the control). (b) The install checked that the workdir had no hooks of its own and then truncated the file in a second step, so a file created in between was overwritten. It now creates the file exclusively (O_EXCL); a leftover from an earlier run is moved aside in one atomic rename, checked, and put back without replacing anything that appeared since. Red: TestCursorSupplementInstallDoesNotOverwriteAFileCreatedMeanwhile and ...AReplacedLeftover both failed (started over a hooks.json created after the check). (c) Cleanup compared the bytes and then removed the file in a second step. It now takes the file aside first, compares that copy, and deletes only the copy; a file the user writes meanwhile is untouched, and a hand-edited file is put back. Red: TestCursorSupplementCleanupKeepsAFileWrittenMeanwhile failed (cleanup left "", want the user's file); the control ...CleanupKeepsAnEditedFile passed before and after. The tests use a small seam, cursorHooksRaceHook, which does nothing in production, to let the user's write land exactly between the two steps; the race itself is not timing-dependent in the tests. Post-fix: the 7 hook-ownership tests of this family give 28 PASS lines with 0 FAIL, all 82 top-level Cursor tests pass, and scripts/test-go.sh --only agent passes.

  • UPSTREAM_VERSION (commit ac64bf443): root file with 0.6.4, plain semver on one line. New test scripts/upstream-version.test.mjs (in the CI selection step) checks the format and that it equals the version in apps/web/package.json. Red: the test failed with ENOENT before the file existed. Note: PR branch agent/x99-agy-argon/aa92e7f5b3d2 (CHE-1910) also adds UPSTREAM_VERSION with 0.6.3; whichever PR merges second resolves that add/add conflict to 0.6.4.

  • E2E repaired in 3708b8c9f. plugin-surface-document.spec.ts did not load because it imported buildSurfaceDocument, which the app no longer has (the plugin document is served by the content origin now). It now tests the wrapper document that exists, buildSurfaceFrameDocument: a srcdoc reload is not reported as hostile navigation, and a plugin error reaches a host that is already listening (the host arms its listener before it assigns srcdoc, as plugin-surface-frame.tsx does). Red: the earlier SyntaxError ... does not provide an export named 'buildSurfaceDocument'. Green: 5 of 5 with plugin-surface-security.spec.ts. issue-content-disclosure:279 was a 60s test budget on a cold dev server plus a 10s settle; it passes with a 30s settle. issue-content-disclosure:362 raced a full page navigation against the description save that follows the upload response; the spec now waits for that save (red 3 of 3, green 2 of 2). project-resource-ref: creating a project leaves the list open, so the spec opens the project (green).

  • E2E still failing and not fixed: issue-content-disclosure 716 (after Fold All, sending the draft and reloading, the thread shows all replies expanded with no "Show N more replies" button; this may be a CHE-479 regression or a spec that predates the reply targeting the new comment, I did not tell which), 824 and 874 (not rerun after the line shift); issues.spec.ts:68 (reopening the filter menu with a date filter active makes the Date item detach and re-attach repeatedly, so hover and focus never settle; possible render loop in the filter menu); issue-table 86 and 269 (the spec looks for Loaded N of M text that the app no longer renders) and 175 (todoRoot.total is 0, expected 3); issue-wakeups.spec.ts:89 (text When this issue moves to In Review · woke Condition Grok not found, state not traced). local-search:99 passed once with a 90s test budget and is load-sensitive. That is 8 to 9 tests I did not repair.

  • Two more Cursor ownership P1s (Luna on 3708b8c9f), both confirmed and fixed in 2790dd346. (a) Missing metadata: the check rejected a wrong version or timeout but accepted an absent one, so a user file with Multica's exact command and no metadata was discarded as a leftover. Multica writes both, so now both must be present and exact. Red: TestCursorSupplementKeepsHooksFileWithMissingMetadata failed 3 of 3 (no version, no timeout, no timeout on a later hook). (b) Overlapping runs: a second run in the same workdir found the first run's file, cleared it and recreated it, and the first run's cleanup then removed the second run's file. Now the runs of one process hold a lease on the file: the first installs it, the last removes it, and a run whose hook command differs is refused. Each run reaches its own relay through its own environment, not through the file, so sharing is safe. Red: TestCursorSupplementOverlappingSessionsShareTheHooksFile failed in both end orders (after one of two runs ended the hooks file is "", want it kept for the other), and ...OverlappingSessionWithAnotherCommandIsRefused failed (a run with another hook command shared the file). Post-fix, observed: the hook-ownership family has 36 PASS lines and 0 FAIL, all 85 top-level Cursor tests pass, a -race -count=3 run of the Cursor supplement tests passes, and scripts/test-go.sh --only agent passes. The lease works inside one daemon process; a second daemon process on the same workdir is not covered and cannot be told apart from a crashed leftover.

  • A gap in my own check: I ran only pkg/agent after the metadata change, and CI backend-tests on 2790dd346 failed TestCursorSupplementHookCommandRequiresInstallableHooks in internal/daemon: its fixture for Multica's own leftover lacked timeout. Fixed in the next commit by giving the fixture timeout like a real leftover and adding a case where a leftover without it is refused. That failure stays on record here. After the fix I ran internal/daemon, internal/migrations and the whole server module in a clean worktree: 84 packages pass, none fail.

  • E2E investigation this round (no assertion weakened, no new spec change). issue-content-disclosure 876 passes. issue-table 86 and 269 assert text Loaded N of M; no source file or locale renders that text any more (checked with a search of packages/views), so those two specs predate a UI change and need the owner to say what replaced it. issue-table 175 receives total: 0 from the real rows API where it expects 3. issue-content-disclosure 725: fixture checks show the compact window works with API-created replies (5, 6 and 7 replies give "Show 2/3/4 more replies"), and an expansion is not kept across a reload (it is session-only), but after Fold All, a UI send and a reload the thread shows every reply; the posted reply does not leave a draft in localStorage and the URL carries no hash, so the cause is still open. Several runs were disturbed by an X99 load average that peaked above 200 from other jobs.

  • Resync and E2E repairs on request of Sol and Cheese (execute the issue then report to sol, comment 01a15f4f-7ab8-7a6c-92b8-d494939de6b0), head 233bfe9a1. Resync: main merged in three more times (merge commits only, no rebase, no force-push). UPSTREAM_VERSION had an add/add conflict with CHE-1910 (it added 0.6.3); resolved to 0.6.4. After that, main's own tests failed because they hard-coded 0.6.3: release-build-stamp.test.mjs failed 2 of 8 ('0.6.4' !== '0.6.3' and Expected mismatch between tag v0.6.4 and UPSTREAM_VERSION 0.6.3 to fail). They now derive the tag cases from the file, so the next sync does not break them. Main took migration 670 after my last renumber; the two upstream migrations are 671 and 672 now. The merge also brought daemon_artifact_version_test.go with a raw executable write: CI on bfb275c68 failed my guard test TestFakeExecutablesGoThroughWriteTestExecutable on it (that failure stays on record), and the file now uses writeTestExecutable. scripts/check-ui-radius-tokens.test.mjs fails in this checkout (Cannot find package 'typescript' from scripts/); it is an environment limit I did not change.

  • Fold All (issue-content-disclosure 725), grounded in CHE-479's adopted option 2 (comment 01a0a3b2-c040-7595-96ec-0a2c7ca3cd4b: a short-lived "just folded" suppression, no new persistent explicit-collapse marker; the temporary pin of an active draft stays). The assertion intent is unchanged: after Fold All with an active draft, sending it and reloading, the thread's length preference is compact. Two spec assumptions had gone stale and are now corrected, neither weakens that check. (a) A reload right after the 201 restores the draft, which is an active reason that rightly keeps the thread open; the draft is cleared a moment later, so the spec waits for that. (b) Manual collapse persists across reload in this app (multica_comment_collapse in localStorage), so the thread reloads as its folded summary; the spec opens it with "Expand thread" and then asserts "Show N more replies" and no "Reply number 1". Green: 2 of 2 runs.

  • Rows API total (issue-table 175), traced and not changed to 0. The rows endpoint computes total only for the ungrouped root head (issue_table_rows.go: cursor == nil && group.kind == "none" && parent_id == nil); grouped pages carry the group count from /groups, and branch_total is the page size. This is the same code in upstream v0.6.4. The 3 seeded issues are groups.total. The spec now reads 3 from /groups and checks the group counts {todo: 2, done: 1}. Second finding: the spec matched the first child-branch request, but the table fetches the parent's children twice, ungrouped (both children) and in the todo group (same-group child only), and which arrived first varied. It now matches group_key === "status:todo". Green: 4 of 4 runs.

  • Product bug found by issue-wakeups:89 and fixed: the timeline entry for a condition wakeup read "When event condition.met occurs · woke Condition Grok", leaking the scheduler's internal event name instead of the rule's condition. Red: new assertions in wakeup-activity.test.tsx failed (expected '当事件 condition.met 发生时,唤醒了 Emacs' not to contain 'condition.met'). Fix in wakeup-activity.tsx: condition.met is read as the rule's own condition. Green: the 98 issue component test files (1177 tests) pass, and the E2E spec passes 2 of 2.

  • Left as written, pending Cheese's choice: the two progress-text waits in issue-table:86 and issue-table:269 (Loaded N of M), still failing.

  • Serial E2E on this branch (--workers=1, _test database, one dev server, 90 tests including the two plugin specs): 11 failed, 79 passed. Failing: issue-table 86 and 289 (the two text waits), issues.spec.ts:68, issue-content-disclosure 196, 279 and 845, local-search:99, navigation (2), project-resource-ref, settings:41. Several of these passed alone earlier in the day (279, local-search:99, navigation, project-resource-ref), so they are load- and order-sensitive in a long run, not fixed. Two have a traced cause I did not fix: issue-content-disclosure 845 (find reveal) fails because the "Collapse thread" click is intercepted by div.flex.items-center.gap-2.5, and issues.spec.ts:68 because the filter menu closes about 300 ms after it opens when a date filter is active. Parallel runs with 3 workers fail more tests (auth, navigation, agent-mcp, chat-fork) because the workers share one E2E user and workspace, as before.

  • CI regressions on bfb275c68, corrected in 233bfe9a1, with main's shipped migration IDs and SQL untouched (git diff origin/main -- server/migrations adds only 671 and 672). Red, observed locally on bfb275c68 merged with main: TestFakeExecutablesGoThroughWriteTestExecutable failed (daemon_artifact_version_test.go:36 writes an executable with os.WriteFile) and TestMigrationNumericPrefixesAreUnique failed (670_run_gate_event and 670_runtime_profile_add_muse share numeric prefix 670). Green on 233bfe9a1: both pass, plus cmd/migrate quick-action tests. The executable guard is kept; the file was changed, not the guard. Migration checks on 233bfe9a1 with a private Postgres 17: main applies 725 versions; upgrading that database applies exactly 671_runtime_profile_add_muse and 672_quick_action_optional_target (ledger now 727, 670_run_gate_event kept); a rerun applies 0; a fresh schema applies 727; pg_dump --schema-only of the upgraded and fresh databases differ in 0 lines (pg_dump's random restrict tokens excluded). CI on 233bfe9a1: 14 pass, 2 skipped, none failed or pending.

  • Nine non-table E2E repairs, head 5fbd0a754 (Sol's ask after Luna's verdict on 233bfe9a1), no assertion weakened, CHE-479 coverage kept, the two table waits unchanged. Observed red is the failure traced in the earlier runs; green is serial. (1) issues.spec.ts:68: choosing a date leaves the filter menu open, so the click on "1 filter" closed it, and the second click opened it (traced: after the click the menu had 0 items; one more click gave 9). The spec closes the menu, then reopens it. Green 3 of 3. (2) issue-content-disclosure 845 (find reveal): the thread's action buttons take pointer events only while its header row is hovered (elementFromPoint returned the row, not the button). The spec hovers the row first (helper clickThreadToggle, also used by the Fold All spec). Green. (3) issue-content-disclosure find-while-folding and the others in that file: the app redirects an id URL to the identifier URL once the issue loads, which remounts the page; an open find bar was lost about 850 ms after Ctrl+K (traced by marking the description editor, which was replaced). The file now opens issues through openIssue, which waits for the identifier URL (18 call sites). (4) issue-content-disclosure paste-then-close (279) and 362: a full navigation right after the unmount discards the fire-and-forget save, so both wait for the save response first. Green 3 of 3. (5) settings:41: the Composio settings sit behind composio_mcp_apps and the page is "Connected apps" now; the spec turns the flag on, waits for the page's real text, and uses the exact toast text. Green 4 of 4. (6) project-resource-ref: clicks the new project until it opens. (7) local-search:99, navigation (2), and the cold-route timeouts: next dev compiles a route on its first visit, 10 to 20 s in the frontend log (/inbox 20.8 s, /settings 14.1 s, /projects 12.2 s), so whichever spec visited a route first timed out. e2e/global-setup.ts now visits each route once before the suite; on a cold server, local-search and navigation pass.

  • Serial whole suite on 5fbd0a754 (--workers=1, _test database, cold dev server, 90 tests): 88 passed, 2 failed. The 2 are the table waits in issue-table 86 and 289, left unchanged pending Cheese's choice. Earlier serial runs of this branch were 79 passed and 11 failed. The E2E gate is not waived.

  • Retarget to upstream v0.6.5 (Cheese's request through Opus, comment 01a160f1-215e-790c-97fd-4a1325617b6c; same PR, same branch, merge commits only, no rebase, no force-push). The release is 14 fix commits over v0.6.4. UPSTREAM_VERSION is 0.6.5; the PR title says v0.6.5. Ten files conflicted, none in a fork-owned migration (v0.6.5 adds no migration): router.go and handler.go (upstream's gitca extra CA certs beside the fork's server build stamp, both kept); issue_wakeup_system.go (upstream clears last_error once a pass holds its locks; placed before the fork's pause logic so every commit below it counts); codex.go (upstream's codexResultUsageMap fallback for usage and the default model; a run behind a credential boundary still reads no session logs, via codexUsageMapWithoutScan); hooks.ts, status-options.ts, status-picker.tsx, issue-actions-menu-items.tsx (upstream's MUL-8121, Default statuses for an issue outside a project, added on top of the fork's resolved/retry model: useProjectWithWorkflow now also returns defaultWorkflow, and useIssueStatusOptions takes the current status); status-picker.test.tsx (both suites); workflow-editor-page.test.tsx (add/add: the fork's file kept, upstream's added as workflow-editor-page.upstream.test.tsx; both pass). issue-property-pills.tsx merged to a duplicate projectId prop and was fixed. Also fixed: two go-lint findings in upstream v0.6.5 lines (argv_limit_test.go, issue_wakeup_condition.go); CI on 3c9788aed failed go-lint on them (kept on record).

  • EQUIVALENT_SIGNAL (Cheese, comment 01a160f3-63e9-7075-9319-2e6b173e4599) applied to the two legacy Loaded N of M waits only. Red was the observed failure getByText(/Loaded \d+ of 1001/) and getByText('Loaded 60 of 60') not found, because the table renders no progress text (searched the page: no such string). issue-table:86 now reads the loaded rows from the rows responses and checks that the number is above 0 and below the exact /groups total of 1001, and that the backlog group's first row is rendered. The cursor test checks that head plus tail return all 60 rows with no cursor left and that the last row is rendered, before and after the refresh. Loaded/total, grouping/hierarchy, realtime-cursor and CHE-479 assertions kept. One more stale premise in the cursor test, found when it first ran past the wait: it moved an issue by changing position, but manual order exists only under status grouping, so in the ungrouped table the sort is by created date and the update crossed no boundary (freshCursor equalled staleCursor). It now sorts by Updated date, touches the issue in the last tail row, and takes the refreshed head to be the head page that has it first. Green: issue-table 4 of 4 and the cursor test 4 of 4.

  • Gates on 3c9788aed (the tree before the two lint fixes), observed here. Go, clean worktree, private Postgres 17: 85 packages pass, pkg/agent passes via scripts/test-go.sh --only agent (2 of 2). Migrations: main applies 725; upgrading applies exactly 671 and 672 (ledger 727, 670_run_gate_event kept); a rerun applies 0; a fresh schema applies 727; the pg_dump --schema-only diff is 0 lines. pnpm typecheck 10 of 10. pnpm test: docs 19, ui 8, ui-lab 46, core 2436, web 341, desktop 704 pass; views 6742 of 6744, and the 2 that failed (workflow-editor-page.upstream.test.tsx and table-view-editing.test.tsx) pass alone 2 of 2, the same load-sensitive pattern as before. Serial E2E, whole suite, one worker, _test database, cold dev server: 91 passed, 0 failed (previous head: 88 passed, 2 failed). The mobile test run printed stack traces from a test that logs on purpose; I did not read its summary line and did not rerun it.

  • go-lint findings from CI job 119570059127 (on 34fcc85de), already corrected in b84cebd9e: gocritic/sloppyReassign at issue_wakeup_condition.go:353 (now if err := vcsRows.Err(); err != nil) and errorlint at argv_limit_test.go:34 (the pass-through check now uses errors.Is and also compares the error text, so it still fails if the error is rewritten). Red, observed locally with the repo's .golangci.yml on 3c9788aed: both findings reported. Green on b84cebd9e: golangci-lint run ./... --new-from-rev=origin/main reports 0 issues (CI gates the same way), the argv-limit tests pass, and go vet ./internal/service passes. No lint rule, directive or exclusion was changed. The 77 findings that a whole-package run reports are older code outside this change.

  • Correction round on b84cebd9e (Sol's resume; the one replacement Cheese authorized, comment 01a16196-…), head a8a04f350, test files and test config only. (a) Table progress signal, issue-table 86: Board and Table both call /api/issues/table/rows, so a signal counted over the whole page was satisfied by Board rows alone. Red, observed: with Table rows withheld by route.abort(), the old collector still held rows (Expected: 0 Received: 50). Fix: collectTableRows calls reset() after the quiet period, then the test switches to Table and counts only responses that arrive after the switch. New negative-control test (60 issues, Table rows aborted) expects 0 loaded rows and 0 first titles; green. The cursor, grouping, hierarchy and CHE-479 assertions are unchanged. (b) Two views-suite failures, diagnosed from the failing run, not from passes: workflow-editor-page.upstream "resolves a default next over the unsaved steps, as the save will" (5260 ms) and table-view-editing "confirms a status change that would start an agent run instead of applying it" (5145 ms) both fail with Test timed out in 5000ms under 32 busy loops (7 timeouts in total in that run, including create-project, create-issue and two autopilot-dialog tests). Alone, they take 2.8-4.3 s and 1.9-3.0 s. Cause: CPU contention against vitest's 5000 ms default. Fix: testTimeout: 15_000 in packages/views/vitest.config.ts. Same stress after the fix: 525 files, 6744 tests pass. This does not explain the historical unnamed pkg/agent failure; that failure stays UNDIAGNOSED. (c) Serial E2E on a8a04f350 (--workers=1, _test database, one dev server, 92 tests): 92 passed, 0 failed. One earlier shard-1 attempt hit my 580 s limit while another process overlapped and showed chat-fork.spec.ts:136 failing without a summary; the clean rerun passed 47 of 47 in 10.5 min. (d) close-target on a8a04f350: the "Checkout trusted workflow checker" step fails with fatal: 'scripts/pr-close-target-check.mjs' is not a directory; to treat it as a directory anyway, rerun with --skip-checks (git sparse-checkout set <file> in cone mode). The workflow runs from main through pull_request_target, so this diff cannot change it; the same check failed on 5 other PR branches the same day, and a fix branch (agent/x99-claude-sonnet/che1676-close-target-sparse) exists. main now also holds 6e3b42150 (fix(pr-workflow): reject mismatched close targets (CHE-1932) [squash] #344), which this branch has not merged; merge it with a merge commit before marking ready (stack-freshness rule).

  • Freshness pass (Sol's ask on a8a04f350; new correction work, not a retry). main moved to 0134372945846952a0df60753bf9d3eecf70dee0 with 3 commits (fix(pr-workflow): reject mismatched close targets (CHE-1932) [squash] #344, fix(ci): use non-cone sparse checkout for close-target checker (CHE-1676) [squash] #352, fix(ci): correct sparse checkout for the PR close-target checker [squash] #351), all in the PR close-target checker: .github/workflows/pr-close-target-check.yml, scripts/pr-close-target-check.mjs, its test and 3 fixtures (6 files, 504 added lines, no overlap with this diff, no migration change). Merged with a merge commit 9195a5306, no conflict, no rebase, no force-push. Local gates on the merge: node --test scripts/pr-close-target-check.test.mjs 20 of 20 pass; TestMigrationNumericPrefixesAreUnique passes. close-target passes on the new head. Serial E2E (--workers=1, private Postgres 17, _test database, 3 shards, 92 tests): on 9195a5306 shard 1 gave 44 passed and 3 failed (auth-callback-locale 19 for both accounts, comment-steering 10) at host load average 17-20; those 3 passed alone (3 of 3). A shard-1 rerun then failed chat-fork.spec.ts:136 (the page surface): after the reload the page showed the source session ("Forked into" link, no "Forked from" divider), so the reload started before the URL named the fork. It failed again alone at load 30 (6 tests: 5 passed, 1 failed, 4.3 min) and passed at lower load (6 of 6 in 1.5 min). The spec is unchanged from main. Red, observed: locator.click: Test timeout of 120000ms exceeded, waiting for getByRole('link', { name: 'Storage engine choice', exact: true }). Fix 77e0b0cc5: wait for the URL to carry the fork session before the reload. Green: 6 of 6 three times, twice with 20 busy loops running. I did not prove the cause deterministically; the evidence is the observed failure state plus passes after the change. Final serial result: shard 1 on 77e0b0cc5 47 passed (10.3 min); shards 2 and 3 on 9195a5306 19 passed and 26 passed (the only change since is the chat-fork spec in shard 1). Total 92 passed, 0 failed, in separate runs; the 3 load failures are disclosed above and are not a waiver. Raw outputs: /tmp/che1900-s1-first.txt, /tmp/che1900-s1.txt, /tmp/che1900-s2.txt, /tmp/che1900-s3.txt on X99. Cleanup: servers on ports 18291 and 13211, the private Postgres container, .env.worktree, test-results and apps/web/.next removed; df -B1 on / before 411156930560 used, after 421455753216 used and on /mnt/ssd2 before 354308087808, after 373623226368 (shared hosts with other runners; the increase is not attributable to this task).

  • Second freshness pass (Sol's ask on 77e0b0cc5; new correction work, not a retry). main moved by 1 commit, 462e0ac93 (feat(cli): add agent skills runtime-set command (CHE-1884) [squash] #349, feat(cli): add agent skills runtime-set command): server/cmd/multica/cmd_agent_runtime_skills.go and its test, 234 added lines, no overlap with this diff. Merge commit 2b765cd05, no conflict, no rebase, no force-push. Local gates: go build ./... and go vet ./cmd/multica/ clean. go test ./cmd/multica/ -count=1 -v in a clean worktree outside the task directory (MULTICA_* env unset): ok, 582 PASS lines, 0 FAIL, including TestAgentSkillsRuntimeSetSendsDisable, TestAgentSkillsRuntimeSetRequiresIdentityAndEnabled and TestAgentSkillsRuntimeSetSurfacesServerRejection. Run inside the task directory the same package shows many unrelated failures (for example TestAgentSkillsAddCallsAdditiveEndpoint): the CLI detects the daemon task marker and requires a task-scoped mat_ token. That is an environment effect, not a code failure; it is why the run uses a clean worktree. E2E was not rerun: the merge delta is Go CLI code that no Playwright spec reaches. The E2E limits stay as recorded: the 92 passed come from separate runs on two heads (9195a5306, 77e0b0cc5), 3 load failures were seen and passed alone, and chat-fork:136 was fixed in 77e0b0cc5 without a deterministic cause proof. Artifact gap: for the earlier E2E runs only a grep-filtered summary was kept (failure titles and the pass/fail line); the Playwright traces and test-results were removed in cleanup and cannot be produced after the fact. Full line-reporter text exists only for the four chat-fork runs, and the full CLI test log is kept. They are attached to the Multica comment as one archive.

  • Pinned exact-head whole-suite E2E (Sol's ask on 2b765cd05; Luna's CHANGES REQUESTED named the missing exact-head evidence). main moved 3 commits (feat(cli): send vote receipts with --vote-issue (CHE-1936) [squash] #356, ci(runners): replace Blacksmith routing (CHE-1922) [squash] #333, fix(ci): stop close-target sparse checkout leaking into reused X99 workspaces (CHE-1961) [squash] #355: CLI vote receipts, runner routing, close-target checkout) to 571008ce5d17f5c422d02ad1ee4c1e46b79bbba4; merge commit 0d0ac595c, no conflict. Local checks on the merge (clean worktree, MULTICA_* unset): go build ./..., go vet ./cmd/multica/, go test ./cmd/multica/ -count=1 -v 586 PASS and 0 FAIL, node --test scripts/pr-close-target-check.test.mjs 22 of 22, .github/tests/test_runner_routing.py 1 of 1. One invocation, no file list: pnpm exec playwright test --workers=1 --retries=0 --trace=retain-on-failure --reporter=list,json, cold next dev (.next removed), task-owned backend, private Postgres 17 multica_e2e_test, head unchanged and main unchanged during the run. 15:54 ICT to 16:33 ICT (38.2 min). Result: exit status 1, 84 passed, 8 failed, 0 flaky, 0 skipped (92 tests). Failed: agent-mcp:164 (page.waitForFunction 30 s), auth-callback-locale:19 x2 (a Japanese button not visible in 5 s; a count of 0 where more than 0 was expected), auth:22 (waitForURL 10 s, navigated to the workspace issues page, not /login), local-search:112 (predicate false after 5 s), navigation:32 (link General not found, 5 s), quick-actions:13 (button Draft the launch brief not found, 30 s), settings:41 (text Connected not found, 10 s). Each failure has a trace. Host load average was 30 at the start and 8.5 at the end (other runners; no stress added). The same 7 spec files passed in the earlier shard runs on 9195a5306 and 77e0b0cc5 (auth-callback-locale and comment-steering failed there under load and passed alone), and the merge deltas touch none of them, but I have not isolated the cause of these 8 and did not retry or edit any test. The Multica comment carries che1900-e2e-core.zip (sha256 305686ea1c26f90123220b9f707aab11da2d705fdcf1c150fa50aec6044b4a24) and the 8 failure traces as a tar (sha256 7a240ad7ca1c1e333d7038d669bafee834e1dc0102b408bbe340734a8862c5cd) split into 10 parts of at most 950000 bytes, because the upload gateway rejects attachments above about 1 MB; the full set is kept on X99 at /mnt/ssd2/che1900-evidence/. They hold: manifest, full output, results.json, 8 traces, logs, CLI test log and a SHA256SUMS file.

The other 15 OCR medium and 10 low findings are not addressed here. They are P2 or non-blocking in Luna's verdict.

Gates on X99, tree c3be0638e418570003b0fc7c512b6988dbcaaef6 for Go and E2E (the only change in 29ea639e8 is cursor_supplement.go and its test, rerun as listed above) (TypeScript gates ran on 3a13e0855, and no TypeScript changed after it) (X99 load average was about 36 during the runs):

  • pnpm typecheck: 10 of 10 tasks pass (run before the renumber commit, which changes no TypeScript).
  • pnpm lint: 0 errors, warnings only.
  • pnpm test: docs 19, ui 8, ui-lab 46, core 2427, web 340, desktop 700 tests pass. Views 6686 of 6686 pass with --maxWorkers=4. The default-parallel runs failed 3 different timing-sensitive tests each time, and each passes alone.
  • Mobile verify (turbo typecheck lint test --filter=@multica/mobile): 4 of 4 tasks pass, 253 tests.
  • Go, private Postgres 17 (pgvector/pgvector:pg17) with an unset MULTICA_* env, run from a clean worktree outside the task directory, on c3be0638e: go test on all packages other than pkg/agent passes (83 packages) except 3 TestPrepareCursorMcpConfig* tests in internal/daemon/execenv. Cause, found this round: cursorProjectRoot walks up from TMPDIR to the first .git, and the X99 host has a stray /tmp/.git and /tmp/.cursor/mcp.json. With TMPDIR set to a directory outside /tmp, internal/daemon/execenv passes. So the 3 failures are a host artifact, not a code defect, and they fail the same way on origin/main. pkg/agent passes (scripts/test-go.sh --only agent, with the new cursor tests). internal/handler passes.
  • Migrations: upgrade from main (723 versions) to 725, a no-op rerun applies 0, a fresh schema has 725, and the pg_dump --schema-only diff is 0 lines.
  • E2E (Playwright), first measurement on c3be0638e (superseded by the repair bullets above, kept as history). Measured on X99 with a local backend, a Next dev server and a private Postgres, all specs except plugin-surface-document.spec.ts, 3 shards:
    • Correction to the earlier count: chat-fork and chat-continuation read DATABASE_URL and refuse to run unless the database name ends in _test (Fork E2E requires an isolated _test database). My first database was not named that way, so those 5 failures were my setup, not baseline and not code. With a _test database they pass when run serially on head (4 chat-fork and 1 chat-continuation), so the fork continue/fork flows pass end to end.
    • plugin-surface-document.spec.ts does not load on origin/main or on v0.6.4: it imports buildSurfaceDocument, which packages/views/plugins/surface-document.ts does not export.
    • Full run on head with a _test database, 5 workers: 31 failed, 55 passed (86 tests); a second run with 4 workers on the first database gave 33 failed, 53 passed. The failing set differs between runs (navigation, issue-table, settings, local-search fail in one run and not in the other), so the parallel run is load-dominated.
    • Serial rerun (--workers=1) of 18 of the failed tests: 8 pass, 10 fail again: agent-mcp (2), auth-callback-locale (2), auth redirect (waitForURL("**/login") does not match /login?next=...), chat-attachments (/api/upload-file answers 404), html-preview-fullscreen.spec.ts:50, issue-content-disclosure (3). Not rerun serially (12 tests, the run timed out): the rest of the failed set.
    • Of the 10: all except html-preview-fullscreen.spec.ts:50 also failed on origin/main in the earlier comparison, and html-preview-fullscreen.spec.ts:50 is new in v0.6.4 and fails the same way on the pristine v0.6.4 tag. I found no failure that the merge introduced, and no defect inside this sync's scope that I could repair. I did not find the cause of the chat-attachments 404.
    • Baseline failures are not a waiver. The gate stays red until the failing specs are fixed or Cheese waives it.
  • Cursor real smoke test TestCursorRealSupplementReachesTheTurn (agentintegration tag): NOT-RUN. Blockers: cursor-agent is not on the X99 PATH (command -v cursor-agent, exit 1), the test needs an account, and the repository rule allows real-agent smoke tests only when explicitly authorized. The CI job cursor-background-lifecycle passes and is not this test.
  • Not run: Windows and macOS tests, any device, simulator or Maestro run, a manual browser check of the chat window.
  • The shared Postgres on X99 reached too many clients during the first Go run, so the results above come from a private container.

CI history: on the first head 785f8e70b plus the renumber commit 6dbd9a2ed, go-lint failed on 2 findings in upstream code (ST1023 in rest_snapshot_test.go, unused errMuseTurnAborted in server/pkg/agent/muse.go), script-checks failed on the PATH shadowing above, and backend failed only because both failed. Commit 9f3058720 fixes the lint findings, 4bc0693e5 fixes the script, 3a13e0855 fixes the floating-chat lifetime test race. On 3a13e0855 every executed job passed and GitHub skipped daemon-artifact and frontend-quality. On c3be0638e the first CI run (39443924699) failed backend-tests once in server/internal/governance/receipt: TestObserve_EditAfterSettledCreateIsAdmittedAgainstRealBudgetService shed the create observation as budget_exceeded (receipt_budget_test.go:169), and backend failed with it. That is the known flaky real-database budget test that PR #295 (CHE-1578) fixes; no commit here touches that package. The failed job was rerun, and the same run 39443924699 now reads success with all executed jobs passing. The historical failure stays on record and is not a waiver. On 29ea639e8 and on 47e09d604 all executed jobs pass; GitHub skips daemon-artifact and frontend-quality. On d66f68c96, backend-tests failed TestMigrationNumericPrefixesAreUnique (the CI merge ref carried main's new 669 beside mine) and image-budget failed because the check compared against an older base that lacked main's new design images; against main 3c84240ed it passes locally. The migration failure is fixed by the merge and renumber in a8e5d6c25. The image-budget cause was in the job, not in the images: it checked out GitHub's merge ref (current main plus the PR head) but compared it with the event payload's base.sha, which is older whenever main moved after the event. On job 119018119494 (head eca11820b) that charged the PR for six design PNGs that only main had gained, and an exemption line would have been the wrong fix. Commit 5a40d604e adds --base merge-parent to scripts/check-image-budget.mjs (the first parent of the merge commit is the base the merge was made with) and uses it in ci.yml with fetch-depth: 2. Red: the new test --base merge-parent compares against the base the merge was made with failed (git diff merge-parent is not a ref). The same scenario built from the real commits (70c7a3383 merged with eca11820b) gives six oversized PNGs against 1de88837 and a pass against the merge parent. Green: 47 of 47 tests in ci-scope and check-image-budget; image-budget passes in CI on 5a40d604e. No exemption was added. The PR's own oversized images are still caught, and a non-merge checkout is refused. On a8e5d6c25 every executed job passes, including backend-tests and image-budget; GitHub skips daemon-artifact and frontend-quality.

Cleanup, twice (first round and this round): removed the verify worktrees, the private Postgres containers, the task databases, the local next dev and backend processes I started, and test artifacts. df -B1 before the first round / after this round: /mnt/ssd2 used 322519064576 / 329330356224 (+7227292648 bytes), / used 363100962816 / 365519876096 (+2418913280 bytes); other runners write to these disks too, so the numbers are not all mine. The growth is mostly the shared Go build cache and pnpm store; I did not prune them. node_modules (2.5G) stays in the task workdir for the reviewers and goes with the workspace.

Remaining scope: after Cheese merges PR #325, dev team owns verification and publication of upstream tags v0.6.3, v0.6.4 and v0.6.5. No tag publication is authorized in this readiness turn.
Intended closer: dev team after verifying the remaining post-merge tag obligation. PR readiness is not a merge or full issue completion.
Refs CHE-1900

forrestchang and others added 30 commits October 18, 2026 16:23
…ultica-ai#9322)

* MUL-8070: feat(cursor): support live-run steering via Cursor hooks

Cursor's headless --print mode has no mid-turn input channel, but its
postToolUse/postToolUseFailure hooks can return additional_context that
reaches the model in the same turn. A steerable Cursor run now writes a
managed .cursor/hooks.json whose command is the multica binary in a hidden
relay mode; the relay forwards the hook event to a token-protected loopback
endpoint owned by the run, which hands back pending guidance tagged with a
per-run nonce. The prompt declares that tag so the model treats the context
as genuine user guidance instead of a prompt injection.

Steering is offered only for daemon-managed workdirs on Unix with cursor-agent
2026.10.14+, and the server allowlists cursor for capability negotiation,
supplement creation and composer binding.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* MUL-8070: fix(cursor): confirm steering only on Cursor's receipt and gate on installable hooks

- Count hook-delivered guidance as delivered only when the tool call's
  completed stream event echoes it in hookAdditionalContexts. Guidance
  Cursor dropped for the same call is retried at the next tool boundary;
  guidance without a receipt fails when the turn ends.
- Offer steering only when the workdir can carry the managed hooks.json,
  and fail supplements immediately if installation still fails after
  negotiation.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
…8071) (multica-ai#9323)

* feat(issues): richer issue mention hover card (MUL-8071)

Name the status and priority in words, add a property list (assignee with
live agent activity, project, parent, labels, due date, sub-issues, lead
PR) and a creator/updated footer, so even a freshly filed issue gets a card
that says more than its chip.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(views): shift+click an issue mention to open it in the side peek

Mount an IssuePeekHost on the issue page and the inbox detail pane so
mentions there can peek, alongside the existing list surfaces.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix(views): only show the assignee's own runs as hover card activity

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
…bles (multica-ai#9321)

* MUL-8067: feat(views): table model, source markdown and column width storage

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* MUL-8067: feat(views): readable, resizable and expandable markdown tables

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* MUL-8067: refactor(views): table frame with overlay resize handles, shared by both renderers

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* MUL-8067: feat(views): resize, expand and copy tables in the editor

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* MUL-8067: fix(views): copy a table's Markdown from the content as written

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* MUL-8067: fix(views): table toolbar in its own row; keep handle height after a click

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* MUL-8067: fix(views): keep mentions in the editor table's filter, sort and CSV text

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
…by default (multica-ai#9325)

* MUL-8074: feat(issues): fold resolved threads in the comment outline by default

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

* test(issues): expand the folded resolved thread before jumping to its reply

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
…ultica-ai#9121)

* feat(editor): preserve loaded HTML previews when enlarging

* fix(editor): remeasure HTML previews after enlargement (MUL-7919)

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: yushen <ldnvnbl@gmail.com>
Co-authored-by: multica-agent <github@multica.ai>
…ultica-ai#9119)

* feat(issues): add a rename entry to the issue actions menu

Renaming an issue was only reachable by clicking the title on the detail
page, and the list / board / gantt / sub-issue views had no entry point at
all. Add a "Rename issue" item to the issue actions menu (the detail page
"..." button and the right-click context menus) that opens a small dialog
and saves through the shared useUpdateIssue mutation, so a title change
refreshes every view that shows it.

The new Chinese copy follows the i18n glossary (issue -> task).

Co-authored-by: multica-agent <github@multica.ai>

* fix(issues): guard rename dialog against concurrent edits

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: yushen <ldnvnbl@gmail.com>
* fix(inbox): keep unread-filtered selection open

Opening an unread notification marks it read, which removed the row from the active unread filter and cleared the selection before the detail could render. Pin the open row across the unread dimension only, releasing it when the selection moves, so the notification actually opens without changing the other filter dimensions.

* fix(inbox): retain archived unread selection through refetch (MUL-8073)

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Jean Wood <JenniferWood@users.noreply.github.com>
Co-authored-by: Bohan-J <bhjiang@outlook.com>
Co-authored-by: multica-agent <github@multica.ai>
…tica-ai#9187)

* fix(desktop): require directory-capable Linux file chooser portals

Co-authored-by: multica-agent <github@multica.ai>

* docs(desktop): record why the Linux portal version switch is set

Name the Electron patch that disables Chromium's own portal version gate,
the StringToUint out-param overwrite that zeroes the minimum, and the
Electron versions still affected, so a future upgrade does not drop the
switch on the assumption that upstream fixed it.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: ZeroIce <zeroicework@163.com>
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Bohan <bohan@devv.ai>
…ai#9309)

* feat(agent): run Meta Muse Code as an MSP runtime

Muse Code speaks MSP (Muse Session Protocol) over `muse serve`: JSON-RPC
2.0 on stdio, like ACP, but with its own method vocabulary. A session opens
with session/start or session/resume, work is submitted with turn/start, and
assistant text, reasoning and tool calls arrive as transcript items.

The adapter maps those items onto the Backend contract, reads the terminal
turn outcome, and reports a notFound resume as ResumeRejected so the daemon
retries on a fresh session instead of losing continuity silently.

Approval is a session property, not a launch flag: `muse serve` has no
--yolo, and the CLI documents --yolo as TUI-only. A fresh session is created
with approvalMode=allowAll and a resumed session re-asserts it with
session/setApprovalMode, which is what lets a claimed task run without
waiting for a human. The container and per-task worktree remain the
isolation boundary, as they are for every other backend.

commandId must be UUIDv7 — Muse derives a turn id from it and rejects
anything else — so the adapter mints time-ordered ids rather than v4.

The model catalog comes from the session-free model/list query and the pick
is applied through session/start modelId.

* feat(runtime-profiles): allow muse as a protocol family

A custom runtime profile may only be based on a protocol family the daemon
supports, and the database enforces the same list. Widening both keeps the
create flow, the server validation and the CHECK constraint in lockstep with
agent.SupportedTypes.

* fix(muse): complete runtime integration and lifecycle handling (MUL-8057)

Co-authored-by: multica-agent <github@multica.ai>

* docs(muse): document runtime setup and support (MUL-8057)

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: yushen <ldnvnbl@gmail.com>
Co-authored-by: multica-agent <github@multica.ai>
…tica-ai#418 (MUL-8013) (multica-ai#9244)

* fix(docs): stop default-locale pages failing hydration with React multica-ai#418

SSG prerenders each page under its canonical `[lang]` route, so client
components see `/en/agents` as the pathname at build time, while the
browser URL for the hidden default locale is `/agents` (the i18n
middleware rewrites it). Fumadocs resolves the page tree path with
`usePathname()`, and the tree stores default-locale URLs without the
prefix — so the prerendered HTML rendered tree-path fallbacks ("On this
page" in the TOC popover trigger) while hydration resolved the real
page name ("Agents"). The text mismatch threw React error multica-ai#418 on every
EN page load and React regenerated the whole tree on the client,
dropping SSR-injected elements such as the next/font preload links.
Non-default locales matched on both sides and were unaffected.

Wrap the docs layout in fumadocs' `FrameworkProvider` with a
`usePathname` that strips the hidden default-locale prefix, so prerender
and hydration see the same locale-less path the page tree uses. The
prerendered HTML now carries the real page names as well. Non-default
locales keep their `/zh`-style prefix and behave exactly as before.

Fixes multica-ai#9243

* fix(docs): preserve static image imports in framework provider

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Bohan-J <bhjiang@outlook.com>
Co-authored-by: multica-agent <github@multica.ai>
…e (MUL-7987) (multica-ai#9207)

* perf(ui): coalesce useScrollFade observers and skip identical setState

Every fade host (sidebar groups, the tab bar, dialogs) observed scroll,
resize AND childList:subtree mutations, and each observer hit
synchronously read layout and dispatched a setState even when the
computed fade value was unchanged. In busy workspaces with frequent
realtime re-renders, a route remount fired hundreds of identical
dispatchSetState calls per second across the shell's fade hosts — every
one going through React's lane scheduler — stretching tab switches and
detail-page opens out to several seconds. A production CPU profile of
the stall showed roughly half of all samples inside that
dispatchSetState path.

Coalesce the three sources through one requestAnimationFrame per host
and only dispatch when the computed value actually changes. The first
frame after mount still applies the initial mask, so the visual
behavior is unchanged.

Also gives packages/ui a minimal vitest setup (jsdom + @testing-library,
matching packages/core and packages/views) with coverage for the mask
values and a regression test asserting that a mutation/scroll storm
with unchanged layout reaches React zero times.

* fix(ui): keep useScrollFade scroll handling synchronous

Coalescing scroll through rAF broke chat's auto-scroll handshake: its
stick-to-bottom latch attributes wheel displacement to the scroll event
that confirms it within the same task, and deferring the fade update
(and its re-render) off that task left followOutput stale in the
autoscroll suite.

Scroll stays synchronous — user scrolling is too rare to need
coalescing, and the storms this hook guards against come from resizes
and subtree mutations, which remain rAF-coalesced. Full
packages/views suite (5981 tests) passes with this split.

* fix(ui): resolve useScrollFade lifecycle defects from review

Two defects from the PR review:

1. The cleanup removed the wrong closure — the listener was registered
   as `run` but unregistered as `schedule`, so StrictMode remounts and
   axis changes left the old measurement attached (double reads, stale
   axis). Remove `run` symmetrically.

2. A synchronous scroll executed `raf = 0` while a queued frame was
   still alive: the frame fired anyway (resize→scroll→mutation yielded
   two pending frames instead of one) and survived unmount cleanup,
   which saw the zeroed handle. `run` now cancels the queued frame up
   front — a synchronous measurement absorbs it.

Also refreshes the header comment that still claimed all three sources
go through rAF, and adds regression tests for the three review
scenarios (absorb queued frame, zero frames after unmount, listener
symmetry across cleanup and axis rebuild).

* chore(ui): drop unused no-var eslint directive in test setup

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Multica Eve <273080382+multica-eve@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
…just its label (multica-ai#8832)

* MUL-7019 fix(wecom): fetch the picture a sender quoted, not just its label

A quoted picture reached the agent as the four characters [Image]. The
common shape of this in a room is somebody quoting a screenshot of an error
and typing 这个怎么处理 underneath: after multica-ai#7980 the agent gets the words and
a label where the error was, and answers the label.

The callback carries the quoted attachment exactly the way it carries one
the sender just made - a pre-signed url and its aeskey, under quote.image or
inside quote.mixed.msg_item - so fetching it is the whole of the fix.

Fetching alone would not be enough. The attachment list is a separate block
of text from the body, so with one attachment a reader infers which marker
names which entry and with two it cannot, and a quote is exactly the case
where a message carries somebody else's picture beside its own. A quoted
attachment therefore renders a marker with room in it, [Image: unavailable],
and travels with the marker and its occurrence number so the binder can
rewrite that one occurrence to name the attachment.

It is named rather than embedded. The picture belongs to a message somebody
else sent; replacing the marker with an inline image would state this sender
attached it. channel.MediaRef.InlineIDOnly is what an adapter asks for that
with, and the engine keeps the label ahead of the colon so the marker the
adapter renders and the marker that replaces it stay one shape.

The sender's own [Image] is untouched. It is already unambiguous, being the
attachment on the message the reader is looking at, and changing it would
change what every wecom message body already stored says.

Two assertions from multica-ai#7980 change with it: a quoted attachment now renders
[Image: unavailable] rather than [Image]. That is the behaviour this change
is for, and a new case pins the sender's own marker beside each of them.

* wecom: number a quoted attachment's marker from the rendered quote, and keep markers through the 500-rune cut

A quoted attachment was bound to its marker by counting the attachments
before it. The engine finds the marker by searching the stored body for the
Nth occurrence of the marker string, so a quoted sentence that spells
"[Image: unavailable]" shifted the real marker's number and the binder
rewrote the sentence instead of the picture. Separately, the 500-rune cut
could remove a marker whose attachment was still fetched and bound, leaving
the quote with no id for the picture next to a newly sent one.

Lay the quote out as runs that carry their attachment, and let one function
decide the cut and stamp each surviving attachment with the occurrence of its
marker in the text as laid out. Words are cut at 500 runes as before; a marker
is never cut or dropped, so the block can exceed the budget by the markers it
carries.

* wecom: name the quoted-marker case that moves only one kind's index

* wecom: build the quoted-marker DB test's rows through the shared testutil fixture
…d task (multica-ai#8506)

* fix(daemon): stop sending an English sentence for a cancelled task

A server-cancelled task carried the comment "task cancelled by server".
reportTaskResult forwards that comment as the task's `error`, which the
web UI renders verbatim in the "Technical details" fold and in the chat
"Show details" block — so a French, Japanese, Korean or Chinese reader
got an English sentence under a translated heading.

Nothing is lost by dropping it. The "cancelled" status already yields
failure_reason "cancelled" a few lines later, independently of the
comment, and that code is localized in all five shipped locales
(FAILURE_REASON_I18N_KEYS; "Annulée par le système" in fr). The sentence
said the same thing, only worse, and only in English.

The comment also stops quoting the removed string so a future grep for
it does not lead back to a line that no longer produces it.

No test: this removes a literal on a defensive branch that handleTask's
cancelledByPoll path normally discards, and the surviving behaviour —
failure_reason "cancelled" rendering through failureReasonLabel — is
already covered on the frontend. Verified by build, vet, gofmt and
./internal/daemon, whose two failures reproduce unchanged on main.

* fix(autopilot): use the shared failure reason helper when a task fails or is cancelled

taskFailureReasonForAutopilotRun trims the raw error and falls back to
the localized failure_reason, so an empty task.Error no longer writes
blank failure_reason onto the autopilot run.

* fix(daemon): correct the misleading comment on the cancelled branch

What reaches the server here is the daemon's own context cancellation
(server cancel or daemon shutdown); a user cancel or reassignment is
discarded earlier by handleTask's cancelledByPoll branch.

---------

Co-authored-by: Atelier Nuvio <atelier@foxace.local>
Co-authored-by: Hermes Agent <hermes@fox.lan>
…e delivery stage (multica-ai#7951)

* fix(wecom): re-read the installation's permission at every stage of a file delivery

A revocation is a person withdrawing the connection, and a file delivery is
minutes of work between the top-of-loop gate and the send: the object read,
the chunked upload, the finish. A withdrawal landing anywhere in there
reached a worker with nothing left to consult, so the file went into a chat
whose owner had removed the bot.

The permission is now re-read from the database after the object read,
before EVERY chunk goes to the socket (uploadMedia's new BeforeChunk hook,
asked inside the goroutine that writes so nothing waits between asking and
writing), and again after the upload before the media_id is sent. Three
outcomes rather than a bool: a row that says revoked is final, a read that
failed is this attempt only.

Tests: two unit regressions (a withdrawal during the slot wait stops the
next chunk; a withdrawal before a retry stops the second offer) and a
database-backed one that flips the channel_installation row to revoked
while three chunks are held on the wire, asserting no finish frame and no
send. With the three re-checks stripped, the file is delivered and the
database test fails.

* test(wecom): drop the chunk-retry case whose harness main removed

multica-ai#8318 deleted mediaConn.dropAcks and the tests that used it, because each
stood still for a whole ackTimeout. This branch still used it, so it built
against its own base and not against main - a break git reports no conflict
for and CI never saw, because these checks ran on 09-02 and multica-ai#8318 landed on
09-14.

The case is removed rather than restored, and the gap is recorded where the
test was: re-adding the field re-adds the test time that PR was cutting,
and closing it properly needs an ackTimeout seam that did not belong in
this PR uninvited.

* test(wecom): pass the stream store NewOutbound gained in multica-ai#8355

* fix(wecom): ask the installation's permission at the write, not before the wait

The previous round asked at the start of each stage. Three waits sit between
those questions and the socket, and a revocation landing in any of them still
got its frame out: the socket writer, the chat's turn, and a throttled push's
retry backoff. The failure notice had no check at all.

The permission check is now a write guard carried in the context
(withWriteGuard) and run by wsSender.writeCtx once the writer is held, so the
chunks, upload init and finish, the file message and its retry, and every
notice go through the same question at the last moment. It replaces the
BeforeChunk hook and the three stage checks in sendAttachment. A revoked or
unreadable permission refuses the frame (wrapping errNotAttempted) and the
worker stays silent about it.

request used to wait for the writer on context.Background(), so a caller that
had given up still had its frame written. writeCtx now ends that wait with the
caller's context and writes nothing for a context that is already over; the
ping, handshake and pong keep calling write(), which waits as before. The
socket write deadline is still the connection's own.

Tests: the three orderings from the review (writer, chat lock, throttle
backoff) at sender level and through the whole attachment worker, the notice
at every tellUser call site with a permission-intact control, an unreadable
permission, and the revoke against the real row using testutil fixtures.

* test(wecom): bound the permission read, and count notices by frame so the test holds on main

The permission guard runs while the socket's writer is held, so its read gets
a test that it ends by itself. The notice assertions compared against the
notice constants, which main has since moved into per-language copy; they now
count markdown pushes, which in that rig are exactly the notices.
* test: cover quick actions without an agent

* feat: allow quick actions without a target

* test(quick-actions): cover clearing an existing agent target

* fix(quick-actions): keep existing locale copy

* test: consolidate quick-action regressions in existing test file

* chore(migrations): renumber quick action optional target migration to 584

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Multica Eve <273080382+multica-eve@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
* test(inbox): add failing coverage for idle issues filter

* test(inbox): pass queryOptions through the page's query mock

* feat(inbox): filter to idle issues with no active agent

* test(inbox): cover archive isolation and idle count for the idle filter

* fix(inbox): rename idle filter to "No agents working" and use BotOff icon

Co-authored-by: multica-agent <github@multica.ai>

* fix(inbox): pin the open row per filter dimension and release a pin when its filter turns off

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Multica Eve <273080382+multica-eve@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
)

Co-authored-by: multica-agent <github@multica.ai>
* docs: explain workflows and quick actions (MUL-8086)

Co-authored-by: multica-agent <github@multica.ai>

* docs: clarify workflow defaults, board handoffs, and quick action limits (MUL-8086)

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: multica-agent <github@multica.ai>
…tica-ai#9067)

* fix(web): keep workspace document titles authoritative

* fix(web): keep latest workspace title across navigation
…ultica-ai#9338)

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
…i#8997)

* fix(usage): price Antigravity Gemini Flash model aliases

* fix(usage): correct Gemini aliases and complete token prices

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: yushen <ldnvnbl@gmail.com>
Co-authored-by: multica-agent <github@multica.ai>
IA386 and others added 18 commits October 21, 2026 14:25
…ng repos (multica-ai#9312)

* fix(daemon): report the checkout's own repo sync error

ensureRepoReady read the sync failure from a per-workspace slot that any
concurrent background sync overwrote, so a later successful sync of another
repo erased the error and checkout reported only "repo is configured but
not synced". syncWorkspaceReposContext now returns its error to the caller
and the shared slot is removed.

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

* feat(server): share SSL_CERT_DIR CAs with daemons for git

Daemons clone with the system git, which never sees the backend's
SSL_CERT_DIR, so a repository on a host with an internal CA could not
sync even after the operator made the backend trust that CA.

The new gitca package mirrors crypto/x509's SSL_CERT_DIR loading, skips
the system directories, and keeps only certificates crypto/x509 trusts.
The register and workspace repos responses carry them as ca_certs. The
daemon writes its system CA bundle plus those certificates to a bundle
file, adds http.sslCAInfo to its git environment, and records it in the
cache and isolated checkout config so agents' fetch and push use it too.
An empty value removes the bundle; Windows daemons skip it.

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

* docs: document private CA trust for daemons

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

* fix(daemon): keep operator CA trust and existing checkouts working

Review of the private CA support found two regressions for repositories
that already worked:

- Removing the last backend CA deleted the bundle while existing linked
  worktrees and isolated checkouts still pointed at it, so their HTTPS
  fetch and push failed. The bundle is now rewritten to the machine's own
  trust instead, and each repository drops the reference the next time
  the cache configures it.
- The bundle started from the system bundle and was injected through the
  environment, overriding an operator's general http.sslCAInfo and any
  repository-local value. It now starts from the file a general
  http.sslCAInfo names (system bundle otherwise), the environment no
  longer carries it, clone passes it with -c, and repository config is
  only set where unset and only unset where it is ours. URL-scoped
  settings, repository-local values, and GIT_SSL_CAINFO keep precedence.

Isolated checkouts record the setting before their first checkout, which
can lazily fetch from origin, and clones before the refspec migration
fetch.

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

---------

Co-authored-by: Iliya Anastasov <ianastasov@shopmetrics.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ce specs (CHE-1900)

- plugin-surface-document: test the wrapper document that exists now; the old buildSurfaceDocument is gone, so the spec did not even load
- issue-content-disclosure: wait for the description save before a full navigation; give the upload settle 30s
- project-resource-ref: creating a project leaves the list open, so open the project
…tween overlapping runs (CHE-1900)

- a hooks.json without version or timeout is not Multica's
- two runs in one workdir hold a lease on one file: installed by the first, removed by the last; a run with another hook command is refused
…ultica-ai#9387)

* MUL-8119 fix(views): keep project workflow name visible in narrow sidebar

Co-authored-by: multica-agent <github@multica.ai>

* MUL-8119 fix(views): show only the workflow name in the sidebar trigger

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
…a-ai#9385)

* MUL-8118 fix(workflows): let a new status step edit its handoff before the save

A step added as a new status had a read-only detail panel: no handoff
fields ('Save the workflow first') and no status picker. The save already
sends a handoff with new_status steps, so the gate was UI-only.

- Key a new column by its draft id so HandoffFields can edit it; its
  next/back still only offer statuses that exist before the save.
- Let a new column's status picker swap it for an existing status.
- Select the new step as soon as it is added.
- Instructions textarea id via useId (draft ids are not valid element ids).
- Drop the now-unused new_step_handoff string.

Co-authored-by: multica-agent <github@multica.ai>

* MUL-8118 fix(workflows): resolve a step's default next over the whole draft

The default next and the card's 'then →' were resolved over saved statuses
only, so with QA and Deploy both unsaved, QA showed 'Next column (Done)'
while the server resolves it to Deploy on save. Resolve the default over
the full draft order (as NextStatus does); the explicit next/back choices
still list only statuses that exist before the save.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-eve <multica-eve@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
… real leftover (CHE-1900)

The stricter metadata rule made the old fixture look foreign. A leftover without timeout is now asserted to be refused.
…side a project (multica-ai#9389)

Co-authored-by: multica-agent <github@multica.ai>
…ultica-ai#9069)

* fix(wakeup): clear a condition rule's error once a check commits

A condition check that fails (for example on its 50ms lock wait) records the
error on the rule. Nothing cleared it afterwards: an unmet condition never
reaches AdvanceIssueWakeup, which is the only write that resets last_error, so
the rule kept reporting the old failure while every later check succeeded.

SetWakeupConditionState runs only inside a check that is about to commit, so it
now resets last_error. A "waiting for claimed run" note written later in the
same dispatch still lands, because AdvanceIssueWakeup runs after it.

* fix(wakeup): clear a system rule's error on every committed pass

dispatchSystem commits early when the assignee changed, when the parent is
closed or the rule is off, and while the parent sits in backlog. None of those
passes writes condition state, so a child_done rule kept an earlier pass's
error: until its parent left backlog, or for good on a closed parent, and the
workspace wakeups view kept flagging it.

The pass now clears last_error once it holds the rule's lock; an error later in
the pass rolls the clear back. Also corrects the SetWakeupConditionState
comment (it is written by baselines and system passes too, not only checks)
and trims the condition test to the assertion it exists for.

* fix(wakeup): preserve current diagnostics when checks recover

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Yushen <yushen@devv.ai>
Co-authored-by: multica-agent <github@multica.ai>
The right-click menu and the matching hover labels were hardcoded English, so zh-Hans, ja, ko, and fr all showed Pin tab, Close tab, and Close other tabs.
…ultica-ai#9392)

Co-authored-by: Sol-Boy <sol-boy@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
UPSTREAM_VERSION add/add conflict with CHE-1910 resolved to 0.6.4.
… condition.met (CHE-1900)

The timeline entry for a condition wakeup showed 'When event condition.met occurs'.
Also repairs the issue-table, fold-all and wakeup E2E specs (rows API total, child-branch request, draft clear, expand thread).
…eam migrations to 671 and 672 (CHE-1900)

main took 670 for run_gate_event. The new daemon_artifact_version_test writes its scripts through writeTestExecutable, as the guard test requires.
…the suite (CHE-1900)

- issues:68: choosing a date leaves the filter menu open; close it before reopening
- issue-content-disclosure: wait for the id-to-identifier redirect before starting (it remounts the page and drops an open find bar), hover a thread's header before clicking its toggle, wait for the unmount save before a full navigation
- settings:41: the Composio settings sit behind composio_mcp_apps and read 'Connected apps' now
- project-resource-ref: click the new project until it opens
- global-setup: visit each route once; next dev compiles a route on first visit (10 to 20 s), which timed out local-search and navigation specs
…HE-1900)

Conflicts: router/handler (gitca ext CA certs beside the fork's build stamp),
issue_wakeup_system (clear last_error on a locked pass, before the fork's pause logic),
codex (usage fallback helper; a credential boundary still reads no session logs),
issue-workflows hooks and status picker/options (MUL-8121 Default statuses outside a project,
on top of the fork's resolved/retry model), status-picker tests (both suites),
workflow-editor-page.test (fork file kept; upstream's added as .upstream.test).
@congvc-bot-x99 congvc-bot-x99 Bot changed the title chore(fork): sync upstream multica v0.6.4 (CHE-1900) [merge] chore(fork): sync upstream multica v0.6.5 (CHE-1900) [merge] Oct 22, 2026
congvc-bot[bot] added 8 commits October 22, 2026 07:38
…nt observable checks (CHE-1900)

Cheese chose EQUIVALENT_SIGNAL. The table shows no progress text any more.
issue-table 86: loaded rows from the rows responses, never the whole result, of the exact
total from /groups, and the group's first row rendered.
issue-table cursor test: head plus tail return all 60 rows with no cursor left, the last
row is rendered; it sorts by Updated date because manual order exists only under status
grouping, touches the issue in the last tail row, and takes the refreshed head as the head
page that has it first. Loaded/total, grouping/hierarchy and realtime-cursor assertions kept.
errors.Is in the argv-limit pass-through test; scoped err in the VCS rows check.
…views suite a 15s test timeout (CHE-1900)

- issue-table: the loaded-rows and first-row signal reads only responses that arrive after the Table switch; a negative control withholds the Table's rows with the Board's data present and expects the signal to stay false
- views vitest: testTimeout 15s; the two full-suite failures and five more were 'Test timed out in 5000ms' under CPU contention
@congvc-bot-x99
congvc-bot-x99 Bot marked this pull request as ready for review October 22, 2026 12:25
@congvc-dev
congvc-dev merged commit a434fde into main Oct 22, 2026
35 of 36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.