Skip to content

chore(fork): sync upstream multica v0.6.3 (CHE-1835, CHE-1837) [merge] - #296

Merged
congvc-dev merged 60 commits into
mainfrom
agent/x99-claude-sonnet/bc7f808b4fad
Oct 19, 2026
Merged

congvc-dev merged 60 commits into
mainfrom
agent/x99-claude-sonnet/bc7f808b4fad

Conversation

@congvc-bot-x99

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

Copy link
Copy Markdown

What does this PR do?

Status: Draft. This PR syncs cheese-work/multica with upstream Multica release tag v0.6.3, following the release-tags-only decision (CHE-1054/CHE-1380). It is source integration only, not deployment approval. It stays draft until the dev team leader reconciles the independent different-lab review, OCR and current-head checks.

Correction, round 11 (19 Oct 2026): Luna's signed review 01a152cf-5ec2-7499-b613-715f221a59b1 on CHE-1835 reported three P1 blockers for head 3222c5363c4da7333cef7653f2898bec02a70fed against base 3ae6189563bbfe705d6baf1a0be0492969b7f510. This head fixes all three. Current head: 1087097e649e732bb81c06508e19cd6a2646e8bd. Live fork main is still 3ae6189563bbfe705d6baf1a0be0492969b7f510: 0 behind. No merge was needed. The latest upstream stable tag is still v0.6.3. Earlier OCR receipts and reviews cover older heads only. Full-range OCR stays NOT-RUN for this head. The 38 medium and 11 low OCR findings of the 3222c5363 report stay undispositioned. The 41 medium and 10 low findings of the earlier pair stay outside that receipt.

Public contract (round 11):

  • POST /api/issues/batch-update: updates.handoff_preview is now enforced. A batch accepts only a null-handler binding ("no handoff"). Any other binding answers 400. A batch without the field behaves as before.
  • The batch response gains updated_ids. updated is unchanged.
  • The web client now rejects a malformed GET /api/issue-workflows body. It no longer reads it as an empty list.
  • Mobile adds one call to GET /api/issues/{id}/workflow-handoff and sends the existing optional handoff_preview. No server change.

Round 11 blockers:

  • Batch confirmation: fixed. A batch status write binds "no handoff" for the issues it carries. The server skips an issue that would hand off and returns updated_ids. A partial result names the issue identifiers that stayed unchanged and offers a Retry that re-checks them. A catalog that cannot answer holds the write with a Retry toast. Sibling batch callers: priority and assignee batches never hand off. The run-confirm modal takes one issue for a status change. No CLI caller of batch-update exists.
  • Incomplete catalogs: fixed at one shared function, resolveIssueWorkflow. It is unresolved when a list is pending or failed, the project is missing from the project list, or the project names a workflow the workflow list lacks. It is resolved with no workflow only when the issue has no project, or when no workflow is stored and the project names none. The picker, the issue menu, the board drop, the batch toolbar and the create-form hint use it. A malformed workflow list now fails the query.
  • Mobile status picker: fixed. A status change previews first. A handoff opens a native alert. The write carries the binding, including the null-handler binding. A failed preview, a cancelled alert or a 409 writes nothing. useUpdateIssue refuses a status write that has no binding. This is source and unit-test work only.

Round 11 commits, in order: 78c1aa692 (tests only, red) → 1087097e6 (fixes, green). No rebase, no force-push.

Round 11 test-first evidence: CI at 78c1aa692 failed frontend-test, backend-tests, both frontend-views-test shards (run 39211898020) and Mobile Verify (run 39211898067). Local red at that commit: core 7 failed, views 15 failed, mobile 1 failed (promise resolved "{ id: 'issue-1', revision: 2 }" instead of rejecting), Go TestBatchStatusRefusesAHandoffTheClientDidNotPreview failed (status = "in_progress", want "todo" and expected 400, got 200). Disclosed repairs: three existing assertions in batch-action-toolbar.handoff.test.tsx now expect the null-handler binding (78c1aa692). In 1087097e6, batch-action-toolbar.confirm.test.tsx mocks now keep the real module and expect the binding, one fixture in issue-actions-menu.test.tsx now uses a project that names no workflow, and the unused empty-list fallback constant and its test were removed. apps/mobile/data/workflow-handoff.test.ts is retrospective.

Round 11 gates on X99, tree 1087097e6: go build ./..., go vet ./internal/..., the full internal/handler suite, internal/migrations and cmd/server pass. cmd/multica fails the same tests on origin/main in this runtime, so it is an environment limit. pnpm typecheck passes 10 of 10 tasks. pnpm lint reports 0 errors. Core passes 2398 tests. Views pass 6508 tests. Mobile typecheck, lint and 251 tests pass. Migrations: upgrade from main (711 versions) to 721, no-op rerun, fresh schema 721, and pg_dump schema diff of 0 lines. No migration file changed this round. CI at 1087097e6: 16 successful, 5 skipped, 0 failed, 0 pending.

Round 11 limits: Web single-issue and board writes that resolve to no handoff still send no binding, so the server hands the issue off after a workflow edit until the catalog refreshes. Other clients that call batch-update without handoff_preview keep the old behavior. Runtime reproduction of the reviewer's scenarios is NOT-RUN. No device, simulator or Maestro run was done for mobile. Windows and macOS tests are NOT-RUN.

Correction, round 10 (19 Oct 2026): Luna's signed review 01a151ca-7b32-7d2a-adc1-3c958ce980a2 on CHE-1835 reported six P1 findings for head 7ff70f64b640f79f33c8833abc778537d5e9cc98 against base 3ae6189563bbfe705d6baf1a0be0492969b7f510. This head fixed all six. Head at round 10 (superseded): 3222c5363c4da7333cef7653f2898bec02a70fed. Live fork main is still 3ae6189563bbfe705d6baf1a0be0492969b7f510: 0 behind, 57 ahead. No merge was needed. The latest upstream stable tag is still v0.6.3. Earlier OCR receipts and reviews cover older heads only. Full-range OCR stays NOT-RUN for this head. The 41 medium and 10 low OCR findings stay undispositioned.

Public contract: No HTTP change. handoff_preview is unchanged and stays optional. A preview that found no handoff is now sent with a null handler. The server already refused that binding when a handler became resolvable, and a test now pins it. One internal SQL query changed: CancelAgentTaskByUser gains two optional parameters (unless_assignee_type, unless_assignee_id). Both null keeps the old behavior and takes no issue lock. The batch endpoint still ignores handoff_preview.

Round 10 findings:

  • Batch status handoffs (batch-action-toolbar.tsx): fixed. A batch status change that enters a handoff step opens one confirmation (BatchHandoffConfirmDialog). Each handing-off issue is written singly with its own handoff_preview and the stop choice. The other issues move in one batch call after the confirmation. Cancel, a pending or failed preview, and an unloaded workflow or project list write nothing. A preview failure offers a retry. Audit of batch writes: status (fixed). Assignee: an explicit assignee never hands off, and an agent assignee already confirms. Priority: no workflow effect. Delete: own dialog.
  • Board moves before the project catalog resolves (board-view.tsx): fixed. A project card stays put until both the workflow list and the project list have loaded. A failed load stays blocked.
  • Side-peek status picker (issue-property-pills.tsx): fixed. The pills now pass projectId and issue. Audit of StatusPicker callers: table, issue detail and sub-issue rows already passed both. The batch toolbar has its own guard. The create modal has no issue yet.
  • Legacy run-confirm payload: fixed. runConfirmIntent carries stop_previous_assignee_runs and handoff_preview into the promote intent, and RunConfirmModal writes them, with or without suppress_run. Closing that dialog writes nothing. Audit: the issue-detail hook and the table row are the only callers.
  • Unbound no-handoff preview: fixed. The dialog sends the preview as a binding with a null handler. A handler that became resolvable, or a changed assignee, now gets 409 handoff_preview_stale.
  • Post-commit cancellation (issue.go, single and batch): fixed. The handoff records the previous assignee's active runs under the issue lock. After the commit it cancels only those runs. Each cancel is also conditional at the statement on the issue not belonging to the previous assignee again. A run that arrived after the commit stays. A hand-back keeps the run. Lock order: the cancel holds no other lock and reads the issue row FOR SHARE in its own statement, like CancelStalePendingStepTask. The stale-step cancellation is unchanged.

Commits, in order: 5f1eb0552 (tests only, red) → f9c68d0b1 (tests only: batch toolbar fixture repairs) → 74818ed2b (tests only: three existing no-handoff assertions now expect the bound result) → 3222c5363 (fixes, green). No rebase, no force-push.

Test-first evidence: CI at 5f1eb0552 failed backend-tests on TestHandoffStopLeavesWorkThatCameAfterTheCommit. All 4 cases failed, single and batch: "the run that started after the commit = "cancelled", want running" and "the run of the agent that owns the issue again = "cancelled", want queued". It also failed both frontend-views-test shards on 20 behavioral assertions: writes before confirmation, a missing binding, missing context and dropped fields. TestUpdateComment_GovernanceFlagOn_ProviderErrors_ResponseUnaffected also failed there; it is outside this diff and passed at the fix head. Green at 3222c5363: 16 successful, 5 skipped by path filter, 0 failed. Two tests are green on the parent by design: TestNegativeHandoffPreviewIsBoundAtTheWrite (characterization) and TestStopPreviousRunWaitsForAnUncommittedHandBack (retrospective).

Local gates on X99, tree 3222c5363: sqlc generate leaves the tree unchanged. go vet ./... passes. golangci-lint reports 0 issues. The full internal/handler suite and the other Go packages pass. cmd/multica and pkg/agent pass from a clean worktree with a clean environment. pnpm typecheck 10/10, pnpm lint 0 errors, pnpm build 6/6. Frontend tests: core 2386, web 337, desktop 678, docs 16, ui-lab 46, views 6494. Migrations are unchanged: a database upgraded from 3ae618956 (711 versions) reaches 721, a rerun applies nothing, and its schema equals a fresh database.

Limits: The mobile app has no workflow code, so its status writes can still hand off without this confirmation. Handoff members of a batch are written one by one, so a failure leaves a partial result. The toolbar reports it. Windows and macOS tests and rendered UI are NOT-RUN.

Base reconciliation and correction, round 9 (19 Oct 2026): Luna's return 01a1505a-2cc5-715b-bc88-f8e762076436 on CHE-1835 was STOPPED/NOT-RUN because live fork main had moved. Candidate head before this round: b423fa51ec38eb3fdee69a9e41f39ebe71a152c9. Live fork main at execution: 3ae6189563bbfe705d6baf1a0be0492969b7f510 (CHE-1848, #303), merged in. Current head: 7ff70f64b640f79f33c8833abc778537d5e9cc98; divergence 0 behind, 53 ahead. The two commits after 5d01ae14b (1dceec2f2, 7ff70f64b) change only server/pkg/agent/hermes_escaped_holder_unix_test.go (see the CI note in the round 9 gates). The latest upstream stable tag is still v0.6.3 (all semver tags listed). Migration numbers in the older round sections below are superseded: the nine workflow migrations are now 657–665, and the fork-only write-tag migration is 666_wakeup_handoff_write_tag. Public-contract change this round: optional request field handoff_preview (see below), reported for fresh review. Earlier OCR receipts and reviews cover older heads only; full-range OCR stays NOT-RUN. The stopped OCR attempt's five P1 candidates were triaged by the author only; the independent verifier makes its own disposition.

Correction, round 8 (18 Oct 2026): The signed review of head 7d7f362cbb96dcfeb32e8e7e1e25e0d526a57aa1 reported three P1 findings (comment 01a14f4f-3f5d-77b0-877f-f5f6da5a5f27 on CHE-1835). This head fixes all three. Current head: b423fa51ec38eb3fdee69a9e41f39ebe71a152c9. Execution base: fork main 6b89a1764afb6418af873336c720bbc36bcb447e, unchanged; upstream's latest stable tag is still v0.6.3. The earlier OCR receipts and reviews cover older heads only. Full-range OCR stays NOT-RUN for the reviewed head (423 of 431 paths plus manual inspection of the other eight). Public-contract changes in round 8: none added. The handoff preview client now throws on a malformed successful response (see below), which changes client behavior only.

Correction, round 7 (18 Oct 2026): The signed review of head f72ba4e0cfc805ed3a923bbda70017e8f3754cba reported two P1 findings, both in the workflow editor (comment 01a14e27-ebaf-72f9-8dae-443927ccc013 on CHE-1835). This head fixes both. Current head: 7d7f362cbb96dcfeb32e8e7e1e25e0d526a57aa1. Execution base: fork main 6b89a1764afb6418af873336c720bbc36bcb447e, unchanged. The earlier OCR receipts and reviews cover older heads only. Full-range OCR stays NOT-RUN for the reviewed head (423 of 431 paths plus manual inspection of the other eight). Round 7 changes no server code and adds no contract change: the optional expected_version / version contract of round 6 is unchanged.

Correction, round 6 (18 Oct 2026): The signed review of head e8e2fe552a1f59381237aa9725a3b2e086343530 reported four P1 findings (comment 01a14d7e-4da3-76a0-9a60-850a1f70994b on CHE-1835). This head fixes all four. Current head: f72ba4e0cfc805ed3a923bbda70017e8f3754cba. Execution base: fork main 6b89a1764afb6418af873336c720bbc36bcb447e, unchanged since round 5. The earlier OCR receipts and reviews cover older heads only. Full-range OCR stays NOT-RUN for the reviewed head (419 of 427 paths plus manual inspection of the other eight). Fresh OCR and a fresh independent review are owned by the dev team coordinator.

Public contract change (round 6): PATCH /api/issue-workflows/{id} accepts an optional expected_version. Workflow responses gain a version field (updated_at at full precision). A request without expected_version behaves as before. A request whose version is not the stored one gets 409 workflow_changed_elsewhere. The TypeScript types and zod schema carry both as optional.

Correction, round 5 (18 Oct 2026): The signed review of head 9c3ae7bcaa5e8bd1a773c924949fbed35fc7677e reported five P1 findings and a test-synchronization caveat (comment 01a14c92-0177-7e42-8816-aa9cac0b199b on CHE-1835). This head fixes all of them. Current head: e8e2fe552a1f59381237aa9725a3b2e086343530. Execution base: fork main 6b89a1764afb6418af873336c720bbc36bcb447e (#298), merged in. The earlier OCR receipts and reviews cover older heads only. Full-range OCR stays NOT-RUN for the reviewed head (416 of 424 paths plus manual inspection of the other eight). Fresh OCR and a fresh independent review are owned by the dev team coordinator.

Correction, round 4 (18 Oct 2026): The signed review of head c46f297f7a4c8250f90f98e1bce1d141674a8f10 reported five P1 findings (comment 01a14beb-71ac-7801-bc41-ef1ab5d6b383 on CHE-1835). This head fixes all five. Current head: 9c3ae7bcaa5e8bd1a773c924949fbed35fc7677e. Execution base: fork main 7c1261d6ff775a889d8e0a46a8d43685f1c5544f (#277), merged in. The earlier OCR receipts and reviews cover older heads only. Full-range OCR stays NOT-RUN for the reviewed head (413 of 421 paths plus manual inspection of the other eight). Fresh OCR and a fresh independent review are owned by the dev team coordinator.

Correction, round 3 (18 Oct 2026): The signed review of head bcdf401416c5ea3d0ea261e9206b1cbe780c0b36 reported five P1 findings (comment 01a149de-f4a8-7c22-bb84-eeaf4e83564f on CHE-1835). This head fixes all five. Current head: c46f297f7a4c8250f90f98e1bce1d141674a8f10. Execution base: fork main 15a54e92da9848c80fec6c97577b3e0317a9041e (#254, #300), merged in. The earlier OCR receipts and reviews cover older heads only. Fresh full-range OCR and a fresh independent review are owned by the dev team coordinator.

Correction, round 2 (17 Oct 2026): The signed review of head 746b0a99267a4448884b3c13a942c4702bee1833 reported five P1 findings. This head fixes all five. Current head: bcdf401416c5ea3d0ea261e9206b1cbe780c0b36. Execution base: fork main a48be61c3221b6e161508ae4ee06b415a55e55ef (#286), merged in. The earlier OCR receipts and reviews cover older heads only. Fresh full-range OCR and a fresh independent review are owned by the dev team coordinator.

Pinned refs:

  • Upstream tag v0.6.3: annotated tag object 61bf2402edf4a585f8356f9b68b03f9e1bdabafe, peeled to commit 18fa9f728a9e25f2465df2789631edb2a75442ad. Also upstream main at execution (17 Oct 2026, 08:40 ICT). No newer stable release existed then (v0.6.3 is the highest v0.6.* tag).
  • Fork base at authoring start: main 2b135399996d8072c5a99deb88ef55edb62f49b4 (after fix(migrations): renumber agent_task_queue_context to clear duplicate prefix 627 (CHE-1824) #290). Fork main then moved to 6c92e0c6820c9ae3512a0b5b38ad701bc0089a1e (feat(execenv): pass the managed MCP config to agy as a per-task plugin (CHE-1821) #287, feat(chat): create a coalesced inbox record for DM replies (CHE-1793) [squash] #276); that head is merged in (second merge commit below).
  • Merge-base with v0.6.2 (7ab92d7622ecd81d179a8d83ae2dea74835a5069). Divergence before the merge: fork main 240 commits ahead, 18 commits behind v0.6.3. No other sync PR or branch was open.
  • Commits: 1fea916f3 (merge of v0.6.3) → 2874e8b9b (merge of fork main 6c92e0c68) → 783571348 (untracks a stray draft file added by mistake in 2874e8b9b). → 29b3b1650 (go-lint fixes in upstream-authored lines: four errcheck in realtime/hub.go and membership.go, one gocritic filepathJoin in daemon/gc_test.go; no behavior change). Merge commits only, no rebase, no force-push. Tests-only first commit: none (merge-only candidate).
  • Correction commits, in order: 53e74d336 (tests only, red) → dd338e4fe (fixes, green) → 2703e4e49 (merge of fork main d4978e0c0, sqlc conflicts resolved by regeneration) → 2bf873ea1 (renumbers the nine upstream migrations) → 746b0a992 (sqlc regenerated after the renumber). Merge commits only, no rebase, no force-push.
  • Divergence at 746b0a992: 0 commits behind fork main d4978e0c0, 27 ahead.
  • Round 9 commits, in order: 450080227 (merge of fork main 3ae618956) → 5a8f7e2c6 (renumber the nine workflow migrations and the write-tag migration to 657–666; test-first disposition below) → e5870fd15 (tests only) → 5d01ae14b (fixes). Merge commits only, no rebase, no force-push.
  • Round 8 commits, in order: 65d9f3d9e (tests only, red) → ee86e7f14 (fixes, green) → b423fa51e (types one test double; no behavior change). No merge was needed. Divergence at b423fa51e: 0 behind, 47 ahead. No rebase, no force-push.
  • Round 7 commits, in order: c81b526e9 (tests only, red) → 7d7f362cb (fixes, green). No merge was needed. Divergence at 7d7f362cb: 0 behind, 44 ahead. No rebase, no force-push.
  • Round 6 commits, in order: c1a5fdcce (tests only, red) → f72ba4e0c (fixes, green). No merge was needed: fork main stayed at 6b89a1764. Divergence at f72ba4e0c: 0 behind, 42 ahead. No rebase, no force-push.
  • Round 5 commits, in order: 4fc9bd7bb (merge of fork main 6b89a1764, no conflicts) → d3ea959b2 (tests only, red) → be5f4e514 (fixes) → e8e2fe552 (test helper: own connections for the race helper). Merge commits only, no rebase, no force-push. Divergence at e8e2fe552: 0 commits behind fork main 6b89a1764, 40 ahead. CI at be5f4e514 timed out in backend-tests because the new helper test took pool connections it then waited on; e8e2fe552 fixes that.
  • Round 4 commits, in order: c8ae94c75 (merge of fork main 7c1261d6f, no conflicts) → 8374469cb (tests only, red) → 9c3ae7bca (fixes, green). Merge commits only, no rebase, no force-push. Divergence at 9c3ae7bca: 0 commits behind fork main 7c1261d6f, 36 ahead.
  • Round 3 commits, in order: 91ee35cd2 (merge of fork main 15a54e92d, no conflicts) → 0cb70ad48 (tests only, red) → c46f297f7 (fixes, green). Merge commits only, no rebase, no force-push. Divergence at c46f297f7: 0 commits behind fork main 15a54e92d, 33 ahead.
  • Round 2 commits, in order: 81c9ad282 (merge of fork main a48be61c3, no conflicts) → 74c403bce (tests only, red) → bcdf40141 (fixes, green). Merge commits only, no rebase, no force-push. Divergence at bcdf40141: 0 commits behind fork main a48be61c3, 30 ahead.

Conditions at merge time: A merge to main starts cd-qualification.yml, and its success triggers cd-deploy.yml, which deploys to C00 through the c00-production environment. The same push builds a new multica-daemon-<os>-<arch> artifact, and fork-built daemons auto-update to it (CHE-1580). Merging this PR is therefore a deployment-relevant decision.

Related Issue

Closes CHE-1837

The PR title also carries CHE-1835, the sync issue this PR delivers. CHE-1835 stays open until review, OCR and the ready decision finish.

Changelog

Changelog: none

Type of Change

  • New feature (upstream v0.6.3 sync: project workflows)
  • Bug fix (upstream v0.6.3 fixes)
  • Tests (adding or improving test coverage)
  • Documentation update

Changes Made

All 18 upstream commits after v0.6.2 merge in with a merge commit. Conflict decisions (17 conflicted files in the v0.6.3 merge, 2 in the main merge):

  • Onboarding shim routes (server/cmd/server/router.go): retain (round 3). Upstream MUL-8047 deleted the pre-v3 shim. The base file onboarding_shim.go allows deletion only after X-Client-Version telemetry shows every active install has aged out, and this PR has no such telemetry. The round 3 commit restores the two routes, the handlers, their tests, SetStarterContentState and the starter_content_state field of the user response from fork main. Retain the fork's RequireHumanActor on /api/cli-token and /api/auth/desktop-token; upstream's plain /api/cli-token registration is not taken.

  • Migrations cleanup map (server/cmd/migrate/main.go): add the three upstream issue_workflow index entries (574, 576, 582). Upstream's renumbered wakeup names are not added; the fork applies those under its own names.

  • Feature flags (featureflags/keys.go): retain all fork keys (Jev, receipts, wakeup definition writes, export privacy); add upstream ProjectWorkflowsV1.

  • Run cost UI (execution-log-section.tsx, issue-runs-dialog.tsx): adapt. Take the upstream redesign (cost bars, totals, per-agent chart). The fork's only delta there was the costYPercent guard (non-finite or zero yMax); it is reapplied in issue-runs-cumulative-chart.tsx, where yPct was still unguarded. costYPercent stays used by stepCurvePath.

  • Issue activity row (issue-detail.tsx): adapt. Upstream's nested row with the workflow TimelineHandoff, plus the fork's wakeup wrapping, WakeupActivityLinks and test ids (CHE-1726).

  • Agent CLI (cmd_agent.go): retain the fork's runAgentEnvPatch.

  • Comment-only conflicts (hermes_home.go, actor_guards.go): take upstream's shorter comments; code is identical.

  • Builtin skill (multica-platform/references/issues.md): retain the fork text and add upstream's project-workflows bullet.

  • Generated code: server/pkg/db/generated regenerated with sqlc v1.31.1, not hand-merged.

  • Inbox insert (queries/inbox.sql, second merge): upstream CreateInboxItem now inserts only when the recipient is a workspace member or agent; fork feat(chat): create a coalesced inbox record for DM replies (CHE-1793) [squash] #276 adds chat_session_id and chat_message_id. Both kept: the guarded INSERT ... SELECT ... WHERE with the two new columns. sqlc output regenerated. Callers that use inbox.CreateIfEligible already treat no rows as inserted=false. server/internal/decisiontx/ping.go (fork) called CreateInboxItem directly, so a decision ping to a recipient who left the workspace returned pgx.ErrNoRows. The ping was then never marked handled and the worker retried it every five seconds. Fixed in dd338e4fe: NotifyPing now uses inbox.CreateIfEligible. A departed recipient gets no inbox item, and the ping is still recorded as handled.

  • Workflow handoff assignee (server/internal/handler/issue.go): a status change that also edits the description or attachments takes updateIssueAtomically. Its nullable-field refresh restored the old assignee, so the workflow-selected handler was lost in single and batch updates. Fixed in dd338e4fe: withWorkflowAssignee marks the assignee as supplied when a handoff chose it. It copies the map, because a batch shares one map across issues.

  • Migration numbering: upstream adds nine migrations (573–579, 581, 582) whose prefixes collide with fork migrations already applied. They are new to this fork, so no database has recorded them. 2bf873ea1 renumbers them to 656–664 in their original order. The SQL bytes are unchanged. No applied migration is renamed or rewritten, allowedForkCollisions is identical to fork main, and the three runner name-map entries follow the new names. Details and the next-sync caveat are in server/cmd/migrate/README.md. The fork's capture_issue_wakeup (latest definition: 532) differs from upstream's 579 redefinition by one added predicate line, so 662_wakeup_skip_handed_to_agent is safe to apply on top.

  • Round 2: workflow decisions under locks (issue_workflow_write.go, issue.go): single and batch writers that land an issue in a project take the shared status-catalog lock, then a key-share lock on the project row, then the issue row. They then decide the workflow again against the locked rows. A workflow edit holds the catalog lock exclusively. A project switch holds the project row for update. So a write either finishes first or sees the new workflow. A status the workflow no longer lists returns 400 status_not_in_workflow. An agent step that a member moved first returns 409 workflow_step_moved. A changed handler or handoff decision returns 409 workflow_changed; a batch skips that issue.

  • Round 2: queue eligibility (service/issue_trigger.go, issue.go): a pending assignment run stamped for a step the issue has left no longer suppresses a handoff into the next step. The write cancels it and queues the run for the new step. The previous assignee's runs now stop before the enqueue decision, so a cancelled run cannot leave a cached no-run answer.

  • Round 2: wakeups (issue_workflow_entry.go): superseded by round 3 below.

  • Round 2: copy name and veto notice: projectCopyName clamps the base to the room left, so workflow QA with a 61-rune project title no longer panics. decisionVetoNoticeStep uses inbox.CreateIfEligible, so a notice for a former member is recorded handled with no inbox item and no retry. The other CreateInboxItem callers already use CreateIfEligible.

  • Round 3: the issue's own project (issue_workflow_write.go): every update now carries a workflow write, also for an issue in no project. After it locks the issue row, verify refuses the write with 409 workflow_changed when the issue sits in another project than the one the write decided for. A batch skips that issue. The write never validates under one project and persists under another, and never moves the issue back. It does not take the new project's lock, because that would invert the lock order against a workflow switch.

  • Round 3: duplicate-mark lock order (issue.go): the write takes the project lock, then the duplicate-mark locks (both issue rows in id order), then verifies under the issue row lock. The issue row lock used to come first, which could deadlock two crossed duplicate marks.

  • Round 3: create (service/issue.go): IssueService.Create resolves the project once (its own or the parent's), then takes the shared status-catalog lock and the project key-share lock, and only then reads the workflow. Both locks stay until the insert, for built-in and custom statuses. A create that a switch or edit overtakes starts on todo.

  • Round 3: handoff delivery (migration 665_wakeup_handoff_write_tag, wakeup_actor.go, issue.go, issue_trigger.go): the capture no longer skips the handed agent's issue events. The write tags its receipts with a write id. After the direct run is enqueued, ConsumeHandoffWakeupReceipts marks that agent's receipts of this write handled. A stop, a cancelled request or a failed enqueue between commit and enqueue leaves the receipts pending, so the agent gets the change at least once. A dispatcher pass between commit and consume can deliver it twice. A receipt merged with earlier pending events is not consumed.

  • Round 3: JEV routing (squad_jev_routing.go): routing takes the catalog, project and issue locks in the same order as other issue writes. It moves a backlog or todo issue to in_progress only when the project's workflow lists that status. Otherwise it dispatches the worker and keeps the status.

  • Round 3: sibling status writers: the GitHub merge sync writes done, and the task-failure reset and the runtime sweeper write todo. Every valid workflow lists todo, done and cancelled (issueworkflow.IsRequired), so these writers cannot leave the workflow. They are unchanged. JEV routing was the only system writer of in_progress.

  • Round 3: test change (issue_duplicate_maintenance_test.go): TestConcurrentWriteDoesNotRestoreClearedMark now also accepts a wait on the issue row lock query, because the write takes that lock before UpdateIssue. The assertion on the final mark is unchanged.

  • Round 4: board drops (packages/views/issues/components/board-view.tsx): handleDragEnd now judges the column the pointer is released over, before it reads the mirrored column. After allowed B and blocked C the mirror stayed in B, and a release over C moved the card to B or opened a handoff for B. A release over a column the card's workflow omits, or an archived column, now resets the board and shows the drop_blocked toast.

  • Round 4: duplicate-run delivery (issue_trigger.go, agent.sql): when the direct run loses the pending-task unique index, dispatchIssueRun reports delivery only if the run that holds the slot is queued. A queued run reads the issue at claim. A dispatched run read it earlier, so the receipts of the write stay pending. New query HasQueuedTaskForIssueAndAgent.

  • Round 4: stale media-deferred runs (agent.sql): ListStalePendingStepTasks now also lists runs deferred behind channel media, the same set as the unique pending-task index. CancelStaleStepRuns cancels them, so the run for the new step queues instead of deduplicating against a stale one. The only caller of the query is CancelStaleStepRuns.

  • Round 4: builder draft recovery (packages/views/agents/create/builder-workspace.tsx): an unclosed <agent_draft> block is recovered only from the final assistant reply. Older replies are read as closed blocks only. A superseded unclosed reply can no longer merge over configuration saved after it. An unclosed final reply that was never applied is still recovered.

  • Round 4: source-context create lock order (service/issue.go): the source issue (also the parent) is locked after the catalog and project locks. A workflow switch holds the project row and then remaps the project's issues, so the old order deadlocked the two. The project is read from the parent before the lock; a parent moved in that window gives the child its former project.

  • Round 5: builder draft rollback (builder-workspace.tsx): only replies newer than the applied one can be read as drafts. After closed A, recovered unclosed B and manual edits, a later plain, failed or invalid reply C no longer brings A back or autosaves it. A genuinely newer closed draft still applies.

  • Round 5: workflow editor refresh (workflow-editor-page.tsx, five locale files): the editor follows a refreshed baseline only while it is clean. With unsaved edits, an updated_at or status-count refresh keeps them. If the refresh changed the workflow's content, the editor keeps the edits, shows an alert with "Load latest" and "Keep my edits", and holds Save until one is chosen, so Save cannot silently overwrite the other admin. A clean editor refreshes as before.

  • Round 5: stale-step cancel (agent.sql, task.go, issue_trigger.go): new query CancelStalePendingStepTask cancels a run only while it is still pending, assignment-level and stamped for another step. CancelStaleStepRuns uses it through CancelTaskOptions.StaleStep, so a dispatched run that starts after selection stays. User cancellation of running work is a different path and is unchanged.

  • Round 5: duplicate-run delivery (agent.sql, issue_trigger.go): HasQueuedRunCarryingIssueWrite replaces HasQueuedTaskForIssueAndAgent. A queued duplicate counts only if it is assignment-level (no wakeup, no trigger comment), has the same reviewed head, and is stamped for the step the issue is in now; when that step hands off, a run without a step does not count. Otherwise the receipts stay pending.

  • Round 5: parent project under lock (service/issue.go): after the source issue is locked, a child that inherits the parent's project compares it with the locked parent. A parent moved in between returns source_context_changed (409), so the client retries against the current project. The create never persists under the former project's workflow.

  • Round 5: race helper (issue_workflow_race_test.go, race_helper_test.go): raceUntilBlocked now records the competing transaction's backend and fires only when a backend is blocked by it (pg_blocking_pids), on its own connection. TestRaceHelperIgnoresAnUnrelatedLockWaiter proves an unrelated waiter cannot fire it. Final-state assertions of the existing tests are unchanged.

  • Round 6: workflow lost update (issue_workflow.go, workflow-editor-page.tsx, mutations.ts): the editor saves against the version it started from. The server compares expected_version with the stored workflow under the exclusive catalog lock and answers 409 workflow_changed_elsewhere instead of replacing a newer save. The editor then keeps the edits, shows the existing "Load latest" / "Keep my edits" choice, and "Keep my edits" saves against the refreshed version. A successful save writes the response into the list cache, so a quick second save names the new version.

  • Round 6: board drop on a stranded status (board-view.tsx): drag-over no longer refuses a return to the card's own column when the workflow omits that status, and drag-end treats a release over the card's own column as a drop where it started. The blocked-target and archived-column checks of round 4 are unchanged.

  • Round 6: squad receipts (squad.go, issue_trigger.go): enqueueSquadLeaderTaskOutcome reports whether the leader's run, a Jev-routed specialist's run, or nothing was queued. Only a leader run settles the leader's receipts. enqueueSquadLeaderTask keeps its bool for other callers.

  • Round 6: create-time handler (service/issue.go, issue.go, source_context.go, issue_workflow_entry.go): IssueCreateParams.HandoffResolver resolves and permission-checks the step's handler under the catalog and project locks, for the status the issue actually lands on. Its answer replaces the assignee from the early unlocked read. The HTTP create and the manual comment sub-issue create set it when the caller chose no assignee. Other service callers (channels, autopilot, Lark, quick-create) set none and behave as before; they never resolved a handler at create.

  • Round 7: version bound to the accepted revision (workflow-editor-page.tsx): the editor keeps the version its baseline and draft came from, and a save sends that one, not the latest row's. The baseline now re-seeds on a version change as well as an updated_at change, so two saves in one second no longer hide behind equal updated_at. A dirty editor that receives a refresh with different content holds Save behind the existing "Load latest" / "Keep my edits" choice. The kept edits are saved against the version that was showing when the editor chose to keep them. A refresh that changes nothing the editor shows advances the token. A successful save adopts the response as the new baseline.

  • Round 7: editor before its row loads (workflow-editor-page.tsx, hooks.ts, five locale files): an existing workflow shows a loading state until its row is in the query, an error with Retry when the list fails to load with no data, and the existing not-found state when it is missing. Fields and Save are absent in those states, canSave requires the row, and save returns early without a row. A new workflow is created as before. useIssueWorkflows gains isError and retry, both additive. The server still accepts tokenless writes for old clients.

  • Round 8: editor draft during a save (workflow-editor-page.tsx): the "adopt the next baseline" flag is gone. A successful save adopts its response as the baseline only if nothing was typed since the save began; edits made during the save stay, on top of the revision the server accepted, and the next save names the response's version. An unrelated refresh during a save leaves the draft alone, so a later stale-version, mapping-required or plain failure finds the draft intact. The editor's own save arriving through the list raises no alert. A response for another target is ignored.

  • Round 8: stale-step cancel (agent.sql): CancelStalePendingStepTask now also reads the issue row FOR SHARE in a CTE and cancels only while the issue's current status is still the step the request captured. A request that captured step B and resumes after the issue moved to step C, with C's run queued, cancels nothing. A move to C that is still uncommitted is waited for. The single caller is CancelStaleStepRuns; running-task protection, stale media-deferred replacement and user cancellation are unchanged. The statement now holds a share lock on the issue row for its duration.

  • Round 8: malformed handoff preview (schemas.ts, client.ts, handoff-confirm-dialog.tsx): handoff is now required in the preview schema, and previewWorkflowHandoff throws instead of returning an empty no-handoff preview when a successful response is malformed. The dialog shows its existing error with a Retry button and does not call onConfirm. A valid handoff: false still applies without asking, and a valid handoff still asks. EMPTY_WORKFLOW_HANDOFF_PREVIEW is removed. The server always sends handoff.

  • Round 9: migrations (server/migrations, cmd/migrate): main added the published 656_dm_channel_default_off, which collided with the unmerged 656_issue_workflow. Main's migration keeps its name. The nine workflow migrations moved up by one, 656–664 to 657–665, and the write-tag migration moved from 665 to 666. Old to new: 656_issue_workflow→657_issue_workflow, 657_issue_workflow_pkey_index→658_…, 658_issue_workflow_primary_key→659_…, 659_issue_workflow_workspace_name_index→660_…, 660_project_workflow_id→661_…, 661_agent_task_workflow_step→662_…, 662_wakeup_skip_handed_to_agent→663_…, 663_issue_workflow_is_default→664_…, 664_issue_workflow_default_index→665_…, 665_wakeup_handoff_write_tag→666_wakeup_handoff_write_tag. Up and down SQL bodies are unchanged except two comment lines in the fork-only 666 files that name the earlier migration. The three runner cleanup mappings (658, 660, 665), the README and one Go comment follow the new names. 666 still runs after the capture_issue_wakeup redefinition in 663. The allow-list and TestMigrationNumericPrefixesAreUnique are unchanged (git diff origin/main -- server/internal/migrations is empty). The next-sync caveat stands: delete the nine upstream workflow filenames if a sync re-adds them, and keep 666 after the re-added capture_issue_wakeup.

  • Round 9, candidate 1 (confirmed, corrected): unresolved workflows read as "no workflow" (hooks.ts, status-options.ts, status-picker.tsx, issue-actions-menu-items.tsx, board-view.tsx): useProjectWithWorkflow returns resolved, true for an issue with no project or once the project and workflow lists have loaded. The single-issue status picker and the status menu write nothing while it is false, and the board leaves a project card where it is. A failed load keeps them blocked. Audited and left unchanged: the project picker (a project move is administrative without enter_workflow_step), batch status editing (server preview), the create modal (no issue yet).

  • Round 9, candidate 2 (confirmed, corrected): a confirmation not bound to the previewed state (issue_workflow_write.go, issue.go, issue_move.go, the dialog and its four callers): the confirmation now sends handoff_preview (the assignee and handler it showed). Under the issue-row lock the write compares it with the locked assignee and the resolved handler and answers 409 handoff_preview_stale on a mismatch, stopping no run. Single updates and board moves carry it; the move endpoint allows the new field; batch ignores it. Absent, the write behaves as before. New public field; review requested.

  • Round 9, candidate 3 (confirmed, corrected): mapping selections reset by parent renders (workflow-mapping-dialog.tsx): a new plan starts from the suggestions. A change in the contents of targetKeys, not its identity, keeps every choice that is still valid and replaces only one that left the list.

  • Round 9, candidate 4 (confirmed, corrected): a cached non-handoff preview auto-applied (handoff-confirm-dialog.tsx): only a preview fetched since this opening counts. It alone may apply the move on its own, fill the dialog or enable the confirm button. While the refresh is pending the dialog shows its spinner. A failed refresh shows the error with Retry and applies nothing. A refresh that finds a handoff asks first.

  • Round 9, candidate 5 (confirmed, corrected): conflict, mapping dialog and catalog refresh (workflow-editor-page.tsx): while a conflict is unresolved, a refresh that adds no content change no longer moves the accepted version, and save returns early with a toast on every path, including the mapping dialog's confirm. "Load latest" also closes a mapping dialog that was computed for the dropped draft.

Fork-only behavior: before the correction, files the fork changed and upstream v0.6.2→v0.6.3 did not touch are byte-identical to fork main (git diff origin/main HEAD outside the upstream file set shows only migrations_lint_test.go). This covers runtime update routing (CHE-1580), governance and auth guards. Files both sides changed were merged by git or by the decisions above. The correction adds two fork-authored behavior changes, both with a tests-first commit (see How to Test).

How to Test

Test-First check: PASS for the correction. The first correction commit 53e74d336 changes only tests. Against its parent 29b3b1650 these three tests fail on behavior (not on compilation):

--- FAIL: TestDecisionPingToFormerMemberIsRecordedNotRetried
    decision_request_api_test.go:334: ping to a former member: item=<nil> error=no rows in result set, want no item and no error
--- FAIL: TestWorkflowHandoffSurvivesCombinedStatusAndDescriptionUpdate/single_update
    issue_workflow_test.go:1082: status/assignee = in_progress//, want in_progress handed to agent b2c65a72-0819-4a03-9bc1-f887fecb2943
--- FAIL: TestWorkflowHandoffSurvivesCombinedStatusAndDescriptionUpdate/batch_update
    issue_workflow_test.go:1095: status/assignee = in_progress//, want in_progress handed to agent b2c65a72-0819-4a03-9bc1-f887fecb2943

The next commit dd338e4fe adds the fix, and the same tests pass. The migration renumber has no new behavior test. TestMigrationNumericPrefixesAreUnique is its gate, and it passes with an allow-list identical to fork main.

Round 9 Test-First check. The merge and renumber commits change no behavior. The renumber has its own red: after the merge, TestMigrationNumericPrefixesAreUnique fails ("migrations 656_dm_channel_default_off and 656_issue_workflow share numeric prefix 656") and passes after the renumber, with the test and allow-list untouched.

For the behavior fixes, commit e5870fd15 changes only tests. Its CI run failed backend-tests, both frontend-views-test shards and the aggregate jobs. Behavioral failures: TestHandoffConfirmationIsBoundToWhatWasPreviewed (reassignment and handler-change cases); status menu, status picker and board tests that expect no write before the workflows resolve; dialog tests for a cached non-handoff preview (applied before the refresh; no Retry after a failed refresh); the editor test that saves the old draft from the mapping dialog; and the binding tests for the dialog, board, picker, project picker and modal. Three of the new tests were wrong in ways the red run did not show, and I corrected them after the fix without weakening an assertion: the mapping-dialog test never actually selected a destination (it used a click the select ignores), so its failure there was not the intended one and it is now red for the right reason (in_review: "todo" where "done" was chosen) when the fix is removed; the status-picker binding test could not find its row; and the dialog binding test clicked the confirm button while it was still disabled. The unrelated-by-design tests that pass before and after: the direct-write control cases, the second editor conflict test, and the "falls back to a valid destination" mapping test.

Round 8 Test-First check: PASS. Commit 65d9f3d9e changes only tests. Against its parent 7d7f362cb these tests fail on behavior. Its CI run failed backend-tests, frontend-test, both frontend-views-test shards and frontend-build. The frontend-build failure is a TypeScript error in one of the new test files, a compile failure that I do not count as red evidence.

--- FAIL: TestStaleStepCancelFromAnOlderRequestLeavesTheCurrentStepsRun
    stale_step_overlap_test.go:38: the current step's run = "cancelled", want queued: an older request cancelled it
--- FAIL: TestStaleStepCancelWaitsForTheNewerStepAndThenSkips
    stale_step_overlap_test.go:59: the write finished while a competing workflow or issue writer was uncommitted
--- FAIL: workflow-editor-page.test.tsx (draft preservation during a save)
    keeps the draft when an unrelated refresh lands during a save that is then refused as stale
    keeps the draft through a refresh after the server asks for a status mapping
    keeps edits made while the save is pending, and saves them against the response's version
--- FAIL: workflow-handoff-preview.test.ts (5 cases) and schemas.test.ts
    rejects a wrong-typed handoff flag / a missing flag / an empty object / null / a list instead of reading it as no handoff
--- FAIL: handoff-confirm-dialog.malformed.test.tsx
    does not apply the move on a wrong-typed flag, and offers a retry / on an empty object / asks for confirmation when the retry returns a real handoff

The overlap test pauses the older cancellation behind the newer step's uncommitted move, queues the newer run in that transaction, then lets it commit. The dialog test runs the real client parser with only the network stubbed. Two test defects surfaced only after the fix and are corrected in ee86e7f14 and b423fa51e: the overlap test inserted the newer run before removing the older one (the pending-run index admits one per issue and agent), and one test double had a wrong type. Commit ee86e7f14 adds the fixes and the same tests pass. The existing schema test that expected a malformed preview to become "no handoff" was replaced, as the admission asked.

Round 7 Test-First check: PASS. Commit c81b526e9 changes only tests. Against its parent f72ba4e0c these tests fail on behavior. Its CI run failed in frontend-views-test (1/2) (and the aggregate frontend); backend jobs passed because no server code is involved.

--- FAIL: workflow-editor-page.test.tsx (version binding)
    holds Save when another admin saves within the same second with different content  (no alert)
    saves kept edits against the version of the content the editor kept them over     (no Keep my edits button)
    reseeds a clean editor from a same-second save                                    (still shows the old name)
--- FAIL: workflow-editor-page.test.tsx (before the row is available)
    offers no editable fields and no Save while the workflow is loading               (a textbox is rendered)
    shows an error with a retry, and no Save, when the workflow list fails to load    (no alert)

The same-second tests give two versions the same updated_at and different full-precision version. They also assert that nothing is saved before the editor chooses. Passing before and after, by design: the new-workflow create test, the not-found test, the successive-save test, the "load row then save" test and the "refresh that changed nothing" test. Commit 7d7f362cb adds the fixes and the same tests pass. No existing test was changed.

Round 6 Test-First check: PASS. Commit c1a5fdcce changes only tests. Against its parent e8e2fe552 these tests fail on behavior. Its CI run failed in backend-tests, both frontend-views-test shards and frontend-build. The frontend-build failure is a TypeScript error (the tests name the new version field), a compile failure that is not counted as red evidence.

--- FAIL: TestCreateIssueResolvesTheStepHandlerUnderTheProjectLocks (both cases)
    issue_create_handoff_test.go:81: the issue kept the former workflow's handler ...
--- FAIL: TestCommentSubIssueGetsTheStepHandlerAndRun
    issue_create_handoff_test.go:132: assignee = "", want the step's handler ...
--- FAIL: TestUpdateIssueWorkflowRefusesAStaleExpectedVersion / TestUpdateIssueWorkflowAcceptsTheCurrentExpectedVersion
    issue_workflow_concurrent_edit_test.go: the workflow response carries no version
--- FAIL: TestJevRoutedSquadAssignmentKeepsTheLeadersReceipts (single, batch)
    squad_jev_workflow_test.go:160: leader's pending receipts = 0, want 1: no leader run carries this write
--- FAIL: workflow-editor-page.test.tsx
    sends the version of the workflow the editor started from / saves the kept edits against the latest version
--- FAIL: board-view-workflow-drop.test.tsx
    does not commit the allowed column it passed over when released back on its own column / does not start the passed column's handoff ...

Passing before and after, as intended: TestUpdateIssueWorkflowWithoutAnExpectedVersionStillWrites (compatibility), TestCommentSubIssueResolvesTheStepHandlerUnderTheProjectLocks, the 409 alert test, and the clean-editor refresh. The version race test saves twice inside one second on purpose, because updated_at has one-second resolution. Commit f72ba4e0c adds the fixes and the same tests pass. No existing test was changed this round.

Round 5 Test-First check: PASS. Commit d3ea959b2 changes only tests. Against its parent 4fc9bd7bb these tests fail on behavior (not on compilation). Its CI run failed in backend-tests, frontend-views-test (1/2) and frontend-views-test (2/2).

--- FAIL: TestStaleStepCancelLeavesARunThatStartedAfterSelection
    stale_step_cancel_test.go:52: the stale run that started after selection = "cancelled", want running
--- FAIL: TestDispatchIssueRunDuplicateMustMatchTheCurrentStep (stale-step and step-less cases)
    issue_trigger_delivery_test.go:106: dispatchIssueRun = true, want false
--- FAIL: TestSourceContextCreateRevalidatesTheParentsProjectUnderItsLock
    source_context_lock_order_test.go:125: the child was created in project eea55bb7-..., want the parent's current project 6435bc2c-...
--- FAIL: TestRaceHelperIgnoresAnUnrelatedLockWaiter
    race_helper_test.go:67: the hook fired before the write reached its lock: an unrelated waiter was taken for it
--- FAIL: workflow-editor-page.test.tsx (5 tests)
    keeps dirty edits when only updated_at changes / when a status is added / holds Save when another admin changes the workflow / Load latest / Keep my edits
--- FAIL: builder-workspace.test.tsx (4 tests)
    does not roll the form back to an older draft when a plain / failed / invalid reply follows a recovered one; does not roll saved edits back when reopening after the applied unclosed reply

The cancel race test holds the dispatched-to-running update open in a competing transaction and waits until the cancel is blocked on it. The helper now identifies that block by pg_blocking_pids. be5f4e514 adds the fixes. e8e2fe552 changes only the helper's connections. The same tests pass at e8e2fe552. The editor test file is new; the clean-editor test passed before and after, as intended.

Round 4 Test-First check: PASS. Commit 8374469cb changes only tests. Against its parent c8ae94c75 these tests fail on behavior (not on compilation). Its CI run failed in backend-tests and frontend-views-test (2/2).

--- FAIL: TestDispatchIssueRunCountsOnlyAnUnstartedDuplicateAsDelivery/a_dispatched_run_holds_the_slot
    issue_trigger_delivery_test.go:56: dispatchIssueRun = true, want false
--- FAIL: TestStaleMediaDeferredStepRunIsReplacedWithReceiptsKept (single, batch)
    issue_trigger_delivery_test.go:87: the stale media-deferred run = "deferred", want cancelled
--- FAIL: TestSourceContextCreateTakesProjectLocksBeforeTheSourceIssue
    source_context_lock_order_test.go:62: source-context create failed: ERROR: deadlock detected (SQLSTATE 40P01)
--- FAIL: board-view-workflow-drop.test.tsx > does not move the card to the last allowed column when released over a blocked one
    expected "vi.fn()" to not be called at all, but actually been called 1 times (status "in_progress")
--- FAIL: board-view-workflow-drop.test.tsx > does not hand off through the last allowed column when released over a blocked one
    expected the handoff dialog to be absent
--- FAIL: builder-workspace.test.tsx > keeps saved manual edits when a superseded unclosed reply is followed by a plain reply / a failed reply
    expected the name field to keep "Saved edit"

The delivery test calls dispatchIssueRun with a queued, a dispatched or no run holding the slot, because a concurrent enqueue cannot be made deterministic in a test. The lock-order test holds the project row as a workflow switch does, starts the create, waits for it to block, and then remaps the source issue in the holding transaction; under the old order the database aborts one side. The board test drives the captured dnd-kit callbacks: start, over allowed B, over blocked C, end over C. Commit 9c3ae7bca adds the fixes and the same tests pass. I changed no existing test case this round; the helper raceUntilBlocked gained an optional hook that runs while the write is blocked.

Round 3 Test-First check: PASS. Commit 0cb70ad48 changes only tests. Against its parent 91ee35cd2 these tests fail on behavior (not on compilation). Its CI run backend-tests failed at the test step.

--- FAIL: TestIssueWriteFollowsTheProjectTheIssueMovedTo (8 subtests: single/batch, with/without description, moved between projects / into a project from none)
    issue_workflow_project_lock_test.go:66: project = b93fc6e7-..., want 4a159e33-...: the write undid the move
    issue_workflow_project_lock_test.go:68: status = "in_progress", want "todo": the write must not leave the issue outside its workflow or over a newer move
--- FAIL: TestCreateIssueRechecksWorkflowSwitchedMidFlight (explicit project, project inherited from the parent)
    issue_workflow_project_lock_test.go:142: created on in_progress, a status the project's workflow no longer lists
--- FAIL: TestHandedToAgentReceiptIsCommittedBeforeTheRunExists (single/batch x assignment/workflow step)
    issue_handoff_wakeup_test.go:120: pending receipts when the run was enqueued = [0], want one: a stop here loses the change
--- FAIL: TestHandedToAgentReceiptSurvivesAFailedRunEnqueue (single/batch x assignment/workflow step)
    issue_handoff_wakeup_test.go:154: pending receipts = 0, want 1: the change reached the agent through neither path
--- FAIL: TestJevRoutingKeepsTheIssueInsideTheProjectWorkflow/workflow_omits_in_progress
    squad_jev_workflow_test.go:104: status = "in_progress", want "todo": ...
--- FAIL: TestJevRoutingRechecksWorkflowSwitchedMidFlight
    squad_jev_workflow_test.go:131: status = "in_progress", want "todo": ...
--- FAIL: TestPreV3OnboardingShimRoutesStayRegistered (both routes)
    onboarding_shim_route_test.go:23: POST /api/me/onboarding/runtime-bootstrap = 404, want 401: the route is not registered

The enqueue tests use a database trigger on agent_task_queue: one records the pending receipts at insert time, one refuses the insert. The race tests hold the competing writer's transaction open and wait for a blocked backend, as in round 2. Commit c46f297f7 adds the fixes and the same tests pass. The original pre-v3 onboarding tests return with the restored handlers.

Round 2 Test-First check: PASS. Commit 74c403bce changes only tests. Against its parent 81c9ad282 these tests fail on behavior (not on compilation). The first failure line of each is shown.

--- FAIL: TestIssueWriteRechecksWorkflowEditedMidFlight (8 subtests: single/batch, with/without description, status dropped/handler replaced)
    issue_workflow_race_test.go:153: the write finished (200: ...) while a competing workflow or issue writer was uncommitted
--- FAIL: TestIssueWriteRechecksProjectSwitchedMidFlight (single, batch)
    issue_workflow_race_test.go:208: the write finished (200: ...) while a competing workflow or issue writer was uncommitted
--- FAIL: TestAgentWriteDoesNotOverwriteNewerMemberMove (single, batch, each with description)
    issue_workflow_race_test.go:259: expected 409, got 200
    issue_workflow_race_test.go:261: status = "in_review", want "blocked": the write must not leave the issue outside its workflow or over a newer move
--- FAIL: TestSameAgentSuccessiveStepGetsAnExecutableRun (single, batch)
    issue_workflow_queue_test.go:58: pending runs after the second step = [in_progress], want exactly one, stamped in_review
--- FAIL: TestHandoffFromSquadKeepsAnExecutableRunForTheLeader (single, batch)
    issue_workflow_queue_test.go:93: pending runs for the new handler = [], want one for in_review: the cancelled run left no replacement
--- FAIL: TestHandedToAgentWakeupKeptWhenNoDirectRunStarts (backlog and suppress_run, single and batch)
    issue_workflow_queue_test.go:148: direct runs = 0 and wakeup receipts = 0, want exactly one delivery
--- FAIL: TestProjectCopyNameShortWorkflowLongProject, TestWorkflowHandoffCopyForLongProjectTitle
    issue_workflow_queue_test.go:161: naming the copy panicked: runtime error: slice bounds out of range [:62] with capacity 32
--- FAIL: TestDecisionVetoNoticeToFormerMemberIsRecordedNotRetried
    decision_veto_notice_test.go:205: no rows in result set

The race tests hold the competing writer's transaction open, start the write, wait until a backend is blocked on a lock, and only then commit the competing writer. They do not depend on timing. The next commit bcdf40141 adds the fixes, and the same tests pass.

Round 9 gates at 5d01ae14b, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers:

  1. Migrations: fork main 3ae618956 migrates to 711 versions (last: 656_dm_channel_default_off). This head upgrades it to 721, applying the nine workflow migrations and 666. A rerun applies nothing. A fresh database also has 721 versions. pg_dump --schema-only of both is identical except the random \restrict token. TestMigrationNumericPrefixesAreUnique passes and the allow-list is unchanged.
  2. sqlc generate leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. Full go test ./internal/handler passes (239 s) on the committed tree 5d01ae14b (clean worktree status). The other 98 Go packages pass with -p 1 (82 ok, 16 without tests). cmd/multica and pkg/agent pass from a clean worktree with scripts/go-test-with-agent-cli-guard.sh and TMPDIR outside /tmp (pkg/agent with -p 2 -parallel 2).
  4. pnpm typecheck 10/10, pnpm lint (0 errors) and pnpm build 6/6 pass. Frontend tests at low host load (load average under 12): core 2386, web 337, desktop 678, docs 16, ui-lab 46 and views 6470 of 6470 (--maxWorkers=6) pass.
  5. CI at 5d01ae14b: the first attempt failed backend-agent-tests on TestHermesBackendReportsTurnWhenEscapedDescendantHoldsPipes with holder state "": EOF. Diagnosis: not a daemon fault. The test's holder process records its pid with os.WriteFile, which creates the file empty before it writes. The test waits for the file to exist, can read it in that window, and fails on the empty read. The test file is upstream's (MUL-5241) and identical to fork main, so the race predates this PR. Fix, tests first: 1dceec2f2 adds TestReadEscapedHolderIdentityWaitsOutAnEmptyStateFile, which reproduces the exact CI message in a clean worktree; 7ff70f64b makes the holder publish by rename and the reader wait out an unparsable state until its existing budget runs out, then fail with the parse error. The budgets and both original assertions are unchanged. After the fix the two escaped-holder tests pass 30 of 30 runs, and pkg/agent passes in full. CI at 7ff70f64b: 16 successful, 5 skipped by path filter, 0 failed, 0 pending. No production code changed in these two commits.
  6. Not run: Windows and macOS tests, rendered UI.

Round 8 gates at b423fa51e, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers. b423fa51e differs from ee86e7f14 only in one test file's types, so the Go runs on ee86e7f14 apply to it (git diff ee86e7f14 b423fa51e -- server is empty).

  1. Migrations: this round adds none. A database migrated with fork main 6b89a1764 (710 versions) upgrades to 720. A rerun skips everything. A fresh database also has 720 versions. pg_dump --schema-only of both is identical except the random \restrict token. TestMigrationNumericPrefixesAreUnique passes and allowedForkCollisions is unchanged.
  2. sqlc generate leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. Full go test ./internal/handler passes (246 s) on the committed tree of ee86e7f14. The other 98 Go packages pass with -p 1 (82 ok, 16 without tests). cmd/multica and pkg/agent pass from a clean worktree with scripts/go-test-with-agent-cli-guard.sh and TMPDIR outside /tmp (pkg/agent with -p 2 -parallel 2).
  4. pnpm typecheck 10/10, pnpm lint (0 errors) and pnpm build 6/6 pass. Frontend tests: core 2386 of 2386, docs 16, ui-lab 46, web 337 and desktop 678 pass when run per package. Views passes 6447 of 6447 with --maxWorkers=6.
  5. Local frontend limits, not waived: the combined pnpm test runs failed with 5 to 100 s timeouts across unrelated files in core, web, desktop and views while the host load average reached 150 to 650 from other tenants' processes. I did not raise a timeout. I waited for the load to fall below 20 and ran each package separately; those runs are the ones reported above. A full-parallel views run at load 135 also failed with timeouts and is not counted.
  6. CI at b423fa51e: 16 successful, 5 skipped by path filter, 0 failed, 0 pending.
  7. Not run: Windows and macOS tests, rendered UI.

Round 7 gates at 7d7f362cb, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers. The Go sources are identical to the previous head; the Go runs below were on this tree.

  1. Migrations: this round adds none. A database migrated with fork main 6b89a1764 (710 versions) upgrades to 720. A rerun skips everything. A fresh database also has 720 versions. pg_dump --schema-only of both is identical except the random \restrict token. TestMigrationNumericPrefixesAreUnique passes and allowedForkCollisions is unchanged.
  2. sqlc generate leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. go test ./internal/handler passes (272 s). The other 98 Go packages pass with -p 1 (82 ok, 16 without tests). cmd/multica and pkg/agent pass from a clean worktree with scripts/go-test-with-agent-cli-guard.sh and TMPDIR outside /tmp (pkg/agent with -p 2 -parallel 2).
  4. pnpm typecheck 10/10, pnpm lint (0 errors; warnings are in files this PR does not change) and pnpm build 6/6 pass. Frontend tests: core 2379, docs 16, ui-lab 46, web 337 and desktop 678 pass. Views passes 6436 of 6437 in both a combined and a standalone run. The failure is the 5000 ms timeout of table-view-editing "confirms a status change that would start an agent run" under host load (load average up to 168 during this round). The test takes 1.7 s alone and passes alone; it timed out the same way in rounds 4 and 6. I raised no timeout and do not count it as waived.
  5. CI at 7d7f362cb: 16 successful, 5 skipped by path filter, 0 failed, 0 pending.
  6. Not run: Windows and macOS tests, rendered UI.

Round 6 gates at f72ba4e0c, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers:

  1. Migrations: this round adds none. A database migrated with fork main 6b89a1764 (710 versions) upgrades to 720. A rerun skips everything. A fresh database also has 720 versions. pg_dump --schema-only of both is identical except the random \restrict token. TestMigrationNumericPrefixesAreUnique passes and allowedForkCollisions is unchanged.
  2. sqlc generate (v1.31.1) leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. go test ./internal/handler passes (245 s). The other 98 Go packages pass with -p 1, except cmd/multica, pkg/agent and one flake. cmd/multica and pkg/agent pass from a clean worktree with scripts/go-test-with-agent-cli-guard.sh and TMPDIR outside /tmp (pkg/agent with -p 2 -parallel 2). internal/governance/receipt failed one test in that run and passes in 2 of 3 isolated reruns here. The same package fails 1 of 6 isolated runs on unmodified fork main 6b89a1764. This PR does not touch it.
  4. pnpm typecheck 10/10, pnpm lint 7/7 and pnpm build 6/6 pass. Frontend tests: core 2379, docs 16, ui-lab 46, web 337 and desktop 678 pass. Views: 6426 of 6427 in the combined run and 6425 of 6427 in a standalone run. The failures are 5000 ms timeouts under host load (load average 21 to 31): table-view-editing "confirms a status change that would start an agent run" in both runs, and autopilot-dialog.validation "clears the assignee error and submits once an agent is picked" in the second. Both files pass alone, twice. The first of them also timed out under load at earlier heads of this PR. I raised no timeout.
  5. CI at f72ba4e0c: 16 successful, 5 skipped by path filter, 0 failed, 0 pending.
  6. Not run: Windows and macOS tests, rendered UI.

Round 5 gates at e8e2fe552, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers:

  1. Migrations: this round adds none. A database migrated with fork main 6b89a1764 (710 versions) upgrades to 720. A rerun skips everything. A fresh database also has 720 versions. pg_dump --schema-only of both is identical except the random \restrict token. TestMigrationNumericPrefixesAreUnique passes and allowedForkCollisions is unchanged.
  2. sqlc generate (v1.31.1) leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. go test ./internal/handler passes (253 s) at be5f4e514; the later commit changes only a test helper, and the targeted race and cancel tests pass at e8e2fe552. The other 98 Go packages pass with -p 1, except cmd/multica and pkg/agent. Both pass from a clean worktree with scripts/go-test-with-agent-cli-guard.sh and TMPDIR outside /tmp (pkg/agent with -p 2 -parallel 2).
  4. pnpm typecheck 10/10, pnpm lint 7/7 and pnpm build 6/6 pass. Frontend tests: views 6421 of 6421 (495 files), core 2379, docs 16, ui-lab 46 pass. Web passes 337 of 337 and desktop 678 of 678 when run per package. In the combined pnpm test run one web test, text-contrast "product UI expresses text hierarchy with tokens, not transparency", timed out at 5000 ms under load; the file passes in the per-package run.
  5. A full local go test ./internal/handler at e8e2fe552 with a four-connection pool was not completed: two governance race tests failed only because my test URL carried pool_max_conns, which those tests hand to a plain connection that rejects it. They pass with the plain URL.
  6. CI at e8e2fe552: 16 successful, 5 skipped by path filter, 0 failed, 0 pending.
  7. Not run: Windows and macOS tests, rendered UI.

Round 4 gates at 9c3ae7bca, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers:

  1. Migrations: this round adds none. A database migrated with fork main 7c1261d6f (710 versions) upgrades to 720. A rerun skips everything. A fresh database also has 720 versions. pg_dump --schema-only of both is identical except the random \restrict token. TestMigrationNumericPrefixesAreUnique passes and allowedForkCollisions is unchanged.
  2. sqlc generate (v1.31.1) leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. go test ./internal/handler passes (249 s). All 98 other packages except cmd/multica and pkg/agent pass with -p 1 (82 ok, 16 without tests). cmd/multica fails only inside the agent task directory; from a clean worktree with scripts/go-test-with-agent-cli-guard.sh and TMPDIR outside /tmp it passes. pkg/agent failed once on TestDimSessionLoadRetrySucceeds under default parallelism and passes with -p 2 -parallel 2; the test passes alone 3 of 3.
  4. pnpm typecheck 10/10, pnpm lint 7/7 and pnpm build 6/6 pass. pnpm check:ui-exports passes.
  5. Frontend tests: docs 16, ui-lab 46, core 2379, desktop 678 and web 337 pass. Views passes 6409 of 6410 in a full run. The one failure is table-view-editing "confirms a status change that would start an agent run", a 5000 ms timeout under load in two full runs; the file passes alone. The same file timed out under load at earlier heads of this PR.
  6. CI at 9c3ae7bca: 16 successful, 5 skipped by path filter, 0 failed, 0 pending.
  7. Not run: Windows and macOS tests, rendered UI.

Round 3 gates at c46f297f7, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers:

  1. Migrations: this round adds 665_wakeup_handoff_write_tag. A database migrated with fork main 15a54e92d (710 versions) upgrades to 720: the nine renumbered migrations as 656–664, then 665. A rerun skips everything. A fresh database also has 720 versions. pg_dump --schema-only of both is identical except the random \restrict token. TestMigrationNumericPrefixesAreUnique passes and allowedForkCollisions is unchanged.
  2. sqlc generate (v1.31.1) leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. go test ./internal/handler passes (242 s). go test ./... -p 1 on a freshly migrated database: every package ok, except cmd/multica, pkg/agent, internal/governance/receipt and internal/service. cmd/multica fails only because the run sits inside the agent task directory; from a clean worktree with scripts/go-test-with-agent-cli-guard.sh and TMPDIR outside /tmp it passes, and pkg/agent and internal/daemon/... pass the same way. internal/governance/receipt (two budget tests) and internal/service (one source-context cleanup test) each failed once in the long run and passed on rerun in isolation; internal/service passed as a whole package. No failure touches the changed code.
  4. pnpm typecheck 10/10 and pnpm lint 7/7 pass.
  5. Not run: frontend unit tests (round 3 changes no frontend code; CI covers the main merge), Windows and macOS tests, rendered UI.
  6. CI at c46f297f7: 16 successful, 5 skipped by path filter, 0 failed, 0 pending.

Round 2 gates at bcdf40141, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers:

  1. Migrations: this round adds none. A database migrated with fork main a48be61c3 (710 versions) upgrades to 719. The nine renumbered migrations apply as 656–664. A rerun skips everything. A fresh database also has 719 versions. pg_dump --schema-only of the upgraded and fresh databases is identical except the random \restrict token. TestMigrationNumericPrefixesAreUnique passes.
  2. sqlc generate (v1.31.1) leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. go test ./... -p 1 on a freshly migrated database: internal/handler and every other package ok, except cmd/multica, internal/daemon/execenv and pkg/agent. Those three fail only because of the host: the agent task directory marker, and /tmp/.git making the Cursor MCP tests collide. From a clean worktree with scripts/go-test-with-agent-cli-guard.sh and TMPDIR outside /tmp, all three pass (pkg/agent failed once on a codex timing test and passed on rerun). execenv fails the same way on fork main a48be61c3.
  4. pnpm typecheck 10/10 and pnpm lint 7/7 pass.
  5. Not run: frontend unit tests (the correction changes no frontend code; the main merge brings chat meter changes that CI covers), Windows and macOS tests, rendered UI.
  6. Not covered by the round 2 locks: other system status writers (GitHub merge sync, task reset, Jev routing). Round 3 traces them (see Changes Made).

Earlier correction gates at 746b0a992, kept for the record:, run on X99 against isolated pgvector/pgvector:pg17 and redis:8-alpine containers:

  1. Migrations: a database migrated with fork main d4978e0c0 (710 versions) upgrades to 719 versions. The nine renumbered migrations apply as 656–664. A rerun skips everything. A fresh database also has 719 versions. pg_dump --schema-only of the upgraded and fresh databases is identical (columns, indexes, function bodies), except the random \restrict token.
  2. sqlc generate (v1.31.1) leaves the tree unchanged. go vet ./... passes. golangci-lint v2.13.2 --new-from-merge-base=origin/main reports 0 issues.
  3. Go suite, go test ./... -p 1 on a freshly migrated database: all packages ok, except cmd/multica and pkg/agent. Those two fail only inside the agent task directory (host environment). From a clean worktree outside it, with scripts/go-test-with-agent-cli-guard.sh, both pass (pkg/agent with -p 2 -parallel 2).
  4. pnpm typecheck 10/10 and pnpm lint 7/7 pass.
  5. Not re-run for the correction: frontend unit tests (the correction changes no frontend code; the fork-main merge brings frontend changes that fork CI covers), Windows and macOS tests, rendered UI.

Earlier gates at 29b3b1650, kept for the record:

Gates run on X99 at the earlier heads. Typecheck, lint, vet and the first full frontend/Go runs were at 1fea916f3; after the second merge I re-ran sqlc, vet, typecheck, the migration check, the full Go suite, and views and core tests at 2874e8b9b (the later commit changes no code):

  1. pnpm typecheck: 10/10 tasks pass. pnpm lint: 7/7 tasks pass (0 errors, 22 views warnings from upstream code).
  2. go vet ./... passes. sqlc generate (v1.31.1) leaves the tree unchanged.
  3. Frontend tests, per package: docs 16, ui-lab 46, core 2372, desktop 678, web 337 pass. Views fails 1 to 4 tests per full run; the failures (inbox-filter-menu, issue-detail source-context, table-view-editing, table-view-project-grouping) are 5000 ms timeouts or a teardown window is not defined under load. The four files pass alone (152/152). A turbo-parallel pnpm test run also showed one timeout in web text-contrast and one in desktop scripts/package.test.mjs; both pass in the per-package rerun.
  4. Migrations on an isolated pgvector/pgvector:pg17 database: a database migrated with fork main (708 versions, including fork 650–652) upgrades to 717; the eight upstream migrations apply out of order (573–578, 581, 582; 579 was already recorded under the fork's name) and a rerun is a no-op. The upgraded and fresh 717-version databases match on column catalog, index definitions and function bodies (md5 of pg_get_functiondef).
  5. Go suite (scripts/go-test-with-agent-cli-guard.sh; regular packages -p 1, pkg/agent -p 2 -parallel 2) against the isolated Postgres and Redis, from a clean worktree outside the agent task directory: all packages ok at both heads. Load flakes seen once and passing on rerun: TestCursorBackgroundUncapturedDisabledToolWatchdog (text file busy on fork/exec) and TestTraecliDrainsNotificationsAfterPromptResponse (passes 2/2 alone and in a full pkg/agent rerun). One environment effect: /tmp/.git exists on X99, so cursorProjectRoot treated /tmp as a project and three TestPrepareCursorMcpConfig* tests collided on /tmp/.cursor/mcp.json. With TMPDIR outside /tmp the package passes. The fork code is unchanged by this PR.
  6. git diff --check passes, except one trailing blank line in upstream packages/views/workflows/templates.ts.
  7. golangci-lint v2.13.2 --new-from-merge-base=origin/main: first CI run reported the five issues above; after 29b3b1650 local run reports 0 issues. CI at 29b3b1650: all checks pass or are path-filter skipped (backend-tests, go-lint, sqlc-check, frontend-*, mobile, Changelog line).

Verification limits: Windows-only and macOS-only tests did not run; this fork has no Windows or macOS CI runner. golangci-lint is left to CI go-lint. No daemon was restarted and nothing was deployed. Rendered UI was not checked.

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have run tests locally and they pass (load-induced timeouts and one host /tmp/.git effect are recorded above)
  • I have added or updated tests where applicable (regression tests for each of the five round 2 and five round 3 findings)
  • If this change affects the UI, I have included before/after screenshots (not applicable: upstream UI changes, screenshots NOT-RUN)
  • I have updated relevant documentation to reflect my changes
  • If I added a new runtime / coding tool / UI tab, I synced the change to landing copy and relevant docs (not applicable)
  • If this PR touches Chinese product copy, I checked it against apps/docs/content/docs/developers/conventions.zh.mdx (NOT-RUN: upstream copy taken as-is)
  • I have considered and documented any risks above
  • I will address all reviewer comments before requesting merge

AI Disclosure

AI tool used: Multica Agent (Claude Code, x99-claude-sonnet)

Prompt / approach: Pin v0.6.3, merge it onto fork main with a merge commit, resolve each conflict by retaining or adapting fork behavior, regenerate sqlc output and run the gates on X99. The independent different-lab review and OCR are dispatched by the dev team coordinator after publication.

Dante-dan and others added 27 commits October 14, 2026 14:43
…ume replay (multica-ai#8549)

* fix(execenv): exclude clean HEAD advances from resume replay

* fix(execenv): normalize carried WIP before replaying local edits

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

* fix(execenv): resume after conflicting local draft commits

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

* fix(execenv): limit snapshot conflict handling to affected paths

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

---------

Co-authored-by: Sol-Boy <sol-boy@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
…ipients (multica-ai#9242)

* fix(inbox): restrict notifications to current workspace recipients

Removing a member or leaving a workspace deletes their subscriptions, but
several notification sources keep addressing the user by a retained ID:
reactions to their issues and comments, retained assignments, explicit
mentions, squad mentions through surviving squad-member rows, quick-create
outcomes and children_done for a retained parent assignee. CreateInboxItem
inserted those rows unconditionally and the inbox:new event was then sent to
the user's own connections, so a former member could keep receiving new
issue titles and comment content.

Guard the shared insert instead of each source: a member recipient must still
be a member of the item's workspace and an agent recipient must belong to it.
An ineligible recipient yields no row (pgx.ErrNoRows), which every caller now
treats as a quiet skip without publishing inbox:new or failing the underlying
operation. The check runs in the statement's snapshot and takes no row locks;
historical inbox rows are left untouched.

Existing delivery tests created recipients without membership rows; their
fixtures now add the membership the production path requires.

* test(inbox): assert children_done settles receipts when the recipient left

* refactor(inbox): centralize eligible notification writes

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

---------

Co-authored-by: 千乘妍 (Xiaoyaner) <xiaoyaner0201@users.noreply.github.com>
Co-authored-by: Sol-Boy <sol-boy@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
…i#9249)

* fix(realtime): revoke workspace access on membership removal

Close removed members' sockets, revalidate registration, and gate captured write batches with fresh coalesced membership queries. Cover relay failure, replay, queue races, and transactional removal/rollback (MUL-8014).

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

* fix(realtime): keep registration free of database waits

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

* perf(realtime): narrow removal event prefilter

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

* test(wakeup): avoid global backfill in workspace default test

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

* fix(realtime): bound concurrent authorization and defer relay revocation

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

* test(realtime): check caller budget without waiting for expiry

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

* fix(realtime): decouple member-list invalidation from revocation

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

* perf(realtime): skip removal decoding for member actor metadata

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

---------

Co-authored-by: Sol-Boy <sol-boy@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
…overy feedback (multica-ai#8514)

* fix(agents): recover completed builder drafts and align recovery feedback

* fix(agents): reject array builder draft payloads
…t GC (multica-ai#7893)

* fix(daemon): preserve tracked files during artifact GC

* fix(daemon): close artifact GC Git preservation gaps

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

---------

Co-authored-by: SSK <ssk@SSKs-Mac-Studio.local>
Co-authored-by: Sol-Boy <sol-boy@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
…ca-ai#9265)

* fix(agents): persist builder draft warning dismissal

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

* fix(agents): preserve builder warnings during automatic model cleanup

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

---------

Co-authored-by: Sol-Boy <sol-boy@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
…ica-ai#9261)

Arabic/Hebrew/Persian comments and descriptions that mix in English
words, paths or numbers render left-aligned with scrambled word order,
because every block inherits the page's LTR direction.

Use `unicode-bidi: plaintext` so each paragraph, list item, heading and
blockquote resolves its base direction from its own first strong
character, and `text-align: start` so alignment follows it. Applied to
.rich-text-editor (prose.css) and .markdown-content (markdown.css).
LTR-only content resolves to LTR and is unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… (ZIC-260) (multica-ai#8774)

* fix(daemon): recover undelivered batch claims after request timeout

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

* test(daemon): avoid copying task service locks in recovery fixture

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

* test(tasks): isolate delegated recovery assertions (MUL-7658)

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

* test(daemon): use one clock for deferred claim hints (MUL-7658)

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

* fix(daemon): keep claim recovery within client deadlines (MUL-7658)

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

* fix(execenv): preserve racy Git index timestamps in snapshots (MUL-7658)

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

* fix(daemon): deliver claim responses before recovery side effects (MUL-7658)

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

* fix(daemon): isolate HTTP claim retries from recovery (MUL-7658)

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

---------

Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Sol-Boy <sol-boy@multica-ai.local>
…ca-ai#9274)

* chore: clean historical comments (MUL-8034)

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

* docs: preserve pending Composio expiry contract

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

---------

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

* feat(runtimes): price Claude Sonnet 5.5 and Haiku 5.5

* fix(runtimes): align Claude 5.5 metrics pricing (MUL-8018)

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

* test(wecom): make inbox fallback timing deterministic (MUL-8018)

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

---------

Co-authored-by: mika <mika@multica.magma-ads.com>
Co-authored-by: Sol-Boy <sol-boy@multica-ai.local>
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: yushen <ldnvnbl@gmail.com>
… log (multica-ai#9288)

* MUL-8046: feat(issues): per-run cost bars and token detail in the run log

The sidebar's cumulative spend curve becomes one bar per run with spend,
token and agent-time totals, and each past run gains a second line with
agent, duration or failure reason, tokens and model. The Runs dialog
replaces the step curve with a bucketed bar chart (cost, tokens or agent
time, stacked by agent or token type), adds a cost split by agent and
model, model and token columns, and an expandable per-run breakdown.

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

* MUL-8046: feat(issues): one bar per run in the Runs dialog chart

Time buckets spent most of the chart on hours where nothing ran and made
each bar roughly one run anyway. The dialog chart now draws one bar per
run like the sidebar strip: height is cost, tokens or agent time, colour
is the agent or token type, a tick marks failed and cancelled runs, and
day dividers replace the time axis. Drops the bucket, lane hit-testing
and time-tick helpers.

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

* MUL-8046: fix(issues): split run bars by share so sub-dollar costs fill them

Segments used dollar amounts as flex-grow; factors summing below 1 fill only
that fraction of the free space, so bars and share bands under $1 drew short.

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

* MUL-8046: feat(issues): keep the cumulative cost chart as a second Runs dialog view

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#9291)

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
…tead of a welcome modal (multica-ai#9289)

* MUL-8047: feat(onboarding): open the runtime guide issue directly instead of a welcome modal

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

* MUL-8047: chore(onboarding): drop dead option cards, copy, and completion animations

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

* MUL-8047: fix(onboarding): reopen the existing guide issue on duplicate

A reload, or a create whose response was lost, leaves the guide issue
from an earlier Skip in place while onboarding is still incomplete; the
retry then hits the server's active-duplicate guard and the step was
stuck toasting forever. Recover the existing issue from the validated
409 body and continue to completion.

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#8814)

* feat(workflows): workspace workflows with step handoffs (MUL-7420)

A workflow is a named, workspace-level selection of statuses from the
shared status catalog, in board order, with an optional handoff on each
step. Projects opt in through project.workflow_id; a project without one
keeps the whole catalog (the implicit Default workflow), so existing
workspaces see no change.

- issue_workflow table + project.workflow_id, CRUD API under
  /api/issue-workflows, POST /api/projects/{id}/workflow with a dry-run
  status mapping plan. Only issues on statuses the target workflow does
  not list are remapped, and remapping never hands issues off.
- Issue writes keep the status inside the project's workflow. Entering a
  step with a handler through a status change assigns the issue to that
  handler and starts its run with the step's brief as the handoff note;
  an explicit assignee in the same write wins. Creating an issue starts
  at the workflow's starting step.
- Task claims carry the issue's project workflow, and the brief renders
  a Project Workflow section so agents move along this project's
  statuses instead of the built-in ones.
- Statuses a workflow lists cannot be archived.
- Adoption is gated by the project_workflows_v1 flag; existing
  workflows keep working and projects can always switch back to Default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* feat(workflows): workflow settings, project picker and workflow-aware boards (MUL-7420)

- Settings → Workflows (behind project_workflows_v1): the Default
  workflow plus named workflows, each shown as its step chain with
  handlers and the projects using it; an editor to pick statuses from the
  library, order them, mark the starting step, and set each step's
  handoff, instructions and next/back statuses.
- Project Workflow property: switching dry-runs first and asks where
  issues on unlisted statuses go; nothing else moves and nothing is
  handed off.
- A project board's columns are its workflow's steps, and each column
  head names who entering it hands the issue to.
- Status pickers offer only the issue's workflow steps and say who each
  hands off to; boards refuse drops onto statuses the dragged card's
  workflow does not list.
- New issues in a workflow project start on its starting step, with a
  hint naming who they will be handed to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix(workflows): name built-in steps once in the brief; roomier picker hints (MUL-7420)

Built-in statuses have no stored name, so the handoff brief printed
`todo` todo. Print the key alone when the name adds nothing.

The status picker widens for workflow projects and shows the handoff as
an arrow and name, so step names no longer truncate to one letter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* feat(workflows): handoff previews and a single timeline entry per handoff (MUL-7420)

- GET /api/issues/{id}/workflow-handoff?status= says what moving an issue
  would do: who it goes to, which runs of its current agent are still
  active, and the brief the handler receives. Backs the confirmation.
- POST /api/issue-workflows/preview-brief renders a step's brief from an
  unsaved workflow, for the editor's preview.
- Mapping plans list the statuses that stay, so the switch dialog can say
  which issues are untouched.
- A handoff is recorded on the status_changed activity (workflow, handler,
  whether a run started, the brief) instead of a separate assignee row.
- The brief now reads like the one shown in the design: the workflow, the
  current step, its instructions, the statuses, and the commands to hand
  off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* feat(workflows): rebuild the workflow UI to the reviewed design (MUL-7420)

The first cut used generic controls where the design had purpose-built
ones. This brings every surface up to the mockup:

- Settings → Workflows is one page: the workflows (step chain with
  handler avatars, handoff count, projects using it; Default first) and,
  below, the shared status library with each status's workflow usage.
- The workflow editor is a full page instead of a dialog: draggable step
  cards summarising each handoff, and a panel for the selected step —
  swap its status, pick the handler with a segmented control and a
  searchable target, handoff instructions, next/back statuses, a preview
  of the brief the handler receives, duplicate and delete.
- The project Workflow picker lists each workflow's statuses, handoffs and
  projects, links to settings, and always confirms the switch with what
  moves and what stays.
- Moving an issue into a handoff step (status picker, table, board drop)
  asks first: who it goes from and to, a default-on option to stop the
  previous agent's run, and the brief.
- Status pickers name the workflow and project, show each step's handler
  with an avatar or that the assignee stays, and explain themselves.
- Boards: the handler chip in project column heads, hatched columns a
  dragged card cannot enter with the reason, and a drop hint naming who
  the card will go to.
- The timeline records a handoff under its status change, with the brief
  the run received; the issue sidebar shows the workflow it follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix(workflows): word a queued previous run as queued in the handoff confirm

The confirm dialog said the previous agent was "still running (for N
minutes)" even when its task had only been queued. Queued runs now read
as queued, and the checkbox cancels them instead of stopping them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* feat(workflows): refuse a run's status change once the issue left its step

A run decides where the issue goes next from the step it was queued for.
When someone else moved the issue while the run worked, applying that
decision undid their move: in the live Grok run, a PM parked NW-1 as
blocked and the still-running agent moved it straight back to code
review.

Tasks now record the issue's status when they are queued
(agent_task_queue.workflow_step, written by the task INSERTs; automatic
retries keep their parent's step), and a run's own status changes move
it along. In a workflow project, an agent status change from a run whose
step no longer matches the issue is refused with 409
workflow_step_moved, naming where the issue is now and who moved it, so
the agent stops instead of retrying.

A handoff away from a squad now also stops the runs made on the squad's
behalf, and stopped runs record who stopped them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix(workflows): keep every status entry point inside the issue's workflow

The issue context menu listed every catalog status and applied handoff
steps without the confirmation the status picker and board show, so a
right-click could pick a status the workflow refuses or start an agent
unasked. It now offers the workflow's steps and opens the handoff
confirmation through a shared modal.

One-click status actions no longer assume built-in statuses a workflow
may not list: "mark done" lands on the workflow's done step (hidden when
it has none), un-marking a duplicate returns to the workflow's start,
and "mark duplicate" is offered only when the workflow lists cancelled.
The batch toolbar offers a single project's workflow statuses when the
selection sits in one project.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix(workflows): keep issue wakeups consistent with workflow steps

Three places where project workflows and issue wakeups met:

- A wakeup run is queued when it fires, so it took the issue's status at
  that moment as its workflow step. A wakeup an agent registered then
  bypassed the stale-step refusal: parked by a PM, the issue could be
  moved on by the agent's "check CI, then hand to review" wakeup. It now
  keeps the registering run's step; a member's wakeup still starts from
  the issue's status.
- Retries carried handoff_note only for wakeup runs, so a retried
  workflow handoff lost its brief. Rows with a workflow step keep it.
- Switching or editing a workflow remaps statuses in bulk without the
  close rule, so an issue remapped into a done status kept its wakeups
  and pending wakeup runs. The remap now ends them like any status write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix(wakeups): don't also wake the agent a write hands the issue to

A workflow handoff or an assignment already starts the receiving agent's
run. If that agent also subscribed an event wakeup to the issue's status
or assignee, the same write queued a second run for one fact, and
workflows made such writes frequent.

The write now names the agent it hands the issue to in the
transaction-local multica.handed_to_agent (the new agent assignee, a
squad's leader, or a step's handler even when it already owned the
issue), and wakeup capture skips that agent's subscriptions for the
write's issue.* events. Other agents' wakeups, and task, comment and
reaction events, are captured as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* feat(workflows): tell agents not to watch workflow issues with wakeups

The handoff brief and the runtime's project workflow section now say
that each step's handler gets a run when the issue enters the step, so
wakeups on a workflow issue are for waiting on time or on something
outside it, not on its status, assignee or runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* fix(workflows): refresh project usage after project changes

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

* feat(workflows): MUL-7420 split columns from per-project handoffs

Workflows now only define a board's columns between a fixed head
(backlog, todo) and tail (blocked, done, cancelled). Handoffs move to
project.workflow_handoffs, so projects can share columns and still hand
off to different people. Default becomes a real, editable row that every
project without a named workflow follows. Adds project handoff, add-column
(project fork or shared) and project brief preview endpoints; same-agent
adjacent columns re-run the agent.

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

* feat(core): MUL-7420 project handoffs and Default workflow client model

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

* feat(views): MUL-7420 column-header handoffs and project workflow menu

Board column headers set the project's handoff for that column; the
project toolbar gets a workflow menu (full flow, switch, templates) and an
add-column action. Views outside a project show Default's columns, with
other statuses reachable through a status filter. Adds the issue flow card,
create-project workflow choice, a columns-only workflow editor and a
lighter status picker.

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

* fix(views): MUL-7420 wrap create-project workflow label in a menu group

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

* feat(workflows): MUL-7420 keep workflow structure in settings; no squad handoffs

Projects no longer add columns or create workflows: the add-column
endpoint is removed and creating a workflow is admin-only. Handoffs go
to one agent, a member or the project lead; squads route work themselves,
so a handoff to one is rejected, as is the issue-creator handler.

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

* refactor(core): MUL-7420 drop the project add-column client

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

* feat(views): MUL-7420 simpler handoff picker and workflow editor form

The handoff form is one searchable "Hand to" picker (no handoff, the
project lead, an agent or a member); the next status defaults to the next
column and next/back overrides sit under "More options". The workflow
editor gets labelled name and description fields and shows the fixed
statuses as one start and one end row. Templates move from the project
menu to Settings → Workflows → New, and the project board loses its
add-column action.

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

* refactor(server): MUL-7420 keep handoffs on the workflow, make backlog and blocked optional

Handoffs move back onto workflow steps: a workflow is the whole pipeline,
and a project only picks one. The project lead stays a relative handler, so
projects can still share a workflow. Handoff edits are admin-only, through
the workflow write or PUT /api/issue-workflows/{id}/handoffs/{status}; the
brief preview moves to the workflow too. Drops the unreleased
project.workflow_handoffs migration.

Todo, done and cancelled are required; backlog and blocked are optional and
keep their fixed places. Without blocked, the brief points agents at the
step's back status.

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

* feat(views): MUL-7420 edit handoffs on the workflow, optional backlog and blocked

The workflow editor sets each column's handoff in its side panel, and
Backlog and Blocked switch on and off. The board's column popover and the
flow panel edit the workflow's handoff, say how many projects it reaches,
and are read-only for non-admins. Drops the per-project handoff client
code.

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

* fix(wakeups): MUL-7420 keep platform rules on writes that hand off nobody

Migration 579 skips an issue.* event for the agent a write hands the issue
to. Its filter compared agent_id with the transaction's handed_to_agent
setting using IS DISTINCT FROM, which is false when both are NULL. Main's
platform rules (child_done) have no agent_id, so every issue.* event that
handed the issue to nobody was dropped for them: a parent leaving backlog
never woke its agent for sub-issues that closed meanwhile
(TestChildDoneHeldWhileParentInBacklog).

Rules without an agent are never the handoff target, so they always
capture. Extends TestHandoffDoesNotAlsoWakeTheHandler with a platform rule
on a handoff and on a plain status change; the latter fails on the old
filter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* feat(views): MUL-7420 three-way handoff choice; quieter move confirm

The handoff form picks one of three directions, as in the mockup: no
handoff, hand to an agent (starts a run), or hand to a member, project lead
included (assignee only). What the step does, where it goes next and the
back status only reach an agent's brief, so they show and save for agent
handoffs alone.

The board's move confirmation keeps the agent's brief collapsed. Create
project shows the workflow picker even when Default is the only workflow,
with a note on where to make more. The hidden-columns panel says a board's
columns come from its workflow, with a link to manage workflows for admins.

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

* feat(workflows): confirm handoff on project move, per-project handoff copy, two-pane editor (MUL-7420)

- A single issue moved into a workflow project from issue detail confirms
  the handoff of the column it lands on; the write opts in with
  enter_workflow_step. Batch, API and CLI moves stay administrative.
- The handoff preview takes project_id to preview such a move.
- Saving a shared workflow's handoff from a board can keep the change to
  that project: the server copies the workflow for it and switches the
  project in one transaction (copy_for_project_id).
- The column popover says which workflow it edits and who else uses it.
- The workflow editor is a step list with a detail panel for the
  selected step.

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

* style: gofmt workflow tests

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

* feat(views): MUL-7420 show next/back always; edit workflow name and description in place

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

* feat(views): MUL-7420 trim copy on the workflows settings page

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

* fix: MUL-7420 keep pre-workflow behavior for workspaces that never adopt workflows

The run step guard now applies only to workflows that hand off, and the
standalone Statuses settings page keeps its original header.

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

* fix(views): MUL-7420 use a solid placeholder tone on the workflow header

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

* fix(workflows): keep the runtime brief cache-stable; fix project board and batch status gaps (MUL-7420)

- Move the project workflow out of the runtime brief into the per-turn
  prompt so CLAUDE.md stays byte-identical across steps and issues.
- An explicit status filter overrides the project workflow's columns; the
  hidden-columns panel lists statuses outside the workflow.
- Batch status offers only statuses every selected workflow allows; a
  partial batch rolls back, refetches and warns.
- setProjectWorkflow returns void instead of an unparsed body.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Lambda <lambda@multica.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
…ultica-ai#9294)

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
…nee or an observer (multica-ai#9293)

- issueworkflow.Brief is the only renderer: the claim carries it into every
  run's per-turn prompt; the editor preview and timeline show the same text.
- Workflow handoffs stop writing the brief into handoff_note, so a person's
  note no longer drops the brief and the handoff framing fits again.
- Agents that are not the issue's assignee get an observer brief without
  the step's commands.
- Clearer rule override and narrower wakeup guidance; user text is
  stripped of control characters.
- multica-platform skill documents project workflows.

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

Co-authored-by: multica-eve <273080382+multica-eve@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
Merge upstream tag v0.6.3 (peeled 18fa9f7)
into fork main 2b13539.

Conflict decisions:
- onboarding shim routes: drop (upstream MUL-8047 deleted the handlers);
  keep fork RequireHumanActor on cli-token and desktop-token.
- migrate cleanup map: add three issue_workflow index entries; drop the
  renumbered wakeup names the fork applies under its own names.
- feature flags: keep fork keys, add ProjectWorkflowsV1.
- run cost UI: take upstream redesign; reapply fork costYPercent guard in
  the cumulative chart.
- issue activity row: upstream handoff block plus fork wakeup wrap, links
  and test ids (CHE-1726).
- cmd_agent: keep fork runAgentEnvPatch.
- comment-only conflicts (hermes_home, actor_guards): take upstream.
- builtin skill issues.md: keep fork text, add project workflows bullet.
- generated sqlc code regenerated with v1.31.1.
- migrations lint: nine upstream/fork prefix pairs join allowedForkCollisions.
Bring in #287 (CHE-1821) and #276 (CHE-1793), merged after the sync base 2b13539.

# Conflicts:
#	server/pkg/db/generated/inbox.sql.go
#	server/pkg/db/queries/inbox.sql
errcheck on realtime write deadline and close frame, gocritic filepathJoin in gc_test. No behavior change.
…ark pings for departed members handled (CHE-1837)
…s instead of allow-listing collisions (CHE-1837)
@congvc-bot-x99 congvc-bot-x99 Bot changed the title chore(fork): sync upstream multica v0.6.3 (CHE-1837) [merge] chore(fork): sync upstream multica v0.6.3 (CHE-1835, CHE-1837) [merge] Oct 17, 2026
congvc-bot[bot] added 24 commits October 18, 2026 08:18
…tep cancel races, duplicate-run step matching, parent project moves and the race helper (CHE-1837)
… across refreshes, cancel stale runs only while pending, match duplicate runs to the write, revalidate the parent project, scope the race helper (CHE-1837)
…ed status, Jev-routed squad receipts and create-time handlers (CHE-1837)
… stranded status, settle squad receipts only for the leader's run, resolve create-time handlers under the locks (CHE-1837)
…te the editor on its loaded row (CHE-1837)
…step cancels and malformed handoff previews (CHE-1837)
…nly while the issue is still on the captured step, fail closed on malformed handoff previews (CHE-1837)
…656_dm_channel_default_off keeps its name (CHE-1837)
…cached previews, mapping selections and editor conflicts (CHE-1837)
…ff confirmations to the previewed state, keep mapping choices, gate cached previews and pin the conflicted editor version (CHE-1837)
…de-peek status, run-confirm payloads, bound no-handoff previews and stop-run overlaps (CHE-1837)
…gain useUpdateIssue, preview call carries its project argument, catalogs awaited on workflows (CHE-1837)
…esult in the cached and malformed dialog suites (CHE-1837)
…ct catalog, carry status context and the handoff through every caller, bind no-handoff previews and cancel only the runs a handoff stopped (CHE-1837)
…lete catalogs and the mobile status write (CHE-1837)
…lve the workflow catalog authoritatively, and confirm mobile status changes (CHE-1837)
…(CHE-1837)

# Conflicts:
#	packages/views/locales/en/settings.json
#	packages/views/locales/fr/settings.json
#	packages/views/locales/ja/settings.json
#	packages/views/locales/ko/settings.json
#	packages/views/locales/zh-Hans/settings.json
@congvc-dev
congvc-dev marked this pull request as ready for review October 19, 2026 18:05
@congvc-dev
congvc-dev merged commit 860e4e1 into main Oct 19, 2026
21 checks passed
@congvc-dev
congvc-dev deleted the agent/x99-claude-sonnet/bc7f808b4fad branch October 19, 2026 18:06
congvc-bot-x99 Bot pushed a commit that referenced this pull request Oct 19, 2026
…7-666 (CHE-1795)

main (PR #296, workflow catalog) now holds 657-666 under different names.
The nine push migrations keep their order and content and move to 667-675;
the concurrent-index cleanup map and the comments that name them follow. A
fresh database migrates up through 675, and 675 down to 666 and up again.
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.

10 participants