mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-09 01:46:46 +08:00
d276991c603e6041d20fb5c1e65df8756d35d2ae
139
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3b1018e9c9 |
Revert "MUL-6016: feat(task): retry exact Codex capacity failures (#6747)" (#7586)
This reverts commit
|
||
|
|
8fda4f22d5 |
MUL-6016: feat(task): retry exact Codex capacity failures (#6747)
* docs: record Codex capacity retry design * docs: add Codex capacity retry implementation plan * fix(taskfailure): recognize exact Codex capacity error * feat(task): retry exact Codex capacity failures * feat(server): configure Codex capacity retries * docs: expose Codex capacity retry setting * fix(task): align capacity retry comments --------- Co-authored-by: cuijs <[email protected]> Co-authored-by: Claude <[email protected]> |
||
|
|
f8ec870f30 |
MUL-6629: feat(agents): add per-agent starter prompts for chat (#7261)
* 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]> |
||
|
|
814ee0cbf7 |
docs: remove six finished implementation records (#7258)
* 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]> |
||
|
|
67c6aa05c4 |
MUL-6166: feat(telegram): add Telegram channel integration (#6944)
* 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]> |
||
|
|
801c671973 |
chore(assets): convert bitmaps to WebP, dedupe, and gate image size (MUL-6352) (#7149)
* 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]> |
||
|
|
cb8ce085ab |
MUL-6108: 清理首批冗余数据库索引 (#6878)
* perf(db): drop redundant indexes (MUL-6108) Co-authored-by: multica-agent <[email protected]> * fix(db): harden concurrent rollback retries (MUL-6108) Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> Co-authored-by: Sol-Boy <[email protected]> |
||
|
|
20a4a8da87 |
docs(readme): rebuild the README around a product hook and job-based features (MUL-5812) (#6506)
* 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]> |
||
|
|
6f14c3004a |
docs: delete design.md, fold its non-derivable rules into CLAUDE.md (MUL-5698) (#6375)
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]>
|
||
|
|
c1ec1f4646 |
docs: cut docs/ to design + product-overview, English by default (MUL-5698) (#6364)
* 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]> |
||
|
|
e4b6f7a31b |
MUL-5581: add Qoder CN CLI runtime (#6232)
* feat(agent): add Qoder CN CLI runtime Co-authored-by: multica-agent <[email protected]> * fix(agent): address Qoder CN review nits Co-authored-by: multica-agent <[email protected]> * fix(agent): defer Qoder CN version gate Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
51f44873cc |
fix(labels): always enable resource labels (MUL-5563) (#6225)
* fix(labels): always enable resource labels (MUL-5563) Co-authored-by: multica-agent <[email protected]> * docs(labels): clarify resource label rollback safety (MUL-5563) Co-authored-by: multica-agent <[email protected]> * docs(labels): correct compat client range (MUL-5563) Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
e395fb8744 |
docs: four-language documentation overhaul with verified facts and product screenshots (MUL-5057) (#5714)
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]> |
||
|
|
8065cead85 |
MUL-5198: Restore server-backed issue table grouping (MUL-5100) (#5817)
* revert(issues): restore server-backed table grouping Co-authored-by: multica-agent <[email protected]> * test(skills): stabilize import completion coverage Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
4dc47ef113 |
Revert "MUL-5100: Move issue table grouping to the server" (#5777)
This reverts commit
|
||
|
|
216aee5629 |
[MUL-5125] Add daily Desktop/Web usage and runtime reporting (#5763)
* feat(analytics): add daily client usage reporting (MUL-5125) Co-authored-by: multica-agent <[email protected]> * fix(analytics): clarify daily usage semantics (MUL-5125) Co-authored-by: multica-agent <[email protected]> * fix(analytics): resolve usage review blockers (MUL-5125) Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
d43e500ff6 |
MUL-5100: Move issue table grouping to the server
Merge approved after review; CI checks are green. |
||
|
|
1483ce0825 |
fix(agents): always enable AI creation (MUL-4998) (#5660)
Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
b31132edf3 |
feat(agents): add access-scope column, filter, and bulk edit to agents list (#5393)
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]> |
||
|
|
06d79c1750 |
feat(agent): add Grok Build CLI as an ACP runtime (#5285)
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 |
||
|
|
6c6143e8fc |
fix(agents): always enable skill toggles (MUL-4520) (#5381)
Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
411a160b99 |
fix(release): harden v0.3.44 migrations (#5345)
Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
5541819f98 |
refactor(ui): unify collection page patterns (#5258)
* refactor(ui): unify collection page patterns * refactor(agents): redesign agent detail workbench (#5263) * refactor(agents): redesign agent detail workbench * refactor(agents): refine capability and settings surfaces * refactor(agents): rebuild general settings form * refactor(agents): refine overview and settings * refactor(agents): redesign custom args editor * docs(ui): record consistency audit |
||
|
|
4efcfb96e3 | feat(ui): establish surface system (#5248) | ||
|
|
0c2e48ded2 |
refactor: retire FF_RUNTIME_BRIEF_SLIM, make slim runtime brief the only path (MUL-4297)
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]> |
||
|
|
3cb5dc3ad6 |
chore(analytics): retire redundant PostHog tracking (MUL-4127) (#4996)
* 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]> |
||
|
|
7b3cad664b |
revert(self-host): remove source channel reporting (#4799)
* Revert "test(onboarding): cover official source reporting controls (#4782)" This reverts commit |
||
|
|
26142d74aa |
feat(self-host): collect anonymous source channels mul-3878 (#4741)
* feat(self-host): collect anonymous source channels * feat(self-host): include other source text * feat(self-host): report source channel domains * feat(self-host): add source domain reporting control * fix(self-host): simplify source reporting copy * fix(self-host): simplify source channel reporting gate * fix(self-host): limit source reporting triggers * chore(self-host): point source reporting at staging |
||
|
|
b336f07617 |
Revert "feat(analytics): anonymous self-host onboarding source beacon (MUL-37…" (#4712)
This reverts commit
|
||
|
|
63eb6f73ad |
feat(analytics): anonymous self-host onboarding source beacon (MUL-3708) (#4691)
* 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]> |
||
|
|
1ac3a03e5d |
MUL-3618: dispatch daemon feature flag snapshots (#4509)
* MUL-3618: dispatch daemon feature flag snapshots Co-authored-by: multica-agent <[email protected]> * MUL-3618: narrow daemon flag snapshots to process scope Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
4a8210912a |
feat(featureflag): framework-level feature flag system (MUL-3615) (#4496)
* 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]> |
||
|
|
12ea1f6a8c |
MUL-3495: support custom runtime args and registration errors (#4408)
* feat: support custom runtime args Co-authored-by: multica-agent <[email protected]> * fix: address custom runtime review nits Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
78342a39ce |
MUL-3305: feat(agent): add qoder CLI as a choice of agent provider. (#2461)
* 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]> |
||
|
|
2d1313c5a4 |
MUL-3264: add Codex usage cache backfill
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. |
||
|
|
8ba1ef2dce |
MUL-3319: Update Codex and Cursor resume docs
Update provider documentation to reflect working Codex and Cursor session resumption. |
||
|
|
659ac2d99a |
Revert "docs: add db-backed scheduler RFC (#3701)" (#3831)
This reverts commit
|
||
|
|
2e50df9a6a |
perf(analytics): report $pageview at section granularity, drop web query-string churn (#3813)
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]> |
||
|
|
23a69f7682 |
docs: add db-backed scheduler RFC (#3701)
Co-authored-by: Eve <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
b92c3fbc93 |
chore(analytics): stop shipping operational events to PostHog (MUL-2967) (#3720)
* chore(analytics): stop shipping operational events to PostHog (MUL-2967) Operational / execution-lifecycle telemetry dominated PostHog event volume and drove the bill: runtime_offline alone was ~54% of ~22.6M events/mo, and ~99% of events were billed at the higher identified-event rate. These signals already have Prometheus counters (Grafana), so the PostHog copies were redundant cost. - Add analytics.IsMetricsOnly; metrics.RecordEvent now skips the PostHog Capture for runtime_* and autopilot_run_* while still incrementing their Prometheus counter (their analytics.Event constructors are retained to feed the metric label set via IncForEvent). - Remove the agent_task_* PostHog path entirely: drop captureTaskEvent and the AgentTask* constructors/constants. Their Prometheus side is unchanged via the typed BusinessMetrics.RecordTask* methods. Also remove the now-dead taskDurationMS / willRetryTask helpers. - Update the pairing lint test (no agent_task allow-list, no naked-Capture exception), add a RecordEvent skip test + IsMetricsOnly test, and update docs/analytics.md (taxonomy, per-event banners, reconciliation). Product/funnel events (signup, onboarding, issue_created, issue_executed, chat_message_sent, agent_created, autopilot_created, etc.) are unchanged and still ship to PostHog. Co-authored-by: multica-agent <[email protected]> * docs(analytics): correct agent_task Prometheus metric contract (MUL-2967) Address PR review: the agent_task_* "Prometheus-only" banner claimed the old PostHog event properties (task_id, agent_id, duration_ms, error_type, will_retry, ...) were the metric label set. They are not — the real labels are only source/runtime_mode/provider/terminal_status/failure_reason. - Replace the agent_task_* sections with the actual metric names and labels (multica_agent_task_*; see business.go / labels.go), and explain that completed/failed/cancelled are terminal_status values on multica_agent_task_terminal_total, with wall-clock in the *_seconds histograms. - Tighten the runtime_*/autopilot_run_* banners so id properties aren't mistaken for labels. - Drop the stale AgentTask allow-list reference from the pairing lint test header comment. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: J <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
7984606eed |
feat(landing): add Contact Sales page and inquiry endpoint (MUL-2493) (#2988)
* feat(landing): add Contact Sales page and inquiry endpoint (MUL-2493) Adds a public `/contact-sales` marketing page with a needs-discovery form modelled on the design reference attached to MUL-2493 — first/last name, business email (with free-provider rejection), company name + size, country/region, intended use case, and a free-text goals field, plus the two consent checkboxes from the reference. Submissions hit a new public `POST /api/contact-sales` endpoint with per-IP rate limiting (Redis-backed via the existing RateLimit middleware, configurable through `RATE_LIMIT_CONTACT_SALES`) and a per-email hourly cap so a single business address can't be used as a flood channel after one valid pass. The inquiry is stored in a new `contact_sales_inquiry` table; analytics fires a `contact_sales_submitted` PostHog event with only the closed-enum dimensions (size, country, use case) — the free-text goals stay in the DB and are never broadcast. The page is linked from the landing header (md+) and the footer's Company column, in both English and Simplified Chinese. The reserved-slug list is updated so a workspace named `contact-sales` can't shadow the route. Co-authored-by: multica-agent <[email protected]> * fix(landing): canonicalize business email and tighten contact-sales form (MUL-2493) - Parse the submitted email with net/mail and run the free-email block-list against the canonical addr.Address, so a display-name form like `Ada <[email protected]>` can no longer slip past the gate (the raw string had domain `gmail.com>`, which wasn't blocked). Adds regression tests covering the display-name bypass and the canonicalization helper. - Drop noValidate from the contact-sales form so the browser's native required / email / select checks fire before submit; the JS-side free-email warning still runs as a UX guard. - Update success copy ("respond within three business days") in EN and ZH plus the page metadata. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: multica-agent <[email protected]> |
||
|
|
fbd965e5bf |
feat(onboarding): v3 — thin server, frontend-orchestrated welcome (#3008)
* feat(onboarding): Multica Helper as general workspace assistant + blocking modal
Reshape Multica Helper from an onboarding-only guide into the workspace's
general-purpose AI assistant. The agent's permanent identity (injected as
`## Agent Identity` into every task's CLAUDE.md / AGENTS.md / GEMINI.md
via execenv.InjectRuntimeConfig) is rewritten to three sections that don't
overlap with what the brief already provides:
- Who I am (built-in workspace assistant, not onboarding-only)
- What Multica is + docs/source/issues URLs as knowledge sources
- What I can do (CLI = manifest, `multica --help` is the source of truth)
- Tone (concise, like a colleague, match user's language)
Bootstrap moves out of the in-flow Step 4. Runtime step now exits the
onboarding shell with no bootstrap call; a blocking OnboardingHelperModal
mounts inside the workspace layout (web + desktop) and gates purely on
`me.onboarded_at == null`. The user picks one of three starter prompts
(intro / assign / second_agent) and the modal calls
BootstrapOnboardingRuntime with a new optional `starter_prompt` field that
becomes the seeded onboarding issue's description.
Side effects required to make `onboarded_at == null` an honest signal:
- CreateWorkspace no longer marks onboarded (was atomic with CreateMember).
The "member exists ⟹ onboarded_at != null" invariant is intentionally
broken; guards (useDashboardGuard / desktop App.tsx) already tolerate
this — comments updated to reflect the new contract.
- AcceptInvitation still marks (invitee skips the modal in someone
else's workspace). Code comment added warning future removers.
- resolvePostAuthDestination flips to workspace-presence-first: a user
with a workspace lands in it regardless of `onboarded_at`, so the
modal can pick up an interrupted setup on relogin.
Other backend changes:
- `onboardingAssistantDescription` rewritten ("Built-in workspace assistant…")
- `onboardingAssistantInstructions` rewritten to the 3-section identity
- `bootstrapOnboardingRuntimeRequest.StarterPrompt` (optional, 2 KiB rune
cap, empty-falls-back-to onboardingIssueDescription)
Frontend changes:
- Delete `packages/views/onboarding/steps/step-teammate.tsx` (no longer a
persisted step)
- `ONBOARDING_STEP_ORDER` and `OnboardingStep` type drop `"teammate"`
- `handleRuntimeNext` exits via `onComplete(workspace, undefined)` — no
bootstrap, `onboarded_at` stays NULL so the modal fires
- Runtime step next-button copy → "Start exploring" / "开始探索"
- New `packages/views/workspace/onboarding-helper-modal.tsx`:
Base UI Dialog, dismissible=false, three localized cards, mutation
invalidates agents + issues queries then navigates to the seeded issue
- Mounted in both `apps/web/app/[workspaceSlug]/layout.tsx` and
`apps/desktop/src/renderer/src/components/workspace-route-layout.tsx`
Tests:
- Backend: TestBootstrapOnboardingRuntime_{With,No}StarterPrompt and
TestCreateWorkspace_DoesNotMarkOnboarded
- Frontend: onboarding-helper-modal.test.tsx covers all four gating
conditions, three-card behavior, mutation pending state, and the
"no close button" invariant
Compatibility:
- Already-onboarded users: zero impact (modal can't fire)
- Invitees: AcceptInvitation still marks → modal can't fire
- Skip-runtime path: BootstrapOnboardingNoRuntime still marks → modal can't fire
- Old desktop / web clients: legacy teammate-step path keeps working
(bootstrap accepts missing starter_prompt) — the new modal only fires
on the new frontend bundle
- Avatar SVG kept (asterisk variant) — no migration of existing Helper
agents, only newly-created Helpers pick up the new instructions/description
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fix(desktop): suppress OnboardingHelperModal while a WindowOverlay is open
On desktop, App.tsx auto-creates a tab pointing at the user's first
workspace as soon as workspaces.length flips from 0 → 1 (during onboarding
Step 2). The new tab mounts WorkspaceRouteLayout under the overlay,
which mounts OnboardingHelperModal. The modal's Portal renders to
document.body — appearing AFTER the WindowOverlay in DOM order, so its
z-50 wins and the modal floats in front of the still-active onboarding
Step 3 (runtime).
Suppress the modal whenever any WindowOverlay is active. When the overlay
closes (onComplete fires after the user finishes onboarding), the modal
re-evaluates `me.onboarded_at == null` and pops on its own.
Web is unaffected (onboarding flow lives at /onboarding, not under
/[workspaceSlug]/, so WorkspaceRouteLayout never mounts during the
onboarding flow).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* docs(onboarding): add v2 refactor plan
Captures the design + 8-step implementation order for collapsing the
onboarding state machine: single mark-onboarded entry point, persisted
Step 3 user choice, dumb Modal, single install-runtime seed call site.
Includes old-user compatibility analysis (4 existing gates) and per-PR
risk/rollback.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(db): persist Step 3 runtime choice on user record (MUL-onboarding-v2)
Adds onboarding_runtime_id UUID NULL + onboarding_runtime_skipped BOOLEAN
columns to "user" and the CHECK constraint enforcing the 3-state machine
(unset / picked-runtime / explicit-skip; the fourth combination is
forbidden). ON DELETE SET NULL on the FK so a deleted runtime degrades
to "unset" rather than dangling.
PatchUserOnboarding gains the two narg fields plus CASE expressions that
collapse the runtime/skipped pair atomically — a follow-up PATCH that
flips one side now clears the other in the same statement, instead of
preserving it via per-field COALESCE and tripping the CHECK constraint.
Backwards compatible for existing users: both new fields default to
(NULL, false), which is the "unset" leaf of the state machine, and four
upstream gates on me.onboarded_at != null already short-circuit the
new fields' readers for everyone who's already onboarded.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* refactor(server): collapse onboarding side effects to service layer
Introduces OnboardingService.MarkComplete and
WorkspaceContentService.{Ensure,Seed}InstallRuntimeIssue as the single
authorities for the two onboarding side effects that used to be
duplicated across four handlers:
- MarkUserOnboarded + claim starter_content_state +
optional install-runtime fallback seed: was inline in
BootstrapOnboardingRuntime, BootstrapOnboardingNoRuntime,
AcceptInvitation, and CompleteOnboarding.
- install-runtime issue seeding: was inline in CreateWorkspace and
AcceptInvitation as a "no runtime yet" fallback.
After this refactor:
- MarkUserOnboarded is called from exactly one place (the service).
- install-runtime issue is seeded from exactly one place (the service).
- CreateWorkspace deliberately does not seed — the new
/ensure-onboarding-content endpoint (also added here) lets the
workspace-entry init component request the seed on first mount, so
workspaces created but never opened don't accumulate stale issues.
- The PatchOnboarding handler now accepts the new runtime_id /
runtime_skipped fields and rejects (uuid, skipped=true) up front.
- UserResponse exposes the two new persisted fields so the frontend
can read them off `me` without an extra round-trip.
Handler-side tests added: TestPatchOnboarding_RuntimeChoiceSwitch (the
explicit cross-request switch path that the original COALESCE design
would have 500'd on) + TestPatchOnboarding_PreserveUntouched.
Old handler-local file no_runtime_issue.go is deleted; its content
moved to service/workspace_content.go with the helpers exported.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(core): API + types for persisted onboarding runtime choice
User type / Zod schema gain onboarding_runtime_id (string | null) and
onboarding_runtime_skipped (boolean); EMPTY_USER + test fixture updated
to match. api.patchOnboarding accepts the new optional fields and the
new api.ensureOnboardingContent endpoint is wired so the workspace
shell can request the fallback seed.
Two new store helpers — recordOnboardingRuntimeChoice(runtimeId) and
recordOnboardingRuntimeSkipped() — replace the prior pattern of
Step 3 calling bootstrap directly. They PATCH the user's choice, sync
the auth store, and return. Mutually exclusive on the server side via
the CHECK constraint; the client just ships one intent at a time.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(workspace): WorkspaceOnboardingInit single decision point + dumb Modal
Replaces OnboardingHelperModal's self-gating render path with a 4-branch
dispatcher that runs once on workspace-shell mount:
branch 0 me.onboarded_at != null → ensure install-runtime issue
fallback, render nothing
branch 1 me.onboarding_runtime_skipped → SkipBootstrapping component:
loading veil → bootstrap →
navigate. On failure shows
a Retry UI instead of
silently freezing the veil
branch 2 me.onboarding_runtime_id → render Modal with the
runtime id from `me` (no
internal list query)
branch 3 (none of the above) → useEffect navigate back to
/onboarding so the user
walks Step 3 again
The Modal itself is now a dumb component — receives `workspace` and
`runtimeId` as props, no internal gates, no runtimeListOptions query.
Tests rewritten to cover the props-driven render + pick-card paths;
the prior gating tests move into the new
workspace-onboarding-init.test.tsx alongside the M2 retry-on-failure
behaviour.
Mounted in both apps/web/app/[workspaceSlug]/layout.tsx and the desktop
workspace-route-layout. Desktop keeps its `!overlayActive` suppression
guard so the init doesn't portal-jump in front of an active
WindowOverlay.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(onboarding): Step 3 records user choice instead of calling bootstrap
handleRuntimeNext now PATCHes the user's pick (recordOnboardingRuntime
{Choice,Skipped}) and navigates straight into the workspace shell. The
workspace-entry WorkspaceOnboardingInit reads the persisted choice off
`me` and runs the appropriate branch — Step 3 is pure intent capture
with zero side effects on its own.
PATCH must succeed before navigation: if it fails the user stays on
Step 3 with a toast, because navigating with no persisted intent would
land them in WorkspaceOnboardingInit's branch 3 "no decision yet" rescue
and trigger a redirect loop back to /onboarding.
The prior asymmetry (Connect deferred bootstrap to the workspace, Skip
ran bootstrap inline) is gone — both paths defer to the workspace
shell now.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(onboarding): v3 — thin server, frontend-orchestrated welcome
Collapse v2's persisted runtime-choice fields + 4-branch dispatcher +
OnboardingService/WorkspaceContentService stack down to a single rule:
`onboarded_at` is the only state field, layout hard-gates on it, and the
welcome experience after Step 3 is owned entirely by the frontend.
V3 flow
- Step 3 button: await POST /api/me/onboarding/complete (mark only) +
park a transient signal in `useWelcomeStore` + navigate
- Workspace layout: hard gate `onboarded_at == null` -> /onboarding
- `<WelcomeAfterOnboarding />` reads the welcome-store signal:
- runtime path: find-or-create Multica Helper via generic createAgent
with bilingual instructions from `templates/helper-instructions.ts`,
blocking modal with 3 starter cards, pick -> createIssue + navigate
- skip path: provision install-runtime (in_progress) -> agent-guide
(todo, body embeds install-runtime mention chip) -> follow-up comment
on install-runtime mentioning agent-guide; then pop celebration
modal with 🎉 emoji pop animation, 2 read-only preview cards, single
[Got it] CTA that navigates to install-runtime
Server cleanup
- Drop OnboardingService, WorkspaceContentService, v2 runtime-choice
columns/CHECK on user, EnsureOnboardingContent endpoint
- CompleteOnboarding/AcceptInvitation call qtx.MarkUserOnboarded
directly (no service indirection)
- BootstrapOnboardingRuntime / BootstrapOnboardingNoRuntime kept as a
deprecation shim in onboarding_shim.go for desktop < v3 during the
rollout window — handlers inlined to qtx.* calls, no service layer
Localization
- Persisted strings (issue titles/bodies, Helper instructions/
description, comment prefix) live as TS const `{en, zh}` maps in
`packages/views/onboarding/templates/` — i18n bundle staleness can no
longer write raw key paths into DB
- UI-rendered strings (modal copy, status chips, buttons) stay in
`packages/views/locales/{en,zh-Hans}/onboarding.json`
- Language picked from live `i18n.language` (not `me.language`, which is
null for new users until they pick a preference)
Race protection
- Module-level promise dedupe (`findOrCreateHelper`, `seedIssueDeduped`,
`postCommentDeduped`) so React StrictMode double-mount can't fire two
parallel API calls that the server would then 409
Cross-references between the two skip-path issues render via Multica's
mention-chip protocol `[<identifier>](mention://issue/<uuid>)` so they
match the styled IssueChip pills used elsewhere.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(onboarding): welcome-after-onboarding modal redesign + cross-user safety
Welcome modal polish (the post-Step-3 surface this branch already
introduced):
Runtime path
- Helper avatar replaces the bouncy 🎉 hero; tone-down animation to
fade. New copy: "Hi, welcome to Multica / I'm your first Agent
assistant" + capability hint sentence so users discover assignment +
chat from the first screen.
- Cards changed from "click = submit" to multi-select with the existing
border-primary + ring selection pattern used by compact-runtime-row;
bottom CTA "Assign N tasks to me →" appears only with N>0.
- New starter cards: intro / tour / welcome_page (the last one tells
Helper to paste an HTML welcome page into the issue comment — works
on any runtime regardless of fs access).
- Success state added between createIssue and navigation: 🎉 +
"All set!" + "Sit tight ☕ — your {agentName} is on it" + inbox/chat
hints, single [Got it] button.
- Title/prompt for starter cards now live in TS const
HELPER_STARTER_PROMPTS (persisted to DB — must not depend on i18n
bundle being loaded); subtitle stays in onboarding.json.
Skip path
- Body restructured into three independent ```md blocks (Name /
Description / Instructions) so each picks up the markdown renderer's
per-block copy button — no manual extraction.
- ZH body now embeds the ZH Helper Description + Instructions (was
Chinese-around-English-block).
- Follow-up comment uses Multica's mention-chip protocol
[identifier](mention://issue/uuid) so it renders as the styled
IssueChip pill.
- Issue titles bilingual with "Step 1 / Step 2" prefix.
Cross-user / cross-workspace safety (code review feedback)
- web onLogout + desktop handleDaemonLogout now call
useWelcomeStore.reset() so user B logging into the same browser
doesn't inherit user A's signal.
- WelcomeAfterOnboarding gates on
currentWorkspace.id === signal.workspaceId — prevents firing the
modal in workspace B when the signal was parked for workspace A
(desktop multi-tab, back/forward, deep-link).
- Module-level promise dedupes (pendingHelperSetup,
pendingIssueSeed, pendingCommentSeed) for the three API calls so
React 18+ StrictMode dev double-mount can't race-create duplicates.
Other small fixes carried in this commit
- Helper instructions / agent description / starter card titles all
read i18n.language (not me.language, which is null for new users
who haven't picked a UI language preference yet).
- Reverted welcome-emoji-pop animation to a small fade for the runtime
avatar (kept the bouncy variant for the skip 🎉 hero where the
celebration is the whole point).
- Removed the duplicate 🎉 from the skip modal title (kept the hero
one only).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fix(views): i18n hardcoded "Close" in welcome FullScreenError
CI lint (i18next/no-literal-string) blocked on a literal "Close" string
inside `FullScreenError` — surfaced as a nit in the original code
review but missed in the merge. Add `error_close` to onboarding.json
(EN: "Close" / ZH: "关闭") and thread it through as a `closeLabel`
prop, matching the existing `retryLabel` plumbing.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
|
||
|
|
614dfae884 |
MUL-2488 feat(timezone): Scheduling / Viewing two-layer timezone architecture (#2968)
* docs(timezone): add scheduling/viewing timezone architecture RFC * feat(db): replace daily rollups with task_usage_hourly, add user.timezone Migrations 100-104: add "user".timezone (Viewing tz), build the UTC hourly task_usage_hourly rollup with its pipeline, drop the legacy task_usage_daily / task_usage_dashboard_daily pipelines, and drop the agent_runtime.timezone column. Report queries now slice day boundaries at read time by the caller-supplied @tz instead of materialising in a fixed tz. Regenerate sqlc. * feat(server): add task_usage_hourly backfill command Replace the two legacy backfill commands (daily / dashboard_daily) with a single backfill_task_usage_hourly that loads historical task_usage into the new UTC hourly rollup, sliced per workspace. * refactor(server): resolve viewing timezone in report handlers Report handlers resolve the Viewing tz per request (?tz query param, then user.timezone, then UTC) and pass it to the hourly-rollup queries. Drop the UseDailyRollup feature flags and the old raw-scan/daily-rollup dual paths, remove the /api/usage endpoints, and stop the daemon from reporting and the runtime handler from accepting host timezone. * refactor(core): switch report queries to viewing timezone API client and dashboard/runtime queries send ?tz with each report request, the user schema/types carry the new timezone field, and the runtime timezone field/mutation is removed. * feat(views): add viewing timezone preference and UI Add the useViewingTimezone hook and a Timezone setting in Preferences; report charts and the dashboard week boundary follow the viewer tz. Remove the runtime detail timezone editor and its locale strings. * fix(test): update fixtures and stabilize tests for timezone refactor The timezone architecture refactor changed several types without updating dependent test code: - RuntimeDevice no longer has a timezone field — drop it from the create-agent-dialog runtime fixture. - User now requires a timezone field — add it to the apps/web mockUser fixture. - The PreferencesTab timezone tests asserted on the async save handler (PATCH then store update) with a bare expect, racing the mutation's settle callback, and timed out querying the Select's ~600-option IANA list on a loaded CI runner. Wrap the assertions in waitFor and extend the timeout for those three tests. * docs(timezone): document self-host migration order and trigger invariant Add a SELF-HOST UPGRADE ORDER runbook to the backfill command's package comment: applying migrations 100-104 in a single migrate-up drops the legacy daily rollups before the hourly backfill runs, leaving dashboards empty until cron catches up. Add an INVARIANT comment on trg_atq_dirty_hourly noting that agent_id must be added to the trigger's OF list if it ever becomes mutable, otherwise dirty buckets for the old agent_id are silently missed. * style(runtimes): drop trailing blank line in runtime-detail |
||
|
|
591e47842d |
refactor(onboarding): remove starter-content kit; unify install-runtime issue across mark-onboarded paths (MUL-2438) (#2884)
* refactor(onboarding): remove starter-content kit, unify install-runtime issue across mark-onboarded paths (MUL-2438) Drops the post-onboarding ImportStarterContent / DismissStarterContent flow (handler + routes + StarterContentPrompt + templates + locale strings + analytics event). The bug — web onboarding seeding 6+ starter issues without a runtime — only existed through that path; with it gone the source disappears. The "install a runtime" issue from BootstrapOnboardingNoRuntime is now the canonical no-runtime onboarding seed. The title/description and a LockAndFindActiveDuplicate-deduped seeder move to handler/no_runtime_issue.go, and CompleteOnboarding / CreateWorkspace / AcceptInvitation seed it whenever the workspace has no runtime yet, so every mark-onboarded entry point lands the user on a concrete next step. starter_content_state column is kept and continues to be claimed as 'imported' in all five entry points so older desktop builds (which still render the legacy dialog on NULL) don't surface it to accounts created after this change. Co-authored-by: multica-agent <[email protected]> * fix(onboarding): backfill starter_content_state for in-window NULL users (MUL-2438) 054 only covered pre-feature users. Anyone onboarded between then and the starter-content kit removal could still sit at NULL, and old desktop clients gate the legacy StarterContentPrompt on `starter_content_state IS NULL`. The import/dismiss routes are gone, so leaving these rows NULL would surface a dialog whose buttons 404. Mark them 'imported' to match the new helper's claim semantics. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Lambda <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
5ad1641b72 |
Revert "Squad archive dialog + role editor + transactional DeleteSquad (#2680)" (#2687)
This reverts commit
|
||
|
|
2980ead4c7 |
Squad archive dialog + role editor + transactional DeleteSquad (#2680)
* docs(squad): address plan-review feedback for archive + role plan
Resolve the 4 items the reviewer raised on MUL-2265:
1. TS schema: declare `active_issue_count` as optional (`number | null | undefined`)
so list/create/update Squad responses don't lie about their shape; only
`getSquad` parses through SquadSchema.
2. Archive semantics: restrict TransferSquadAssignees to active issues
(status NOT IN done, cancelled) so dialog count and SQL operate on one set
and terminal-state issues keep their historical assignee.
3. Index assumption: corrected — `idx_issue_assignee (assignee_type,
assignee_id)` exists and is sufficient at realistic squad cardinality;
no new index needed.
4. Fixed `*int64` test comparison and added `.loose()` to SquadSchema per
the local schemas.ts convention.
Co-authored-by: multica-agent <[email protected]>
* docs(squad): plan v3 — revert to count-all/transfer-all on archive
Reviewer round 2 surfaced two structural problems with plan v2's
active-only carve-out:
1. useActorName resolves squad names via ListSquads, which filters
archived_at IS NULL. A closed issue with an archived-squad assignee
would render as "Unknown Squad".
2. The status-only update path in UpdateIssue skips validateAssigneePair,
so a done/cancelled issue with an archived-squad assignee could be
reopened to in_progress, violating the "no active issue on an archived
squad" invariant enforced elsewhere.
Both problems disappear by reverting to count-all + transfer-all: after
ArchiveSquad runs, no issue points at the archived squad, so neither
case can occur. The product trade-off is that closed historical issues
now show the leader agent instead of the archived squad in their
"Assigned to" badge — consistent with existing agent-level reassignment
behavior elsewhere in the product.
Field rename: active_issue_count -> issue_count.
TransferSquadAssignees SQL is unchanged (already transfers all).
Co-authored-by: multica-agent <[email protected]>
* docs(squad): add Task 2b — wrap DeleteSquad transfer + archive in one tx
Reviewer round-3 flagged that the v3 invariant ("after archive no
issue points to the squad") was asserted on the happy path only.
DeleteSquad's current best-effort impl breaks it two ways:
- transfer failure → slog.Warn but archive proceeds (Unknown Squad,
reopen-into-archived-squad bugs reappear)
- archive failure after a committed transfer → 500 with squad still
active but emptied
Task 2b rewrites DeleteSquad to run TransferSquadAssignees +
ArchiveSquad inside one pgx tx, mirroring the project.go:266-314
pattern. Publish moves below Commit. Adds two regression tests that
lock both partial-write failure modes.
Co-authored-by: multica-agent <[email protected]>
* feat(squad): replace native confirm() with AlertDialog and rewrite role editor as combobox
Backend:
- Add CountIssuesForSquad sqlc query (counts every issue assigned to a squad,
no status filter — matches the existing transfer-all archive semantics).
- Extend SquadResponse with optional `issue_count` (`*int64` + omitempty,
populated only by GetSquad to avoid an N+1 in the list endpoint).
- Wrap DeleteSquad's transfer + archive in a single pgx transaction so the
v3 invariant ("after archive, no issue points to the squad") is durable
rather than best-effort. Promote slog.Warn to slog.Error and check the
parseUUIDOrBadRequest ok flag (silent zero-UUID was a #1661-class latent
bug). Publish only after Commit so realtime never sees rolled-back state.
- Tests cover happy path (count, transfer-all including terminal statuses)
and both rollback directions (transfer fail / archive fail) via a
fault-injecting tx wrapper.
Frontend:
- Extend Squad TS type with `issue_count?: number | null` (optional —
list/create/update legitimately omit it). Add SquadSchema with `.loose()`
and wrap getSquad with parseWithFallback so older servers and count-error
responses degrade to the dialog's "no count" copy variant.
- Replace `window.confirm()` with shadcn `ArchiveSquadConfirmDialog`
(destructive variant, leader name + count + closed-issue caveat in the
copy, Loader2 while pending). i18n keys added under squads.archive_dialog.
- Rewrite RoleEditor as a Popover + Command combobox: Pencil affordance is
always visible, suggestions aggregate other members' roles, commit only
on Enter or selecting a suggestion (blur discards), per-member savingId
drives Loader2 so the spinner only renders on the row being saved.
Co-authored-by: multica-agent <[email protected]>
* fix(squad): discard RoleEditor draft on close and no-op blank Enter
Two reviewer findings on
|
||
|
|
623d29f276 |
feat(agents): one-click create from curated templates (Phase 1) (#2520)
* docs(agents): three-phase agent quick-create plan
Captures the full design for moving agent creation from manual form +
one-by-one skill attachment to a tiered experience:
- Phase 1 (this PR): one-click curated templates, AI-free.
- Phase 2 (next): AI-recommended skills via the existing quick-create
task mechanism — no new server-side LLM dependency.
- Phase 3 (later): AI creates the whole agent end-to-end, composing
Phase 2 with a new `multica agent create` CLI driver.
Documents the architectural decisions that keep all three phases on
existing infrastructure (no SSE, no server-side LLM SDK, no new WS
channels), the two soft blockers Phase 1 unlocks for later phases
(createSkillWithFiles TX composability + skill same-name dedupe), and
the scope decisions we explicitly opted out of (Anthropic plugin
marketplace, ClawHub UI affordances).
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* fix(skills): harden import against invalid UTF-8 and binary files
PG rejects two byte patterns in a TEXT column. Both crashed real skill
imports we hit while assembling the template catalog:
- Embedded NUL (0x00) -> SQLSTATE 22021. Already stripped by
sanitizeNullBytes, kept as-is.
- Other invalid UTF-8 (e.g. 0x91 — Windows-1252 smart quote in a skill
whose author saved prose from Word). sanitizeNullBytes now also runs
strings.ToValidUTF8 over the content so the second class no longer
takes the whole import down.
For non-text payloads (images, fonts, archives, compiled binaries),
sanitization isn't the right fix — agents never read those as text,
and the bytes can't survive a TEXT column at all. addFile now skips
them by extension before the per-bundle cap counters tick, logging
the skip so an unexpected drop leaves a breadcrumb.
Function name kept for compatibility with the many call sites; both
behaviours are strict supersets of the original.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* refactor(skills): split createSkillWithFiles for tx composition + add workspace find-or-create query
Two soft blockers cleared so create-from-template (next commit) can
fold N skill creates and the agent + binding writes into one outer
transaction:
1. createSkillWithFiles used to Begin/Commit its own tx. Caller
composition was impossible — N invocations meant N separate
transactions and no atomicity over the whole materialise step.
Pull the body into createSkillWithFilesInTx(ctx, qtx, input); the
original function becomes a thin wrapper that manages its own tx
for standalone callers. Existing call sites: zero behaviour change.
2. Add GetSkillByWorkspaceAndName sqlc query — workspace skill lookup
by name, anchored to UNIQUE(workspace_id, name) from migration
008. Lets the template materialiser implement find-or-create:
reuse the workspace's existing skill row when a template
references the same name, rather than crashing on the unique
constraint or polluting the workspace with `<name>-2` clones.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* feat(agents): agent template catalog + create-from-template endpoint
Server-side foundation for Phase 1 of the quick-create roadmap (see
docs/agent-quick-create-plan.md). Adds:
- server/internal/agenttmpl/ — embed-loaded catalog of curated agent
templates. Each template ships pre-written instructions plus a list
of skill URLs that get materialised into the workspace at create
time. Validation runs at startup (init() panics on a malformed
template) so a bad JSON ships as a deploy-time defect, not a
runtime 500. Slug must equal the filename basename so the URL
router is mirror-symmetric with the file layout.
- 11 starter templates covering Engineering / Writing / Building /
Testing (code-reviewer, frontend-builder, planner, docs-writer,
one-pager, html-slides, full-stack-engineer, …).
- Three new endpoints, all behind RequireWorkspaceMember:
GET /api/agent-templates — picker list (no instructions)
GET /api/agent-templates/:slug — detail with instructions
POST /api/agents/from-template — materialise + create
Create flow:
1. Auth + runtime authorization happen BEFORE the GitHub fan-out
so a 403 never wastes 20s of upstream fetches.
2. Pre-flight dedupe by cached_name reuses workspace skills
without an HTTP fetch — second create-from-the-same-template
drops from 20s to <100ms.
3. Parallel fetch (30s per-URL timeout) for the remaining skills.
4. Single transaction: every skill insert, the agent insert, and
the agent_skill bindings. On any upstream fetch failure the TX
rolls back and the API returns 422 with `failed_urls` so the
UI can name the bad source(s).
5. extra_skill_ids (user-supplied additions) are verified through
GetSkillInWorkspace per id before attach, so a malicious client
can't graft a skill from another workspace via UUID guessing.
- multica agent create --from-template <slug> CLI flag dispatches to
the new endpoint with a 60s ceiling, matching `multica skill import`.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* feat(agents): one-click create-from-template UI
Frontend half of Phase 1. CreateAgentDialog becomes a state machine
spanning four steps:
chooser → Start blank / From template cards
blank-form → existing manual form (post-chooser)
duplicate-form → existing form pre-filled from a duplicated agent
template-picker → grid of templates, click navigates to detail
template-detail → instructions + skill list preview + one-click Use
Picking a template never lands on the form: name auto-deduped against
existingAgentNames, runtime = first usable one, visibility = private.
Refinement happens on the agent detail page if needed. Same rationale
the doc spells out — templates exist precisely to skip configuration.
New components, all collapsible-by-default so quick-create stays fast:
- template-picker.tsx — categorised grid, lucide icons + semantic
accent tokens resolved through static maps so Tailwind's JIT picks
up every variant (dynamic class strings would silently miss).
- template-detail.tsx — instructions preview, skill list with cached
descriptions, Use CTA. Renders the failedURLs banner when a 422
fires — the only step that can trigger that response.
- instructions-editor.tsx — collapsed preview-card / expanded full
ContentEditor.
- skill-multi-select.tsx + skill-picker-list.tsx — shared multi-
select surface, also adopted by the existing skill-add-dialog.
- avatar-picker.tsx — agent avatar upload, mirrors the inspector's
visual language.
Schema-defended client (CLAUDE.md → API Response Compatibility): the
three new endpoints are wired through parseWithFallback with lenient
zod schemas. Desktop builds outlive any given server — a future
field rename / wrapping must not white-screen older installs.
listAgentTemplates accepts both the current bare array and a future
{templates: [...]} envelope. Coverage: 7 new schema-test cases in
schema.test.ts (null body, missing skills/instructions, malformed
create response, envelope migration).
Catalog + detail go through TanStack Query with staleTime: Infinity —
workspace-independent static data, no per-mount refetch.
Other:
- skill-add-dialog becomes a true multi-select (Confirm button +
checkbox list); attached skills are filtered out of the list.
- agents-page hands the freshly-created Agent back to the dialog so a
follow-up setAgentSkills can attach the form-selected skills.
- agent-overview-pane drops the mx-auto/max-w-2xl frame on config-
tab content; the wider dialog visual language reads better with
tabs filling the column.
- Every new UI string lives in both en/agents.json and
zh-Hans/agents.json under create_dialog.* / tab_body.skills.* —
locales/parity.test.ts blocks drift in CI.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* fix(ci): align skill import test + drop next-only lint suppression
- TestFetchFromSkillsSh_ResolvesRootLevelSkillMd now expects assets/logo.png
to be skipped; matches the new addFile binary-extension guard
(
|
||
|
|
46eed3b298 |
Add task dispatched analytics event (#2310)
Co-authored-by: Devv <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
ce00e05169 |
Add canonical PostHog core metrics events (#2302)
* Add canonical PostHog core metrics events Co-authored-by: multica-agent <[email protected]> * Address analytics review feedback Co-authored-by: multica-agent <[email protected]> * Tighten analytics review follow-ups Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: Devv <[email protected]> Co-authored-by: multica-agent <[email protected]> |
||
|
|
64c605e227 |
fix(execenv): write OpenCode skills to .opencode/skills/ for native discovery (#2016)
* fix(execenv): write OpenCode skills to .opencode/skills/ for native discovery * fix(repocache): exclude OpenCode skill directory |