* feat(wakeups): accept blocked in --until-issue-state
Lets a coordinator wait for a specific child to reach blocked, alongside the
existing in_review handoff, without waiting for the child to close.
Exact status key match, same as in_review.
Refs #9344
Co-authored-by: multica-agent <[email protected]>
* fix(i18n): keep blocked state key in lowercase English in zh wakeup copy
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
* feat(lark): deliver reply images and files
Based on #8332 by @zprial, addressing its review of bounded delivery,
resource uploads, uncertain sends, file fallback and HTTP boundary tests.
Render bound images with the reply text in one card, pair video/audio
uploads with their message types, preserve pending-send uncertainty and
normalize Markdown destinations before matching bound attachments.
Keep runtime artifact ingestion and deployment-specific configuration
outside this change. Add transport, ownership and rendering regressions
and document the behavior in all supported languages.
* refactor(lark): scope attachment transfer slots to the patcher
The concurrency semaphore was a package-level global while the admission
and pending tiers lived on each Patcher. Move it onto the Patcher so all
three limits share one scope and tests no longer mutate global state.
MUL-8205
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* MUL-8135: feat(wakeups): wake on linked PR merge conflicts (--until-pr conflicts)
Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* MUL-8135: fix(wakeups): conflict wakes per PR identity, rotate the watch sweep
- A linked-PR condition only fires for a PR key the rule has not seen; a PR
leaving the satisfying set no longer re-wakes the agent for the others.
- Conflict keys use the PR id, so same-numbered fork PRs on one head stay
distinct.
- The conflict-watch sweep rotates an address cursor so failing fetches
cannot starve the rest.
Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* MUL-8135: fix(wakeups): count repeated linked-PR keys when deduping
Checks and merged keys carry only the PR number, so same-numbered PRs from
two repositories share a key. Compare occurrence counts so the second one
satisfying still wakes the agent.
Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Cursor <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(lark): persist and reconcile typing cleanup across replicas
- preserve terminal trigger targets and per-input cleanup ownership
- reconcile late Adds and source deletion without delaying replies
- recover interrupted ledger indexes and cover lifecycle boundaries
Refs: #8655
* fix(lark): restrict typing cleanup to the current application
- match both app operator type and the current App ID
- preserve foreign-app, human and unknown-owner reactions
- cover ownership filtering in fast and durable cleanup paths
Refs: #8655
* fix(lark): bound typing recovery and credential retention
- recover taskless indicators after lost debounce timers
- cap workspace ledger admission and redact terminal credentials
- isolate maintenance budgets and test retention and quota boundaries
Refs: #8655
* docs(lark): document typing cleanup recovery and rollout limits
- explain ownership, retention, quota and observable terminal outcomes
- document migration upgrades and safe application rollback
Refs: #8655
* fix(deps): require patched Go 1.26.9 and x/net 0.60
- Require the patched Go standard library to clear reachable vulnerabilities.
- Upgrade x/net to the matching HTTP/2 security release.
- Align local development prerequisites with the minimum Go version.
* feat(wakeups): add rule deadlines and expose the child-done system rule (MUL-7680)
Wakeups can now end: an absolute deadline (expires_at) or a relative wait
(expires_in_seconds) that restarts when the rule is re-enabled. When the
deadline passes first, the scheduler ends the rule; event rules with
on_timeout=wake run the target once with a wakeup.timeout fact. A timed-out
rule keeps disabled_at NULL so its timeout run stays claimable.
The implicit "wake the parent's assignee when a stage of sub-issues
finishes" behavior is now described by GET /api/issues/{id}/system-wakeups
and can be turned off or given a supplementary instruction per issue via
PUT /api/issues/{id}/system-wakeups/child_done. Rule reads fail open to the
previous behavior.
List responses add the creator (member or agent) and the new expiry fields.
The CLI gains --expires-in, --expires-at and --on-timeout.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(issues): let members create wakeups and show the child-done system rule (MUL-7680)
The issue sidebar's Wakeups section is always available on open issues and
gains a New wakeup popover. Members pick a condition (a time, a recurring
check with an end date, someone's reply, an agent's run ending, or raw
events), the agent to wake, the instruction, how often it fires, how long
to wait and what happens on timeout. It posts the same configuration agents
create with the CLI.
The parent's child-done wake appears as a System rule with its stage,
remaining sub-issues and target, a per-issue toggle and a supplementary
instruction. Rows show when a rule ends, timed-out rules read as such, and
details name who created the rule. Rule titles wrap to two lines instead of
truncating.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* docs(wakeups): document deadlines, member-created rules and the system rule (MUL-7680)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(wakeups): platform conditions, runaway protection and check-ins (MUL-7680)
Conditions: a wakeup can now wait for a fact the platform checks itself
(a status, assignee, label or property value; sub-issues or one stage
finishing; a linked PR's checks finishing or merging; another issue's
status). Related events only prompt an early evaluation; the rule wakes
its agent when the predicate becomes true, and a repeating rule fires
again only after it turned false or its facts changed. Finished PR checks
from before registration are ignored.
Runaway protection: repeating event rules stop after max_fires runs
(default 20), a rule pauses when its causal chain passes through it a
third time without a person in between, or when it starts 12 runs in an
hour. The reason is stored and shown.
Also: silent check-ins for every/cron checks (the run then posts no
fallback comment), wake now, delete, a per-rule run history, paused-rule
lists, timeline entries for created/triggered/timed-out/paused/check-in,
and source, paused scope, 7-day runs and child-done system rows in the
workspace list. The CLI gains --until-* conditions, --max-fires and the
trigger/delete/checkin/runs commands; the brief and wakeup prompt state
the check-in exception.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(issues): record child-done transitions in the status write (MUL-7680)
The child-done system rule ran after the status write committed and gave
up on any error, so a crash, deploy or transient failure lost the
parent's wake. A trigger now records each child's move into a closed
status in the writing transaction, for every writer. The request that
wrote it processes the rows right away and a scheduler sweep retries
anything left over; claims make the two paths process a transition
once.
The rule also follows a workspace default (settings key
system_wakeup_child_done) until an issue sets its own, the per-issue
update accepts partial bodies, and each trigger adds a timeline entry
that names its system comment.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(views): wakeup conditions, history, timeline and workspace list (MUL-7680)
Issue sidebar: members can create the platform-checked conditions (a field
reaching a value, sub-issues or a stage finishing, a linked PR's CI or merge,
another issue's status) and cap how often a repeating wait fires. Rule
details show the fire count, why the platform paused a rule, the latest runs
with silent-check notes, and offer wake now and delete.
The issue header says what the issue is waiting for ("Emacs 在等 Jiayuan 回复
+2") and opens the Wakeups section. Board and list cards use the same short
sentence, or flag a paused rule. Wakeup runs read as "由唤醒触发 · <condition>"
in the execution log, transcript and on the comments they post.
The timeline shows created, triggered, timed-out, paused and check-in entries
with the rule they came from; consecutive check-ins merge, and the child-done
entry stands in for its system comment, which it can reveal.
Automation → Issue wakeups adds a source column and filter, 7-day runs, a
paused scope with a banner, the child-done system rule on each waiting parent,
and "New wakeup" with an issue picker. Settings → Issue statuses sets the
system rule's workspace default. Copy in all five languages.
The server records the watched issue's identifier in other-issue conditions
and the rule's creator in timeline snapshots, and returns board summaries with
their condition.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* docs(wakeups): document conditions, runaway protection and check-ins (MUL-7680)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(wakeups): list system rules without a revision and localize statuses (MUL-7680)
System rule rows returned revision 0, which clients reject as a rule
revision; they now return null, and a list row with an invalid revision no
longer fails the whole page. Built-in statuses in conditions and the status
picker read in the viewer's language. Adds a browser test for a condition
created from the form, the header summary, the scheduler waking the agent
and the workspace list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(views): say why a rule paused without repeating "paused" (MUL-7680)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(i18n): use the product's task terms in wakeup copy (MUL-7680)
ja, ko and fr wakeup strings said イシュー / 이슈 / ticket; the product term
for an issue in those languages is タスク / 태스크 / tâche (and sub-tasks
accordingly), per the conventions page. French strings are rewritten for
the feminine noun.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* test(views): pin the shortcut platform in the wakeup form test (MUL-7680)
The send shortcut is primary+Enter, which is Ctrl on Linux CI runners, so
pressing Cmd+Enter only submitted on macOS.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(views): simplify the workspace wakeup list
Put the filters on one row: scope as a segmented control on the left,
source/trigger/agent and a search-as-you-type box on the right. "New
wakeup" moves to the page header, which drops the separate Search button.
Rows are single-line: the identifier sits before the title, frequency and
timezone move into the trigger tooltip, and selection, prompt edit and
the last run's transcript show on row hover or focus. The issue column
takes the remaining width and truncates, so the table fits the page.
Also fix creating from the list: the issue picker closes itself right
after reporting the selection, and that close cancelled the flow before
the form could open.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* refactor(server): run the child_done rule as a system issue wakeup
The parent-assignee wake on closed sub-issue stages was its own code path:
a system comment, a mention-style run, an override table and endpoints.
It is now an ordinary issue_wakeup row with system_rule set, sharing the
condition, receipts, runaway protection, timeline entries and run model of
people's rules.
- One children_done evaluator: a stage wakes the assignee when it closes
while a later stage waits; the wrap-up waits for every sub-issue,
unstaged ones included. People's sub-issue conditions read the same set.
- issue_child_event records closing, reopening, re-parenting and restaging
for every writer; the request processes it right after commit and the
sweep retries. User sub-issue rules become immediate too.
- The target is resolved when it fires: an agent run, a squad leader run,
an inbox notification for a member, or only the timeline entry.
- A parked (backlog) parent catches up when it leaves backlog; a waiting
run of the same agent is joined instead of queuing a second one; the
hourly limit pauses a runaway rule. No system comment is posted.
- The run gets the issue's instruction, else the workspace's, else the
built-in one, plus each stage's counts and the next stage.
- Workspace defaults move to GET/PUT /api/system-wakeups (owners and
admins) and apply to every rule nobody customized; turning a rule on
records what already holds so old facts do not fire.
- Existing open parents get their rule from a one-time backfill.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(views): workspace wakeup settings and sub-issue notifications
- Settings → Wakeups holds the sub-issue rule's workspace default and
default instruction; the toggle leaves Issue statuses.
- The issue's system rule shows who it reaches (a member is notified),
a platform pause, and the instruction it will use.
- Timeline entries say whether the assignee was woken, notified or joined
a waiting run. The folding of the old system comment is gone.
- Inbox renders the new children_done notification on web, desktop and
mobile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(server): stop wakeups from repeating their agent's runs
Runs are serialized per issue and agent, so a wakeup never ran beside
another run of its agent, but it queued behind one and repeated it. Each
rule now checks two things before it starts a run:
- Acknowledged: every input came from the agent itself. Its own comments
and issue changes never wake it, and a condition its own unfinished run
on the issue satisfied does not wake it when the platform or the agent
set the rule up. This fixes#8849: a coordinator closing its own stage is
not woken again while that run is going. A person's condition rule still
runs afterwards, since the running agent lacks its instruction.
- Merged: when a run of the agent is already waiting to start on the issue
(assignment, comment, another rule), the rule's instruction and facts
join that run (context.wakeup_joined, sent to the daemon as
wakeup_joined and rendered for every prompt kind) instead of queuing
another. Turning the rule off or replacing it withdraws what it joined.
Sub-issue changes now record the run that made them, and hints and
condition.met inputs carry those runs so a satisfied condition knows
whether the agent caused it. A rule that names the agent itself as the
actor keeps waking it on its own changes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(views): show merged and acknowledged wakeups, hint assignee comment rules
- Timeline entries say when a wakeup joined the agent's waiting run or was
the agent's own doing, for people's rules and the sub-issue rule.
- The create form tells a member that a reply or comment rule for the
issue's agent assignee joins the run a comment already starts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(server): hand waiting wakeups to a run when it is claimed
A rule that fired while its agent had a run waiting used to write its
instruction into that run right away and consume its inputs. That let a
member's rule run under another member's identity, kept deleted rules'
instructions in the run, lost the inputs when the run was cancelled or
claimed by an older daemon, and skipped the fire cap and loop check.
Now the rule only waits for a run that runs as the same person its own
run would, keeping its inputs. When a daemon advertising
joined-wakeups-v1 claims that run, JoinWaitingWakeups rechecks each rule
(on, same agent and person, creator still allowed, not the agent's own
input, no loop), consumes its inputs, counts the fire toward max_fires,
adds its chain and records the merge. A re-claim drops entries of rules
turned off or changed since. Anything else leaves the rule its inputs to
start its own run.
A child_done rule created by the backfill or by processing a change now
treats sub-issues with unprocessed close events as still open, so the
backfill no longer swallows a close waiting for the sweep.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(server): consume joined wakeup inputs only once the run starts
A claim used to consume the inputs of the rules that joined the run. If
that claim did not go through, the run went back to the queue carrying
inputs the rules no longer had: turning the sub-issue rule off on the
issue or for the workspace (which only changes enabled) did not remove
them from the next claim, and cancelling the run lost a once rule that
had already ended.
A claim now reserves the inputs for the run (receipt task_id, still
unprocessed). The rule's next dispatch settles them: a run that started
consumes them as a merged firing (once ends, max_fires counts and can
pause, timeline entry); a run that ended without starting gives them
back; one that has not started keeps them. Turning a rule off or
changing it discards its pending inputs, reserved ones included, and a
later claim of the same run rechecks every entry and drops the ones
without reserved inputs or no longer allowed to reach the run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(agents): add per-agent starter prompts
Co-authored-by: Agent Op <[email protected]>
* fix(agents): default omitted starter prompts
Co-authored-by: Agent Op <[email protected]>
* fix(migrations): renumber agent starter prompts
Co-authored-by: Agent Op <[email protected]>
* fix(chat): make starter prompt prefills reliable
Co-authored-by: Agent Op <[email protected]>
* fix(agents): preserve starter prompt writes
Keep dirty starter-prompt edits across agent refetches and fail closed when an older server cannot persist them. Clarify that copied starter prompts have no dedicated CLI override.
Co-authored-by: Agent Op <[email protected]>
* fix(migrations): avoid starter prompt prefix collision
Main now uses migration prefixes through 403, so move the starter-prompts migration to the next available prefix before requesting another review.
Co-authored-by: Agent Op <[email protected]>
* fix(agents): preserve edits after failed save
Ignore optimistic instruction snapshots while a save is pending so a rejected update can roll back the cache without erasing the user's submitted starter prompts. Cover the delayed optimistic-update and rollback sequence for both label and prompt fields.
Co-authored-by: Agent Op <[email protected]>
---------
Co-authored-by: Agent Op <[email protected]>
* docs: remove the two implementation docs under docs/
The database index audit and the Telegram channel review were one-off
implementation records for MUL-6108 and the Telegram contribution; both
are finished work and neither is referenced by the product docs. Drops
the now-dangling review pointer from the Telegram handoff notes.
docs/assets/ stays — the READMEs load the logos from it.
Co-authored-by: multica-agent <[email protected]>
* docs: remove the Telegram integration handoff notes
HANDOFF-telegram.md was the implementation record for the Telegram
channel contribution and is referenced by nothing. Operator-facing setup
lives in the localized Telegram guides under apps/docs/content/docs/.
Co-authored-by: multica-agent <[email protected]>
* docs: remove three finished implementation leftovers
- apps/mobile/docs/markdown-renderer-research.md: 2026-05 renderer
selection research and incident log. Its conclusions already live in
markdown-rendering-adr.md and in the MD_FONT/MD_LINE comments; the HIG
calibration source is folded into markdown-style.ts so the numbers keep
their rationale.
- packages/views/locales/glossary.md: a redirect stub. The content moved
to the docs-site conventions pages, which now say the file is gone
instead of claiming a stub still points at them.
- scripts/audit-redundant-indexes.sql: the MUL-6108 one-off audit query,
orphaned once its report was deleted — no caller in Makefile, check.sh
or CI.
All pointers into the deleted files are updated; nothing dangles.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Lambda <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(telegram): add Telegram channel integration
Co-authored-by: multica-agent <[email protected]>
* chore(telegram): rebase onto main and renumber migrations to 366-367
349/350 collided with main's agent_task_queue_chat_terminal_resume_index
and agent_task_queue_chat_retired_session_index. Renumber the Telegram
origin pair to the next free prefixes, update the cross-references inside
the SQL comments, and point telegram_origin_migration_test.go at the new
filenames. Contributor-facing behavior is unchanged.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: multica-agent <[email protected]>
Co-authored-by: Bohan-J <[email protected]>
* chore(assets): convert web and docs bitmaps to WebP
The repo carried 21.7MB of PNG/JPG and zero modern formats. next/image
already serves visitors AVIF/WebP, but the raw masters are still paid for
on every clone, every Docker build context, and every deploy upload.
Re-encode with cwebp and cap the source width at the largest size any
layout can request: 1920px for the decorative landing backgrounds (they
render behind content under object-cover), 2640px for the landing hero
(2x its 1320px slot), 1600px for screenshots (2x the ~720px docs and
use-case column). EXIF goes with it — the four feature backgrounds were
144dpi print exports.
Encoder choice is per image rather than uniform. Most compress best as
lossy; two of the docs screenshots are flat palettized PNGs that encode
smaller with `-lossless -z 9`, so they use that instead. Screenshots are
q80 to keep UI text crisp, decorative art q72.
apps/web/public 10.70MB -> 1.57MB
apps/docs/public 7.47MB -> 2.44MB
Verified: 1:1 crops against losslessly-downscaled references show no
visible difference; all 266 image references in the tree resolve; docs
prerenders all 177 pages and the landing/use-case pages render with no
broken images.
Co-authored-by: multica-agent <[email protected]>
* chore(assets): drop the duplicated README hero image
docs/assets/hero-board.png was byte-for-byte identical to the docs site's
workspace-overview screenshot — 633KB stored twice. Only the two READMEs
referenced the copy, so point them at the canonical file instead.
Co-authored-by: multica-agent <[email protected]>
* chore(assets): losslessly recompress app icons
oxipng -o max --strip safe over the app icon PNGs: -640KB with pixels
untouched (verified by comparing lossless-WebP encodes of the before and
after of every file — all 16 identical).
Deliberately not switched to WebP and not regenerated: the prerendered
sizes under apps/desktop/build/icons/ exist because electron-builder's
auto-derivation silently shipped only the 1024x1024 source in the v0.2.31
.deb (#2515), and the PWA manifest and .icns/.ico containers want PNG.
Filenames, dimensions, and the electron-builder config are unchanged, so
packaging behaves exactly as before.
Co-authored-by: multica-agent <[email protected]>
* ci: gate pull requests on a 300KB image budget
Keeps the previous three commits from being undone one screenshot at a
time. A bitmap added — or grown — past 300KB fails the check until the PR
description carries an `Oversized image exemption:` line saying why.
Shrinking an oversized file always passes: the check compares against the
size at the base ref, not just the absolute size.
Path-filtered to PRs that touch a bitmap, so the full-history checkout it
needs to diff against the base is only paid for when it is relevant.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Lambda <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* docs(readme): rebuild the README around a product hook and job-based features (MUL-5812)
The README had drifted badly: 1771 commits landed in three months while its
feature list last changed on 2026-05-24, and both screenshots dated to
2026-04-14 — a UI that predates the type-scale migration, the execution log
rebuild, and the issue detail rework. Whole surfaces were missing (Chat, Inbox,
Projects, usage analytics, self-hosted Git, the chat-bot integrations), the
desktop app appeared nowhere at all, and iOS was a footnote under Development.
The roster drift had a mechanism: the runtime list was repeated five times per
README, so every new provider needed ten hand edits. Four had gone missing
(deveco, grok, qwen, qwenpaw). Each README now lists it once, between markers,
and scripts/check-readme-runtimes.sh fails CI when either table drifts from
scripts/agent-cli-command-names.txt.
Structure follows what warp, block/buzz, openclaw, and hermes-agent do: the
product screenshot lands before any prose, features are grouped under job verbs
rather than flattened into one list (Notion's features page), and the docs table
is goal-oriented. Screenshots are the ones already shipping on the docs site, so
they are days old rather than months.
Moves the Multics origin story out of position two into VISION.md alongside the
vision narrative approved in MUL-4877, which had no home in the repo before.
Adds a Works today inventory that names the rough edges (iOS not on the App
Store, no Android, DingTalk community-maintained) instead of leaving readers to
find them.
README.zh-CN.md stays a 1:1 mirror and follows the Chinese glossary in
apps/docs/content/docs/developers/conventions.zh.mdx: agent as 智能体, issue as
任务, autopilot as 自动化.
Co-authored-by: multica-agent <[email protected]>
* docs(readme): fix nav paths, switch to .zh.md i18n naming, rework project status (MUL-5812)
Review feedback on #6506:
- The setup steps sent people to Settings for both Runtimes and Agents. Both
are sidebar destinations (packages/views/layout/app-sidebar.tsx: runtimes
under Configure, agents under Workspace), and the button reads "New agent".
- Renames README.zh-CN.md / VISION.zh-CN.md to README.zh.md / VISION.zh.md so
translations match the locale suffix the docs site already uses (.zh, .ja,
.ko). check-readme-runtimes.sh now discovers README.md plus README.*.md
instead of hardcoding the list, so a new locale is covered the day it lands.
- Reworks "Works today", which re-listed the features the four groups had just
covered and buried the honest part underneath. It is now "Project status": a
per-surface table saying how finished each one is, including the parts we'd
rather people hear from us (iOS off-store, no Android, DingTalk without an
SLA). No feature re-listing.
- Drops the banner. It was generic scenery that said nothing about the product
and pushed the logo, tagline, and board screenshot below the first viewport.
The board screenshot is the hook, so it now leads.
Also revises the Chinese throughout. The first pass tracked the English clause
by clause and read like a translation; this one keeps the meaning but uses
Chinese sentence rhythm, and follows the shipped UI strings — assignee 负责人,
in_review 审核中, execution log 执行日志.
Co-authored-by: multica-agent <[email protected]>
* docs(readme): regroup features around the team arc, drop project status (MUL-5812)
Co-authored-by: multica-agent <[email protected]>
* docs(readme): drop the unsuitable inline screenshots from the feature sections (MUL-5812)
Co-authored-by: multica-agent <[email protected]>
* docs(readme): make quick start cloud/desktop-first, keep CLI for self-hosting (MUL-5812)
Co-authored-by: multica-agent <[email protected]>
* docs(readme): smooth out stiff Chinese phrasing across README.zh and VISION.zh (MUL-5812)
Co-authored-by: multica-agent <[email protected]>
* docs(readme): apply translation review — glossary terms, OS metaphor, VISION naturalization (MUL-5812)
Co-authored-by: multica-agent <[email protected]>
* chore(ci): drop the readme-runtimes check (MUL-5812)
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Lambda <[email protected]>
Co-authored-by: multica-agent <[email protected]>
design.md had the same failure mode as product-overview: its most
prescriptive section restated code, and the code moved without it.
§3.2 declared "the entire project uses only three sizes — text-base /
text-sm / text-xs". Actual usage across packages/ui + packages/views:
text-sm 0, text-xs 0, text-base 0. The type system migrated to a
role-named scale (text-micro / text-caption / text-label / text-body /
text-body-lg / text-title-sm / text-title, 638 and 1131 uses for the two
commonest). tokens.css:63-85 now documents that scale, its rationale, and
the `text-sm -> text-body` migration in the file that defines it, where it
cannot drift — a strictly better version of what design.md was for.
§3.3 banned font-semibold and font-bold; the codebase has 116 and 5.
A ban nobody enforces is worse than no rule: an agent reads it, believes
it, and writes something inconsistent with 116 existing call sites.
Parts were still accurate — the interaction-state classes match button.tsx
verbatim, and the surface/radius tokens exist. But nothing referenced the
file, and the sections most likely to steer an agent's output were the
wrong ones.
Three rules that genuinely cannot be regenerated from tokens.css move into
CLAUDE.md UI Rules, which sets exactly this bar ("hard to infer from code
or easy to get wrong"): the authoritative --text-* scale pointer, the
active-must-survive-hover bug pattern, and spacing-before-divider.
docs/ is now just assets/ — the images README.md and README.zh-CN.md embed.
Co-authored-by: Lambda <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* docs: delete completed plan docs and fix stale repo documentation (MUL-5698)
Ten repo docs were verifiably out of date against the code. Completed
plans are deleted outright rather than archived — they are dead weight in
every agent's context and their decisions already live in the code.
Deleted (all describe work that has shipped):
- docs/agent-quick-create-plan.md — marked "未动工", but agenttmpl
templates (Phase 1) and the AI-create-agent page (Phase 3) are live
- docs/docs-outline.md — tracker planning "Chinese only, 25 pages"; the
doc site is 39 pages x 4 languages
- docs/docs-rewrite-plan.md — plans 55 mdx, lists webhook autopilot
triggers as unrouted; both superseded by what shipped
- docs/docs-onboarding-optimization-plan.md — work log for #5714, which
landed the four languages and screenshots it lists as pending
- docs/onboarding-refactor-plan.md — v3 shipped (welcome-store,
welcome-after-onboarding, onboarding_shim)
- apps/mobile/docs/project-v1-{plan,gap-audit}.md — marked
"pre-implementation"; the picker files they scope no longer exist
- docs/plans/* + docs/ideation/* — implementation plans for agent
access-scope (agent.visibility) and issue-table server query
(/api/issues/grouped), both shipped
Fixed:
- docs/codex-sandbox-troubleshooting.md — the decision matrix claimed
non-darwin gets workspace-write. Linux is danger-full-access (MUL-5578)
and Windows is danger-full-access (MUL-4957); Windows had no row at all
- apps/mobile/docs/rnr-migration.md — Phase 1 is complete, not "not
started"; every checklist item is in the tree
- AGENTS.md — architecture list was missing apps/mobile, apps/docs,
packages/eslint-config
- docs/ui-consistency-audit.md — §3.2 status column is a mid-PR snapshot;
#5263 and #5258 have merged
- server/internal/agenttmpl/loader.go — drop reference to a deleted doc
Co-authored-by: multica-agent <[email protected]>
* docs: reduce docs/ to design + product-overview, English by default (MUL-5698)
docs/ now holds two documents, each with an English default and a .zh.md
translation. Everything else was engineering scratch that agents load as
context on every run without ever being read by a human.
Deleted:
- docs/analytics.md, docs/feature-flags.md, docs/timezone-architecture-rfc.md,
docs/codex-sandbox-troubleshooting.md, docs/codex-usage-cache-backfill.md,
docs/custom-runtimes.md, docs/ui-consistency-audit.md
Language convention — English is the default filename, translations carry
a language suffix:
- docs/design.md (new English) + docs/design.zh.md (was design.md)
- docs/product-overview.md (new English) + docs/product-overview.zh.md
(was product-overview.md)
While translating product-overview, three facts were corrected against the
code rather than carried over from the 2026-04-21 survey: the provider list
now matches README, onboarding is the shipped three-step
about_you/workspace/runtime sequence with Helper creation moved after exit
(packages/core/onboarding/step-order.ts), and the stale "28 tables" total
was dropped.
Reference cleanup so no comment points at a deleted file — .env.example,
server/internal/analytics/{client,events}.go, packages/core/analytics/index.ts,
server/cmd/server/main.go, server/pkg/featureflag/doc.go,
server/cmd/backfill_task_usage_hourly/main.go, and the doc pointers in
migrations 100/101/103/104. Migration edits are comment-only; the runner
tracks applied versions by filename, not by checksum.
docs/assets/ is kept — README.md and README.zh-CN.md embed those images.
Co-authored-by: multica-agent <[email protected]>
* docs: drop product-overview, fix rnr body and CLAUDE/AGENTS drift (MUL-5698)
Addresses all four blockers from review.
1+2. Delete docs/product-overview.md and docs/product-overview.zh.md.
The review found the doc carried facts that would make an agent do the
wrong thing — skill injection claimed a .agent_context/skills fallback for
providers that now have native paths in execenv/context.go, and it
documented `multica skill create --title` when the CLI only registers
--name. Rather than chase those, the document goes: it is derived from
code and can be regenerated from code when it is actually wanted. That
also removes the zh-as-historical-snapshot problem, since neither
language survives.
docs/ is now design.md + design.zh.md + assets/. design.zh.md is a
faithful translation of the English, not a snapshot, so blocker 1 does
not apply to it.
3. apps/mobile/docs/rnr-migration.md: the body contradicted its own
status line. §1 asserted in present tense that there is no theming
infrastructure, hardcoded tailwind hex, and a three-line global.css;
§5.2 said the same. Both are now marked as the pre-Phase-1 baseline with
the shipped state noted, §6's Phase 0/1 checklists are backfilled as
complete, and Phase 2 is labelled not started.
4. CLAUDE.md gains apps/docs/ and packages/eslint-config/, so the
authoritative Project Shape list matches the pointer list in AGENTS.md.
The two lists are now identical.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Lambda <[email protected]>
Co-authored-by: multica-agent <[email protected]>
Complete rework of the docs site, in three passes squashed into one change:
1. P0 fact corrections across the core-loop pages (agent access model,
comment triggers and coalescing, issue lifecycle, offline/queue
semantics, task states and retries, dispatch via realtime push).
2. Chinese rewrite as the authoring language: final 12-group IA, new
"Core concepts" and "Put agents to work" pages, curated welcome page,
permanent-UI quickstart (no onboarding-wizard dependency), operator
reference restored (CLI command reference, webhook response codes,
task state/timeout tables, config set keys, upgrade notes).
3. Verification and sync: every load-bearing claim re-checked against
current main with file:line evidence (migration 103 auto-backfill,
CLI-only trigger toggling, duplication semantics, COOKIE_DOMAIN,
GitHub App variables, Qwen Code as the 17th provider); register
standards applied to all 39 pages; en/ja/ko synced from zh with
same-language heading anchors (verified, zero dead links);
17 product screenshots plus 2 diagrams wired in.
Supporting fixes: i18n middleware no longer swallows /images assets,
sidebar folder indent survives custom link padding, unused editorial
components removed.
Co-authored-by: Claude Fable 5 <[email protected]>
Adds a three-state access column/filter and a bulk "Set access scope" action to the agents list, plus a fix for a state-update loop in the bulk access dialog (parent/child callback + effect cleanup were re-triggering each other).
Co-authored-by: chouti <[email protected]>
Add xAI Grok Build (`grok`) as a first-class Multica runtime over ACP
(`grok --no-auto-update agent --always-approve stdio`), reusing
hermesClient like traecli and kimi. Includes daemon discovery,
protocol_family migration 174, model discovery, MCP passthrough,
thinking effort, frontend branding, and product docs.
Follows xAI's documented headless ACP flow: after `initialize`, read the
advertised `authMethods` and send `authenticate` (preferring xai.api_key
when XAI_API_KEY is set, else the cached login token) before any session
operation — a real, logged-in CLI rejects session/new and session/load
without it. Model discovery performs the same handshake so it returns the
live catalog instead of the static fallback.
`--no-auto-update` is passed as a global flag (and kept daemon-owned in
the blocked custom-arg set) so a background update check can't stall an
unattended ACP task. Thinking effort uses the current `--effort` flag,
and the minimum grok version is 0.2.89 (ACP + authenticate + session/load
+ session/set_model + MCP + --effort).
Closes#2895
The runtime_brief_slim feature flag has burned in; the slim runtime brief is now the sole path.
- execenv: buildMetaSkillContent / BuildCommentReplyInstructions delegate to the slim assembler unconditionally; delete the legacy verbose brief body and writeBackgroundTaskSafetyInstructions.
- Remove the runtime_brief_slim flag and the daemon-bound flag delivery subsystem built solely for it: execenv flag wiring (runtime_config_flag.go, server_snapshot_provider.go), the featureflagdispatch package, the DaemonFeatureFlagSnapshot heartbeat protocol field, and the server/daemon wiring in router.go, handler, daemon.go, main.go, cmd_daemon.go.
- Keep the generic server/pkg/featureflag engine (still used by composio_mcp_apps).
- Update tests to slim-only expectations and docs/feature-flags.md.
Co-authored-by: Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* chore(analytics): retire redundant PostHog tracking (MUL-4127)
PostHog had become a chaotic, largely-unused second copy of data we already
query from the DB and Grafana. Remove the redundant instrumentation.
Server: every product event (signup, workspace_created, issue_created,
issue_executed, chat_message_sent, team_invite_*, onboarding_*, agent_created,
cloud_waitlist_joined, feedback_submitted, contact_sales_submitted,
squad_created, autopilot_created) is now in metricsOnlyEvents, so
metrics.RecordEvent still increments the Prometheus/Grafana counter but no longer
ships to PostHog. DB rows remain the source of truth. Runtime/autopilot/
agent_task lifecycle were already Prometheus-only.
Frontend: delete the PostHog-only funnel instrumentation — $pageview (+ web and
desktop trackers), download_intent_expressed/page_viewed/initiated, the
onboarding_started mirror, onboarding_runtime_path_selected/detected,
feedback_opened, and source_backfill_*. The source-backfill modal itself stays
(it PATCHes the questionnaire to the DB).
Kept on PostHog (frontend only): $exception autocapture and the
client_crash / client_unresponsive stability telemetry (no DB equivalent), plus
$identify/$set. captureSignupSource (attribution cookie) stays — it still feeds
the signup_source Prometheus label.
Verified: pnpm typecheck, pnpm lint (0 errors), vitest (core/views/web/desktop),
go test ./internal/analytics/... ./internal/metrics/...
Co-authored-by: multica-agent <[email protected]>
* docs(analytics): fix stale PostHog references after MUL-4127 (review follow-up)
Addresses review of #4996 — three spots still described server events as active
PostHog signals after they became metrics-only:
- docs/analytics.md: issue_executed is no longer a PostHog success signal; it is
Prometheus-only (multica_issue_executed_total) + issue.first_executed_at, in
both the event contract and the Reconciliation section.
- docs/analytics.md: the signup $set_once person properties (email, signup_source)
are no longer emitted — signup is Prometheus-only; only the bucketed
signup_source survives as the multica_signup_total label.
- server/internal/metrics/business_events.go: RecordEvent doc comment no longer
claims it ships product events to PostHog / "PostHog is reserved for
user/product-behaviour events" — every server event is now metrics-only.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: J <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(analytics): anonymous self-host onboarding source beacon (MUL-3708)
Production self-host servers now report the anonymous onboarding "how did
you hear about us" channel to Multica's public write-only ingest, so the
self-host source distribution becomes visible alongside official cloud.
Official cloud keeps its existing PostHog capture unchanged; this is a
submit-time beacon, not a background telemetry pipeline.
- server/internal/sourcebeacon: ShouldSend gate (production + non-local +
non-*.multica.ai app host, fail-closed — judged by the app/frontend host,
not the backend URL, which official often leaves unset), per-instance
salted hashing, deterministic event uuid, fire-and-forget sender.
- POST /api/telemetry/self-host-source: public, write-only, per-IP
rate-limited, 4 KiB body cap, channel allowlist, strict unknown-field
rejection. Lands in PostHog as self_host_source_channel with a
deterministic uuid (best-effort dedup), $process_person_profile=false,
and deployment=self_host — a distinct event name so it never pollutes the
official onboarding funnel.
- Hook in PatchOnboarding fires once when the source is first set; never
blocks onboarding. Only channel enum(s) + two per-instance hashes leave
the box — never user_id/email/name/workspace/org/domain/role/use_case/the
source_other free-text/IP.
- migration 128: system_settings singleton holding instance_salt.
- frontend: self-host-only anonymous-collection notice on the source step,
gated by a new /api/config self_host_source_notice flag (en/zh-Hans/ko/ja).
- analytics.Event gains an optional top-level uuid; docs/analytics.md,
SELF_HOSTING.md and .env.example document exactly what is/isn't sent and
how to disable it (ANALYTICS_DISABLED). Also fixes the long-standing
team_size→source drift in docs/analytics.md.
Verified locally: go build/vet, go test (sourcebeacon, analytics, handler),
pnpm typecheck (all packages), locale parity (157), step-source (6) + core
config/schema (69) vitest, lint (0 errors).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* fix(analytics): wire self-host source beacon through metrics, guard nil pool (MUL-3708)
Addresses Howard CI blockers on #4691 (no product-direction change):
- loadInstanceSalt returns "" on nil pool; salt is only loaded when
ShouldSendFromEnv() is true, via a bounded (5s) context — restores the
"router constructible without a DB" invariant (nil-pool routing tests).
- Add multica_self_host_source_channel_total counter (by source) + an
IncForEvent case, so every analytics event is paired with a Prometheus
counter. NormalizeSourceChannel reuses sourcebeacon allowlist (no 3rd copy).
- Beacon handler now builds the event via the analytics.SelfHostSourceChannel
helper and ships it through obsmetrics.RecordEvent (no naked Capture); not
IsMetricsOnly, so it still reaches PostHog.
- Prime the new family in the registry-families test.
Verified: go build/vet, go test ./internal/metrics ./internal/sourcebeacon
./internal/handler ./cmd/server (incl. the 3 named blockers + registry +
record-event-helper lints) all green.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(featureflag): framework-level feature flag system (MUL-3615)
Introduces a reusable feature flag framework so future features can adopt
flags without writing infrastructure code.
Backend: server/pkg/featureflag (Go)
- Service / Provider / Decision separation per Martin Fowler's Toggle
Point / Toggle Router / Toggle Configuration pattern.
- Providers: StaticProvider (rules in source control), EnvProvider
(FF_<KEY> overrides for ops kill switches), ChainProvider
(first-hit-wins composition).
- EvalContext carried through context.Context with WithEvalContext /
EvalContextFrom; supports user_id, workspace_id, free-form attributes.
- PercentRollout via deterministic FNV-1a bucketing; same user always
lands in the same bucket so experiments do not flap between requests.
- Nil-safe Service: a nil *Service or missing flag returns the caller's
default so business code never panics on a missing flag.
- 100% unit-test coverage with -race; go vet clean.
Frontend: packages/core/feature-flags (TypeScript)
- Same vocabulary as the Go side (Decision, EvalContext, Rule,
PercentRollout). FNV-1a parity ensures cross-tier bucket agreement.
- FeatureFlagService + StaticProvider + ChainProvider in pure TS.
- React glue: FeatureFlagsProvider, useFlag(key, default),
useVariant(key, default). Hooks fall back to the default when no
provider is mounted so Storybook / unit tests stay simple.
- Vitest tests for service, providers, hash, and React hooks.
Docs: docs/feature-flags.md — wiring, EvalContext, toggle points,
backend-protection note, and the standard best-practice checklist.
The framework intentionally has no third-party Go deps and no API
surface beyond what real callers will need. New providers (DB, remote
config, LaunchDarkly) plug in by implementing Provider; no existing
caller has to change.
Co-authored-by: multica-agent <[email protected]>
* fix(featureflag): cross-tier hash parity + variant only when enabled (MUL-3615)
Two must-fix issues from the PR review on #4496:
1. TS hash had a trailing zero separator that Go did not emit, so the
same (key, identifier) bucketed differently on the two tiers. The
"user lands in the same bucket on server and client" promise was
broken. For example billing_new_invoice/user-42 was bucket 97 in Go
and bucket 11 in TS.
Fix: TS fnv1a now emits the zero separator BETWEEN parts only, never
after the last one, matching Go's hash.Write byte stream exactly.
Verified by parallel golden tests on both sides that pin five
(key, identifier) -> bucket triples; if either side drifts both tests
fail and one must be brought back in sync.
2. StaticProvider returned `Rule.Variant` regardless of whether the rule
evaluated to enabled=true. A 0%-rollout user, a deny-listed user, or
a default-off user would see variant="experiment-v2", so callers
branching on Variant() would route control users into the experiment
arm.
Fix: Rule.Variant is now the ON-variant only. When the rule evaluates
to enabled=false the Decision's variant is the canonical "off",
regardless of what Rule.Variant says. Documented as a behavior
contract in the Rule godoc / JSDoc and covered by regression tests
on both sides.
Tests: - go test -race ./pkg/featureflag/... : all green (1.58s).
- pnpm --filter @multica/core test : 661/661 (3 new).
- pnpm --filter @multica/core typecheck: clean.
Co-authored-by: multica-agent <[email protected]>
* fix(featureflag): hash UTF-8 bytes on the TS side for cross-tier parity (MUL-3615)
Follow-up review on PR #4496 caught that the previous hash fix was only
correct for ASCII input. The TS side used `charCodeAt`, which returns
UTF-16 code units, while the Go side hashes the UTF-8 byte
representation. Any non-ASCII flag key or identifier — Chinese flag
names, accented user IDs, emoji — would bucket differently on backend
vs frontend, silently breaking the "same user, same bucket" promise the
PR description makes.
Concretely:
flag/é Go 53 vs TS-old 68
flag/🦄 Go 82 vs TS-old 75
实验/user-1 Go 90 vs TS-old 4
flag/用户-1 Go 95 vs TS-old 2
Fix: replace per-char charCodeAt with a module-level `TextEncoder`
('utf-8') and hash each encoded byte. After the fix all four cases above
match Go exactly, and the existing ASCII cases continue to match.
The cross-language golden tables on both sides now include the 5 new
non-ASCII cases alongside the 5 ASCII cases, so any future regression
that swaps UTF-8 for charCodeAt (or vice versa) will fail loudly on
both Go and TS simultaneously.
TextEncoder is part of WHATWG Encoding and is available in every
evergreen browser, in Node 11+, and in Hermes (React Native) >= 0.74,
which covers every runtime that imports @multica/core/feature-flags.
Tests: - go test -race ./pkg/featureflag/... : all green.
- pnpm --filter @multica/core test : 661/661.
- pnpm --filter @multica/core typecheck : clean.
Co-authored-by: multica-agent <[email protected]>
* feat(featureflag): wire into main app config — YAML file + env override (MUL-3615)
Follow-up requested by Yushen on PR #4496: make the feature flag
framework configurable through the existing main-program config system
instead of requiring Go code edits. multica's main app is purely env-var
driven (see .env.example) with optional MULTICA_*_FILE knobs for richer
config; feature flags now follow the same pattern.
server/pkg/featureflag/config.go
- LoadRulesFromYAMLFile(path) parses a YAML rule set into runtime
Rule structs. Empty files are a valid "no flags yet" state; missing
or malformed files surface a hard error so operators see misconfig
the same way DATABASE_URL parse errors do.
- NewServiceFromEnv composes the standard provider chain:
1. EnvProvider("FF_") (runtime kill-switch path)
2. StaticProvider from YAML file (declarative rule set)
When MULTICA_FEATURE_FLAGS_FILE is unset, only the env layer is
active and every IsEnabled call falls through to the caller's
default, so the server can boot before any flag is authored.
server/cmd/server/main.go
- Construct the Service once at startup right after env-var warnings,
fail loudly on malformed YAML, log the loaded rule count via the
Service logger. The Service is held in a local `flags` variable
ready to be threaded into handler.Handler / service constructors
when the first flag user lands. Threading is deferred to the PR
that adds the first business consumer so this PR stays a pure
framework + config layer.
.env.example
- New "Feature flags" section documents MULTICA_FEATURE_FLAGS_FILE and
the FF_<KEY> override convention, with a minimal YAML schema example
inline.
docs/feature-flags.md
- Replace the "build a provider manually" example with the
NewServiceFromEnv pattern that now matches what main.go actually
does. Show the YAML schema in one place. Note the on-variant /
off semantics from the previous review round.
server/pkg/featureflag/doc.go
- Update package doc to mention the gopkg.in/yaml.v3 dependency
(already a server-level dep) instead of the now-inaccurate
"no third-party dependencies" claim.
Tests: - go test -race -count=1 ./pkg/featureflag/... all green; new
config_test.go covers: simple YAML, full-shape YAML, empty file,
missing file, malformed YAML, no env var, file-only, env-beats-file,
bad file surfaces error.
- go test -race -count=1 -run TestHealth ./cmd/server/... sanity
check that the main.go boot path with the new wiring still passes.
- go vet ./... clean.
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Eve <[email protected]>
Co-authored-by: multica-agent <[email protected]>
* feat(agent): Qoder ACP runtime, chat reconnect recovery, and task linkage
- Add Qoder CLI backend (ACP transport, model discovery, blocked-args policy)
- Wire daemon/runtime config, docs, and UI provider assets
- Retry terminal task reports; add backoff unit tests
- Chat: SQL attach user message to task; handler + optimistic cache reconcile
- Invalidate chat/task-messages caches on WS reconnect; extract helper + tests
Co-authored-by: Orca <[email protected]>
Co-authored-by: Cursor <[email protected]>
* chore: drop non-Qoder changes (chat reconnect, task link, terminal report retries)
Keep only Qoder runtime, docs, daemon config/execenv, and UI provider assets.
Co-authored-by: Orca <[email protected]>
Co-authored-by: Cursor <[email protected]>
* fix(agent): harden Qoder ACP drain and wire project skills path
- Stop streaming to msgCh after reader wait so grace timeout cannot race close
- Resolve injected skills to .qoder/skills per Qoder CLI discovery
- Update AGENTS.md skill copy and add execenv tests
Co-authored-by: Orca <[email protected]>
Co-authored-by: Cursor <[email protected]>
* feat(qoder): add provider logo and wire MCP config into ACP sessions
- Add inline SVG QoderLogo component to provider-logo.tsx, replacing
the generic Monitor icon placeholder
- Add convertMcpConfigForACP helper to convert Claude-style MCP server
config (object map) into ACP array format for session/new and
session/resume
- Add unit tests for convertMcpConfigForACP covering stdio, SSE,
empty/nil, and multi-server cases
Co-authored-by: Orca <[email protected]>
* fix(test): capture both return values from InjectRuntimeConfig in Qoder test
Co-authored-by: Orca <[email protected]>
* fix(qoder): preserve remote MCP headers and promote provider errors
Addresses review feedback on #2461 (Bohan-J): two runtime-correctness
issues in the Qoder ACP backend.
1. Remote MCP headers were dropped. The bespoke convertMcpConfigForACP
only forwarded url/type, so an authenticated remote MCP server looked
configured in Multica but failed inside the Qoder session. Replace it
with the shared buildACPMcpServers helper (same path Hermes/Kimi/Kiro
use), which preserves headers as [{name, value}], sorts for
deterministic output, and handles remote transport aliases. Fail
closed on malformed mcp_config instead of silently dropping servers.
2. Provider failures could report as completed tasks. stderr was wired
via io.MultiWriter and the result was only promoted to failed when
output was empty, so a terminal upstream error (HTTP 429 / expired
token) racing a stopReason=end_turn with text still became
"completed". Switch to StderrPipe + an explicit copier, drain it
(bounded by the existing grace window, since qodercli can leave a
child holding the inherited fds) before the decision, and run the
shared promoteACPResultOnProviderError.
Tests: replace the convertMcpConfigForACP unit tests with two
end-to-end Qoder tests — one asserts the Authorization header reaches
the session/new payload as {name, value}, the other asserts a terminal
stderr error with non-empty output reports failed.
Co-authored-by: Orca <[email protected]>
* fix(qoder): align ACP session handling
Co-authored-by: Orca <[email protected]>
* fix(agent): guard qoder late output after drain
Co-authored-by: multica-agent <[email protected]>
---------
Co-authored-by: Orca <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: J <[email protected]>
Co-authored-by: multica-agent <[email protected]>
Adds a dry-run-first operator command for historical Codex usage cache correction, packages it in the backend image, documents the operator-job flow, and covers execution with DB-backed tests.
capturePageview now section-normalizes the path (strip query/hash, collapse
UUID and issue-key resource segments) and dedupes consecutive same-section
views, so navigating between issues/agents/etc. no longer fires a billed
PostHog event per resource. The web tracker keys on pathname only (not
searchParams), removing ~17% pure query-string-churn pageviews and keeping
OAuth code/state out of $current_url.
MUL-3081
Co-authored-by: J <[email protected]>
Co-authored-by: multica-agent <[email protected]>