Files
open-design/CHANGELOG.md
CaprikaGitHublefarcena1chztPerishCodeopen-design-crew[bot] <299007234+open-design-crew[bot]@users.noreply.github.com>alchemist
2b41bc5c3d feat(share): publish files as public links with stop/resume and synced comments (#8408)
* wip: wire deleted-file stop retry and cover owner-scoped HTTP behavior

* wip: persist published comment backfill state

* wip: connect backfill delivery receipts and preserve completed batches

* wip: expose exact-file backfill state and retry through HTTP and CLI

* wip: wire scoped comment sync pause into preview panel

* test: cover backfill terminal rejection and stale publication receipts

* wip: separate published content from unavailable viewer origin

* feat: distinguish publication success from unavailable viewer links

* fix(web): gate export share guide on authoritative project history

* test: restore publish backfill HTTP proof with current publisher pipeline

* fix(web): show authoritative stopped-share guidance

* fix(web): restore publish help before creating a share

* fix(web): show unread comments on collapsed rail

* fix: share annotated selectors across URL and srcdoc bridges

* test: verify saved comment selector reanchors in Chromium

* fix(web): warn about active shares before project deletion

* fix: authorize and bootstrap public share project bindings

* wip: prepare dispatched live share-chain probe (not run)

* fix(web): deliver per-file share residuals after deletion

* fix(web): prevent stopping shares in read-only viewers

* fix(e2e): retain complete share probe HTTP evidence

* fix(web): name shared projects in delete confirmation titles

* docs(e2e): clarify live share acceptance and human login boundaries

* fix(e2e): preserve share query context in diagnostic evidence

* fix(web): restore share-panel copy and comment-pin visuals per board8

chain1-publish G4: delete the stale "仅支持分享单个文件,内嵌图片等本地
资源暂不支持" sentence from fileViewer.publishSingleFileDescription (19
locales) — it contradicts the current multi-resource share and G4 required
its removal.

chain1-publish S1: add the missing "链接访问" title row + its static
description ("接收者可查看预览和已有评论,不包含对话和代码。") to
ShareTab's public-share section, as new fileViewer.linkAccessTitle /
linkAccessDescription keys (19 locales). No switch added — that control
overlaps the existing "停止分享" button and is left as a 需确认 item.

chain4-owner 4.2b: canvas comment pins go from 20x20/weight 600/2px white
ring/fixed #282828 to 28x28/weight 500/no ring, and now derive their
background/foreground from the same per-author color used by that author's
sidebar avatar (commentAuthorAvatarColor), both pure local CSS/TSX with no
packages/ or protocol change.

chain4-owner O7: formatCommentTime now buckets by calendar day (adds
"昨天", drops the "N 天前/N 周前" buckets, switches the hour bucket to
same-day instead of <24h elapsed, and formats older dates via
toLocaleDateString with year only when it differs from the current year),
and comment-side-time gets a title attribute with the full absolute
timestamp on hover.

Verification: pnpm --filter @open-design/web typecheck passes. Not run
through a real renderer — no pixel-level claim.

* fix(web): share entry icon, delete-confirm title, OP2 read-only hint

chain1-publish E0: swap the share toolbar entry's icon from RemixIcon
share-forward-line to the board's own upload-arrow glyph (13px path
"M12 4v11M7 9l5-5 5 5M5 14v5h14v-5") — same size/shape as the board's
.shareBtn icon, glyph only, no behavior change.

chain6-dialogs D-04: drop the extra "确定" from designs.deleteConfirm's
zh-CN/zh-TW title ("确定删除「{name}」?" → "删除「{name}」?") to match
the board and every other locale, which already read "Delete "{name}"?"
without an "are you sure" prefix.

chain4-owner OP2: add the missing "分享页评论不可编辑" explanation line
under a comment's textarea specifically when the viewer can't edit it
AND the comment is a share-page (external) one — team-member-reads-
external-comment (OP3) is unaffected, matching the board's OP2-only
scope. New comment.sharePageCommentReadOnly key, 19 locales.

Verification: pnpm --filter @open-design/web typecheck passes. Not run
through a real renderer — no pixel-level claim.

* test(web): update FileViewer specs for the G4/O7/4.2b restorations

Three specs encoded the exact pre-fix behavior the previous two commits
intentionally changed, so they went red:

- "reads the publish help without publishing" located the help bubble by
  the now-deleted stale sentence ("Only a single file can be shared for
  now..."); relocates it by the sentence that remains.
- "keeps relative comment-time boundaries stable across clock boundaries"
  asserted the old <24h-elapsed hour bucket; the O7 rewrite buckets by
  calendar day, so the fixture's 23h-ago comment (which crosses this
  fake clock's midnight) now correctly reads "昨天" instead of "N 小时前".
- "keeps D1-D4 marker and deck-page presentation on the rendered
  selectors" asserted the old 20px/weight 600/white-ring pin CSS; updated
  to the board's 28px/weight 500/no-ring geometry.

Verification: pnpm --filter @open-design/web typecheck passes;
tests/components/FileViewer.test.tsx: 334/334 passed (via
node_modules/vitest/vitest.mjs --pool=forks, run directly against the
node 24.18.0 binary — this shell's `npx`/`node` wrap nvm in a way that
blows bash's FUNCNEST under vitest's fork pool).

* feat(web): draw the export guide's countdown, title and description

The 10s countdown already existed as state - remainingMs, checkedAt and
paused were computed and consumed - but nothing rendered it, so the guide
showed no sign that it was about to dismiss itself.

The bar is one CSS width transition per run rather than a per-frame timer:
the remaining time is already known when the run starts, so animating to
zero over exactly that duration needs no re-render, and a pause just freezes
the transition.

Board copy added across all 19 locales.

* test(web): update AfterExportShareGuide specs for the P1 card restoration

Three assertions encoded the pre-restoration visual/DOM shape that
d2c72f1624 intentionally changed to match Board8 Chain-6-dialogs.dc.html's
P1 ("导出完成 · 分享引导条", `.s-m1eexportprompt .prompt[role=status]`)
mockup:

- role query changed region -> status: P1's `.prompt` div in the board
  markup carries `role="status"`, not the implicit `role="region"` that
  came from the old bare `<section aria-label>`.
- button count 3 -> 2, and the close-button-then-never-show sequence was
  replaced: the board's P1 card has no `.pclose` element (unlike the
  discarded EX1 alternative, which does have one) - its only controls are
  the `.pfoot` pair (`.pghost` "不再提示" / never-show, `.paction` "去分享"
  / open-share). The guide is still dismissible via never-show, open-share,
  or the unchanged 10s auto-collapse in after-export-share-guide.ts.
- dropped the dead `awaitingDesign` fixture key/assertion now that the P1
  design reference is in hand and the placeholder key has been removed.

Scoped run: 3 files / 40 tests passed via direct-node vitest (avoids the
nvm-wrapped node FUNCNEST issue under the fork pool noted in
od-side.md).

* feat(web): render the backfill, align, and shareStopped comment sync banners

CommentSyncBanner.tsx previously only rendered K8 (sessionMissing). The
backfill (K2), align, and shareStopped fields already existed on
CommentSyncState with i18n copy standing by, but no render branch consumed
them — this was flagged in .tmp/share-p0/visual-static/chain1-publish.md (K2)
and chain4-owner.md ("补传横幅/评论对齐横幅/分享已停用横幅").

Adds three branches, single-slot priority sessionMissing > shareStopped >
backfill(failed) > align(diverged), respecting the hard semantics documented
on packages/contracts/src/api/share.ts's CommentSyncState:
- backfill absent/pending/succeeded never renders a warning (absence is
  "never attempted", not success); the server already filters stale-revision
  backfill records, so any backfill this component sees is for the file's
  current publication.
- align absent or 'unknown' never renders as aligned or as a problem; only
  'diverged' is actionable.
- shareStopped: null (could not be read) never renders as stopped nor as
  healthy; only `true` renders.

Retry buttons call the existing retry endpoints (POST comment-sync-state for
backfill, POST comments/align for align) and re-fetch via a new
`refreshToken` option on useCommentSyncState. The backfill retry button is
only shown when `retryable === false`, per the field's own doc comment:
retryable:true means the server is already retrying on its own.

useCommentSyncState also gained an optional `filePath` param, threaded from
HtmlViewer's `file` prop through CommentSideDock -> CommentSidePanel, because
the backfill answer is per-file and the endpoint requires filePath to compute
it (readPublishedCommentBackfill returns undefined without one) — without
this wiring the backfill branch would be permanently unreachable regardless
of the new rendering code. This is plumbing an existing parameter to an
existing endpoint, not new triggering behavior.

Copy is the parent-approved text verbatim, added as three new i18n keys
across all 19 locales (fileViewer.commentSync.backfillFailedTitle/Body,
alignFailedBody); the retry button reuses the existing preview.retry key.

Tests: new tests/components/share/CommentSyncBanner.test.tsx (12 cases)
covering all silence/render rules above. Full apps/web typecheck, guard, and
tests/components/FileViewer.test.tsx (334/334) all pass.

Not done in this commit: the ShareTab panel's own shareStopped-driven hint
(a separate, pre-existing `publicationStatus === 'stopped'` render in
ShareTab.tsx using project-share-history data, not CommentSyncState) is
untouched — it is a different signal for a different surface.

* feat(web): add the board's "link access" switch to ShareTab

chain1-publish.md's S1/S4 rows show a `role=switch` "链接访问" toggle as
part of the panel's fixed structure that the current implementation never
had (flagged under chain1-publish.md's "需要人确认" #1 and chain6-dialogs.md
D-00). The user explicitly authorized adding it and asked me to resolve its
semantic overlap with the existing "Stop sharing" text button.

Resolution: the switch reads and drives the exact same `filePublished` state
through the exact same handlers already used by the primary button/ghost
button (publishCurrentFilePublic / unpublishCurrentFilePublic) — on -> off
calls unpublish, off -> on calls publish. Because both controls act through
the same two functions on the same state, they can never show or produce
inconsistent results; this is presentation only, not a new trigger path.

Did NOT remove the existing "Stop sharing" ghost button, even though the
board's own S4 mockup has no separate one — chain1-publish.md's own
"需要人确认" #2 explicitly warns not to delete it off this board alone,
since it may belong to a different board's scope (chain2's handoff notes
mention Chain-5). Keeping both is redundant but not contradictory, per the
task's own escape hatch ("你判断不该同时存在,那就说明理由并只做一个" —
here the judgment is the opposite: keep both, and make sure they can't
diverge).

Geometry/colors from chain6-dialogs.md D-00: 32x19 track, r999, off #D6D6D6,
on #282828, 15x15 white thumb. No new i18n keys — the switch's accessible
name reuses the existing `fileViewer.linkAccessTitle` key.

Also updates the pre-existing frozen DOM-baseline fixture
tests/fixtures/share-tab/share-panel.team.html. Confirmed via `git stash`
bisection that this fixture was ALREADY stale/failing before this commit —
the earlier "链接访问" title+description restoration (commit 721cb976cf,
prior round) changed the panel's markup without updating this baseline, and
nothing in CI caught it because this file wasn't in that round's targeted
test run. This commit's switch adds one more, unrelated divergence on top of
that pre-existing one, so the fixture is regenerated from the component's
actual current output rather than hand-edited, and captures both changes at
once. The `personal` fixture is untouched: canPublishPublic is false in that
scenario (no workspace context at all), so the entire link-access section
(switch included) never renders there, exactly as before.

Verification: pnpm --filter @open-design/web typecheck and pnpm guard both
pass. tests/components/FileViewer.share-tab-extraction.test.tsx (22/22),
ShareTab.copy-button/first-publish/publish-error/scope-keyboard.test.tsx,
tests/styles/share-scope-design.test.ts, and FileViewer.test.tsx
(394 tests total) all pass. Not rendered in a real browser — see the
evidence file for the "未经真实渲染验证" caveat.

* fix: scope deletion stops to the selected published file

* feat(contracts): define share bridge v2 geometry boundary

Define authoritative OD v2 viewport geometry, fallback pin bounds, display tokens and geometry-update gates. Add 16 passing boundary tests; root guard and typecheck passed.

Known pre-existing failure: share-contract.test.ts expects SHARE_SNAPSHOT_DISCOVERY_IN_P0=false while baseline bd39802907 exports true. The identical assertion was reproduced against unchanged HEAD source; this unrelated assertion is not modified. Vela runtime and browser acceptance remain separate.

* fix(comments): create anchors for personal public shares

* fix(comments): create anchors for personal public shares

* fix(web): prefer readable element names in comment titles

Use screen labels then element-text snapshots before localized type fallbacks. Keep synthetic pin/page titles and hide internal IDs. Export a pure resolver for share-page parity without new locale strings.

Verified 10 contract tests, all 334 FileViewer tests, all 19 existing locale key sets, root guard and root typecheck. Vela adoption and browser visual acceptance remain separate.

* fix: stop published files before file deletion

* fix: explicitly resume stopped publication generations

* Restore main comment UI styling while retaining share flows

* test: verify guarded resume through real Vela CLI

* fix(web): match main comment composer surface

* fix: register personal project owner before share history lookup

* fix(web): align comment avatar colors with shared page

* fix(share): revoke deleted sources through atomic resource removal

* fix(comments): seed client avatar colors only from authoritative author key

* test(share): expect the additive publish failure detail after merging main

#8402 adds an optional closed-token `failure` to failed publish responses.
The share-origin HTTP test compared the 502 body exactly, so it now also
expects { stage: 'push', reason: 'unknown' } for a synthetic upload error.

* test(contracts): assert deployed snapshot discovery in P0

* fix(share): revoke folder publications before deleting descendants

* fix(web): normalize share controls to supported font weights

* test(web): assert manual copy fallback after clipboard rejection

* test(share): align route fixtures with original owner authority

* feat(comments): expose explicit visitor comment pull to UI and CLI

* feat(cli): expose explicit project share resume command

* fix(cli): emit JSON for files delete

* refactor(share): name the unpublish response contract

* fix(comments): preserve Vela author key in inbound avatar identity

* test(share): use valid sharedAt timestamps in owner fixtures

* fix(share): use promise stat with production folder route fs

* fix(comments): use defined theme red for unread indicators

* fix(cli): preserve workspace context error codes

* fix(files): hide internal paths in deletion failures

* fix(comments): persist local author name and reconcile cloud author key

* fix(share): align owner sharing flows with final UI audit

* test(share): verify unboxed comment sync across owner states

* fix(share): apply second-round final design review to owner share UI

- Share panel: remove every yellow warning; show the public link row above
  the copy button in published states; oversized shares stay blocked and
  explain why in the red error row.
- Global toast for update success/failure and residual stop failure, with
  final design icons and copy; share panel buttons match the design.
- Comment sync: K1 turns the primary action into the sync progress bar, K4
  shows a busy "正在同步评论…" button, K5 retry is red.
- Deployment menu layers above the share popover; export-complete guide
  rebuilt to the final design.
- Share bridge pin label accepts exactly one letter so a CJK author's
  initial can label their pin.
- Update ui-audit capture specs for the intended copy/structure changes.

* fix(share): apply third-round design review to owner share UI

- Share toast rebuilt as ShareFeedbackToast to the design: solid red/green
  badge, plain underlined 重试, close; used for update success/failure and
  the deleted-project stop failure.
- The ··· deploy menu drops down under the trigger, right-aligned to the
  header tools, over the share panel.
- Signed-out share button reads 「登录后分享」; update notice loses its box;
  更新链接 / 登录后更新 / K5 retry use the design's blue and retry icon.
- Signed-out observed share reuses the published link row and copy button
  (chain icon, green check 已复制) and no longer overflows its card.
- Capture specs follow the intended copy and placement changes.

* fix(share): keep the signed-out share panel instead of re-homing on sign-out

Signing out on a shared file page intermittently landed on Home instead of
the signed-out share panel (Owner-S13), depending on which re-read returned
first:

1. The workspace-context refresh re-adopted the route's bootstrap witness
   from the previous account, re-keying ProjectView on the same file.
2. FileWorkspace treated that remount as a file switch and cleared the
   observed public link.
3. With no link left, the tab scope reset fell back to Home.

- useProjectRouteWorkspaceContext: a bootstrap witness issued before an
  identity refresh is retired and never adopted after it (the hook's
  documented rule for resolved authority now covers the witness too).
- isObservedShareFileSwitch: mounting or remounting a file view is not a
  file switch; only a change to a different file clears the observed link.

Red specs: hook re-adoption (red on previous source) and FileWorkspace
remount (red on previous source). S13 capture: 8/8 passes, was 2/7.

* fix(share): align remaining owner share copy and K1 sync count with final design

- S0: signed-out description uses the design copy via its own key; the
  publish-failure message keeps its original text.
- S5: update notice reads 「文件有新改动,访问者看到的还是上一版。」.
- K2/K5: the failed-sync notice drops the 「分享已发布」 title and uses the
  design copy; terminal variants keep their own wording.
- K1: first-publication sync shows 「同步已有评论 · N 条」. The backfill
  state carries optional total/synced from the daemon's backfill members
  table, driving a determinate bar when progress is known; older daemons
  fall back to the previous copy and the indeterminate sweep.
- Refresh the PR screenshot of the Share popup comment-sync state.

* chore(web): restore generated next-env.d.ts accidentally included in 30361fd670

* refactor(share): give each owner share UI element one owning component

Behavior-preserving consolidation of the owner share surfaces, verified by
pixel diff of all 17 Owner capture specs against the pre-refactor baseline
(every state within the known 0-0.25% noise from timestamps, placeholder
animation and the dev badge; the share panel itself is identical).

- Shared components: LinkAccessRow, ShareNoticeRow, ShareProgressButton
  (S2 upload + K1 sync), ShareErrorRow, ShareButton (primary / soft /
  soft-error / dark-sm on @open-design/components Button), ShareSignInButton
  over a new useCloudSignIn hook shared with CloudSignInTip.
- The popover skin moves from a :has(.panel) override to an explicit
  .chrome-unified-popover--share class; SignedOutObservedShare reuses
  SharePanelHeader and the extracted rows.
- Icons: every inline share SVG moves into components/Icon.tsx with
  byte-identical path data.
- Tokens: styles/share-tokens.css holds the share palette and sizes as fixed
  light values (not app theme tokens, so dark mode renders as before); share
  CSS modules reference them. Single-use decorative values stay local.
- Delete the unreachable React-component share popover (canShareCurrentFile
  is false by design, Owner-G3) and its dead state and handlers.
- Toasts: share-adjacent feedback uses ShareFeedbackToast; top toasts share
  one stacking order instead of overlapping at top:64px.
- Static CSS-contract tests read share styles through readShareCss, which
  resolves share tokens to the design values they assert.

Allowed visual changes (existing defects):
- The sign-in entry is a real button; signing/error messages render below
  it instead of inside a nested button (UI-040).
- The comment side panel's retry-failed message is red (UI-042).
- Simultaneous top toasts stack instead of overlapping (UI-039).
- 「正在开启…」 goes through i18n (UI-041).

* feat(daemon): resolve share links only from the Viewer origin

Public share links come from OD_SHARE_VIEWER_URL(S) (HTTPS root origin)
as https://<viewer>/artifact/{projectId}/{slug}; the console origin is
no longer a fallback. GET publish-public derives the URL from the
resolver instead of the stored row, so pre-upgrade console URLs are
never echoed, and reports link unavailable plus the slug when no Viewer
origin is configured. Remotely stopped rows are not live. Manual-revoke
recovery data carries projectId and a nullable url. od project share
prints the slug in the unavailable state. Test fixtures move from
OD_VELA_WEB_URL to OD_SHARE_VIEWER_URL.

* feat(web): show a link-unavailable state for URL-less publications

A publication is published when it has a slug, not a URL. Without a
Viewer origin the share panels show a translated link-unavailable
status with Stop sharing, never the Publish CTA, so the file is not
uploaded twice. registry normalizes the publish and read responses to
{slug, url|null}; auto-copy is skipped without a URL.

* feat(tools-dev): inject a default share Viewer origin for local daemons

The default stays empty until the test Viewer domain is decided
(decision 67 #7), so local shares report link unavailable. An operator
OD_SHARE_VIEWER_URL(S) always overrides it.

* feat(daemon): parse C3-LITE member comment pages in both collab transports

Add a single-page member pull to the Vela CLI and HTTP collab clients
that returns either a typed page or a rebuild-required signal.

- The page envelope is validated strictly; malformed comments are
  skipped per item and reported, so one bad stored payload cannot
  block the cursor.
- Minimal tombstones ({id, seq, deleted}) are accepted and get the
  requested projectId.
- INVALID_CURSOR / CURSOR_STALE / SCOPE_CHANGED map to
  rebuild-required; INVALID_PAGE_REQUEST stays an error.
- Paged requests send no If-None-Match.

* feat(daemon): drain member comment pages into SQLite atomically

Team projects on the Vela CLI transport now receive member comments
through bounded C3-LITE pages instead of the unbounded legacy pull.

- Each page's merges, tombstone deletes and cursor advance commit in
  one immediate transaction; the persisted cursor is the cloud's
  stateless page/resume token.
- The drain continues through empty hasMore pages, hands a finished
  snapshot off to an incremental round, and stops at the end of an
  incremental round.
- A remote tombstone deletes a row that has a pin_seq; unknown ids
  are a no-op.
- A wake during a pull triggers exactly one trailing rerun.
- Merge notifications fire only for committed, changed rows.
- Share-page (user) comments stay on the legacy pull until public
  paging exists. The legacy pull also keeps carrying member comments
  whenever paging did not complete, and a cursor that needs a
  rebuild is latched until the rebuild path lands.

* feat(daemon): rebuild member comment cursors and isolate sync scopes

An unusable member cursor is no longer latched. It is reset and rebuilt
from a fresh snapshot in the same drain.

- A per-scope ledger records the ids the member stream delivered. The
  terminal snapshot page, in the same transaction as the handoff cursor,
  removes cloud-origin rows the snapshot no longer contains. Rows the
  stream never delivered (never-synced local comments, share-page
  comments, rows another account learned) are never pruned, and
  nothing is deleted before the snapshot completes.
- Writes committed during the snapshot arrive through the handoff
  incremental round and are not pruned.
- Scope is (workspace, member, team, project). A scope must prove it is
  current before taking a project over. Every page is re-checked after
  its await and before its commit (active scope, team-share binding,
  bound context), so an old account's or unshared project's in-flight
  page is discarded.
- Account switch and stop sharing drop that scope's cursor and ledger
  but keep the local comment rows as local copies.
- INVALID_PAGE_REQUEST and repeated transport failures back off per
  scope (bounded exponential, capped at 5 minutes). A refused rebuild
  snapshot backs off instead of looping.

* feat(daemon): announce comment merges once per pull round

A member page drain used to fire `onMerged` after every committed page,
and the legacy pull fired once more, so one 3-page drain of 250 comments
pushed three or four `comment-changed` events and cost the open web view
as many full list reads.

- Every row a round commits (member pages, prune, tombstones, legacy
  share-page rows) is summed into one `onMerged` call when the round
  settles, including rounds that pause, lose their scope or throw after
  some pages committed. A trailing rerun is its own round.
- A throwing listener is reported through `onError` and neither masks
  the round's own error nor turns a successful round into an unredeemed
  wake.

* feat(web): single-flight the comment list refresh and keep unchanged comments

Every comment trigger (project SSE, fallback poll, visibility, stream
reconnect, status patch) started its own full list read; the generation
guard only discarded stale answers. A burst of 10 `comment-changed`
events cost 10 reads, and a refresh that found nothing new still
published a new list and re-rendered.

- `refreshPreviewComments` goes through a single-flight runner keyed on
  project and conversation: a trigger during a read buys exactly one
  trailing read with the latest closure; a different conversation starts
  its own read and the old loop stops rerunning.
- Fresh reads are reconciled into the current state: unchanged comments
  keep their objects, an unchanged list keeps its array (no re-render),
  and attached comments follow the same rule. The initial conversation
  read reconciles too.
- The remote half of an explicit pull has a 15s deadline and falls back
  to the local list, so a stalled daemon pull cannot hold every later
  refresh behind it.

Measured in ProjectView tests (10 SSE events during one read): 10 -> 2
list reads; an equal-data refresh: 1 -> 0 new lists/renders.

* fix(daemon): make a published private team project team-visible before its comment backfill

Publishing a public link for a private project in a team workspace registers
it in the team catalog, but only the later background reconcile marked the
local row team-visible. Until then the row was personal in a team workspace,
which is neither Team- nor personal-relayable: the publish backfill was
cancelled (BACKFILL_DELIVERY_DISCARDED) and comments written right after
publishing were never queued.

- publish-public marks the creator's private row team-visible right after
  catalog registration, before the publication transaction enqueues the
  backfill (decision 67 #11); the response carries madeTeamVisible.
- The relay re-queues a personal record as Team relay once its creator's
  project turned team-visible, and defers (bounded) a personal record in a
  team workspace whose publication is still current instead of cancelling it.
- A cloud seq is never adopted as pin_seq when another confirmed row of the
  file already holds it, which produced two pins numbered 1.

* feat(share): tell the owner a public link makes a private team project team-visible

The share panel (and the React component viewer's share menu) shows
"After publishing, team members will be able to see this project" before
the first publish of a private project in a team workspace, in all 19
locales, and updates the scope control and team project views once the
daemon reports madeTeamVisible. `od project share publish` prints the same
notice to stderr and --json keeps the madeTeamVisible field.

* feat(packaged): hand the daemon its channel's share Viewer origin

Packaged desktop and headless daemons get the share Viewer origin for
their release channel: stable sends OD_SHARE_VIEWER_URLS with the prod
profile mapped to https://open-design.app; other channels send nothing
until the test Viewer domain is decided (decision 67 #10), so shares
report link unavailable. An explicit OD_SHARE_VIEWER_URL(S) at launch is
forwarded unchanged instead. The daemon resolver stays the only
validator. The legacy payload-desktop handoff strips both keys so a
channel default never becomes a sticky override.

* fix(daemon): report madeTeamVisible when a catalog reconcile marked the project first

Registering a private team-workspace project in the team catalog lets a
concurrent catalog reconcile (hub team-projects-changed, a project list
read) rebind the local row to team before publish-public marks it. The
mark then changed nothing and the response omitted madeTeamVisible, so the
share panel kept showing "only me" and the CLI printed no notice. Seen in
the local integration rerun on the first UI publish.

publish-public now reads whether the row is the creator's private team row
before registration, and reports madeTeamVisible when that row is the
creator's team row afterwards, whichever writer flipped it. A project that
was already team-visible still reports nothing.

* fix(daemon): order synced comment revisions by cloud seq so replays never undo newer state

A share-page comment resolved at cloud seq 10 reverted to open after a daemon
restart: the legacy pull cursor is in memory, so the restarted daemon replays
from seq 0, and the merge stamped Date.now() on the share-page create (which
carries no updatedAt), making the replayed create look newest.

Each preview_comments row now records the highest cloud revision seq it
reflects (cloud_revision_seq) and the seq space it came from
(cloud_revision_stream: relay team + member stream epoch). An incoming
revision at or below that seq in the same stream is a replay and never
overwrites the row. Unseen revisions keep updatedAt last-writer-wins when they
carry an updatedAt; one without updatedAt applies in seq order and is stored
with its createdAt, never the local clock. pin_seq is untouched.

* feat(share): take the canonical share link from AMR

The daemon no longer builds the public share address from a hardcoded or
per-channel Viewer origin. AMR reports it: `vela share publish|bind|resume|
project-status --json` carry `url` (https://<share origin>/artifact/{projectId}/{slug})
for an active binding, or `link: {status: "unavailable", code}`.

Link rule, one place (resolvePublicShareLink): an explicit
OD_SHARE_VIEWER_URL(S) override wins (a present invalid override is
unavailable, never a fallback); otherwise the AMR address, accepted only when
it is byte-for-byte the canonical HTTPS root-origin address of this project
and slug; otherwise unavailable, passing AMR's code
(PUBLIC_SHARE_WEB_URL_UNAVAILABLE / PUBLIC_SHARE_IDENTITY_INVALID) through.
A rejected address is logged without echoing it.

The verified AMR address is persisted with the publication and refreshed on
publish, foreground bind/resume, the background binding retry and every
publish-public status read, without rotating the publication witness. When
the status read fails, GET falls back to the persisted link marked
`stale: true` (not for a queued stop); a stored legacy console URL fails
re-verification and stays hidden until the next online read replaces it.

Packaged daemons no longer get a per-channel Viewer origin and tools-dev has
no default; an explicit launch override is still forwarded. `od project share`
prints the same link, a stale notice on stderr, and says when an alias has no
Viewer address.

* fix(share): keep the link access switch thumb at the left when off

* fix(share): settle the share status after a stop or resume in the open panel

* fix(comments): keep a comment that was handed to the agent

Sending a saved comment to the agent deleted it as soon as the send was
queued, so a run that failed or was stopped left nothing to retry and
pushed a tombstone to the share page.

The hand-off is now carried by status alone. The three send paths no
longer delete after queue acceptance; they only clear the composer and
the selection for the accepted ids. A run that fails, is reported
canceled, or is stopped by the user releases its comments back to open
through releaseCommentsAfterUnsuccessfulRun. A successful run still ends
in needs_review.

* fix(comments): keep an unsent comment draft when another element is picked

Picking another element, lassoing, opening a saved pin, or selecting a
comment in the side panel retargeted the composer at once and dropped
the draft, queued notes, and context without a word.

All four pick paths now retarget through requestComposerRetarget. While
the composer holds unsent work the first pick changes nothing and shows
a notice inside the composer; the next pick confirms the discard and
retargets. Editing the draft disarms the pending discard. Closing the
composer, Escape, and switching files behave as before.

* fix(daemon): walk nested HTML in share/deploy plans and refuse root index.html conflicts

Referenced .html/.htm files (iframes, frames) are now walked like the entry:
their references resolve against their own directory at any depth, so their
stylesheets, images and child pages ship and missing/invalid ones are
reported. Share mode rewrites their root-absolute references relative to
their own directory and maps references to the entry to the published
index.html; standalone deploy keeps their bytes.

Parsing is deduped on each file's real path, which bounds the walk even with
in-project symlink loops (x -> .) and also fixes the existing unbounded CSS
@import recursion through such links.

A reference to an existing root index.html while sharing another page no
longer overwrites the entry: the plan records the conflict (referrer file,
attribute, line, suggested free name) and deploy refuses it with 409
ENTRY_INDEX_CONFLICT carrying a copy-to-agent fix prompt.

* fix(daemon): refuse share entry/index.html conflicts before reserving

The share plan summary carries entry-index-conflict blockers (with the agent
prompt), and publish-public now builds the plan, and refuses on size or
blockers, before reserving an alias or preparing Vela. A conflict returns
409 SHARE_ENTRY_INDEX_CONFLICT with the details in error.data. Freshness is
not compared against a blocked plan.

* fix(daemon): print share blockers and agent prompt in the od CLI

od project share preflight prints each blocker with its copy-to-agent
instruction and exits 1 when blockers exist (also with --json). A publish
refused with SHARE_ENTRY_INDEX_CONFLICT prints the instruction readably in
text mode before the structured error envelope.

* fix(web): explain share index.html conflicts with a copy-to-agent action

The share panel shows the entry-index-conflict blocker from the share plan or
a 409 publish refusal, disables publishing while it stands, and copies the
daemon's agent prompt (falling back to showing it for manual copy). New
strings are translated in all 19 locales.

* fix(comments): release a comment whose queued send never becomes a run

A saved comment is written 'applying' when its send is queued, before any run exists. The release added for unsuccessful runs only fired on run end, cancel and stop, so a send held by the pre-run balance gate, stopped or failed before its run was created, or taken out of the queue left the comment 'applying' and out of the comment list for good.

Queue drains now go through startQueuedChatSend, which releases the comments of a send that ended without a run. A send the gate holds no longer reserves its comments, and removing or editing a queued send releases what it carried. A held send that starts later reserves them again when its run begins.

* fix(share): keep a reopened share busy until its link has been read

A resume answers without a link, so the panel re-reads the share state. The busy state ended with the resume response, and for the seconds the re-read took the panel said the link was temporarily unavailable before swapping to the link.

settlePublicShareAfterMutation now writes the active state only once the re-read has settled, and the publish flow stays busy on it. A re-read that fails or carries no link still ends on link unavailable, without an error.

* fix(web): size the copy-for-agent button to its longest label

The soft share button is a fixed 88px slot sized for short labels, so
"Copy fix for agent" overflowed in English and longer locales. Let the
button grow to fit, and stack both labels in one grid cell so the width
stays put while "Copied" shows.

* test(e2e): reopen a stopped share through the switch in the share audit specs

The share audit specs stopped a share inside the panel and then clicked
the publish button to bring it back, which is the behaviour the panel no
longer has: a stopped share reads as stopped and reopens through the link
access switch with a resume request.

The mocked publish responses also lacked the receipt the client parses,
so they now return the real response shape.

* test(share): align CI cases with current share UI

* test(share): align CI cases with current share UI

* test(web): pin comment time boundary to local noon

* fix(share): allow reopening saved comment after save

* fix(daemon): wallet 401/403 no longer expires the AMR session

Billing is display-only (OPEND-3553). Vela maps internal billing
failures to 403, so a wallet rejection now only reports the balance as
unavailable instead of marking the credential expired, which dropped
workspace context and redirected to sign-in.

* fix(daemon): billing summary failure no longer expires the AMR session

A failed 'vela billing summary' (even an auth-shaped 401) only drops the
optional plan/balance from /status. Credential expiry stays owned by the
non-billing workspace directory path (OPEND-3553).

* fix(daemon): billing projection 403 no longer flushes workspace authority

A rejected workspace billing snapshot only marks the billing runtime
entry. It no longer invalidates the workspace directory, exact authority,
or exact context caches with auth_reject; Vela also maps internal billing
failures to 403, and membership revocation stays owned by the directory
and the hub workspace-access-revoked event (OPEND-3553).

* fix(daemon): carry share content fingerprint across stop and resume

Resume reopens Vela's saved version of the same alias but writes a new
local publication revision, so the fingerprint row (keyed to the old
revision) made freshness permanently 'unknown' and hid the update entry.
Rebind the stored fingerprint to the resumed revision of the same slug so
later edits read as 'outdated' (OPEND-3517).

* fix(daemon): redact identity argv from vela command failures

Node's exec error repeats the full command line in message, cmd and
stack, and presence/member/catalog callers logged it verbatim, leaking
--display-name and activity payloads. The vela command runner now
rewrites the values of --display-name, --name, --email, --activity-json
and --metadata-json before rejecting (stderr and the upstream status
signal are untouched), and the presence failure log records only the
classification and upstream status (OPEND-3520).

* fix(daemon): refuse plain publish of a stopped share before uploading

When Vela reports the file's share as stopped, a plain publish-public POST
now answers 409 SHARE_STOPPED_RESUME_REQUIRED before pushing bytes, since
Vela rejects it with 409 share_binding_stopped anyway; reopening the link
is the explicit { mode: 'resume' } request. The share publish wrapper also
keeps the upstream 'API request failed with status NNN: code' line so the
failure classifies as upstream_http instead of reason=unknown (OPEND-3510).

* test(daemon): satisfy test typecheck for batch-2 share fixes

* fix(daemon): keep vela argv redaction from crashing on non-string args

Some media callers pass optional argv slots through as undefined. The
redaction must never replace the real command failure, so it skips
non-string entries (caught by vela-media-safety-process tests).

* fix(daemon): increase comment id entropy

* fix(share): keep deployment menu alive through pointer press (OPEND-3537)

* fix(share): unify owner Share entry glyphs (OPEND-3535)

* fix(share): show pending link-stop feedback (OPEND-3513)

* fix(share): retain scoped preflight across panel reopen (OPEND-3536)

* fix(comments): wait for preview targets before lost writeback (OPEND-3516)

* fix(share): restore Owner toolbar hierarchy (OPEND-3580)

* test(share): align toolbar style contract with C0 (OPEND-3580)

* fix(web): preserve owner scroll after comment location

* fix(cli): distinguish share size refusals from connection failures

* fix(web): separate share eligibility and preserve comment identity UX

* test(cli): preserve daemon connection failure exit code

* fix(share): expose owner-scoped persisted stop tasks

* fix(contracts): bound share anchors at original selector lengths

* fix(web): recover pending deleted-share stop notices

* fix(contracts): define atomic share pin snapshot metadata

* test(web): provide retry preflight fixture and await completion

* fix(web): keep billing upgrade outside account identity boundary

* fix(share): protect staging and restore client continuations (#8675)

* fix share staging and client continuations

* docs: clarify share batch verification baseline

* fix(web): notify when sending to a deleted project or conversation

* fix(share): dismiss card share popover on conversation scroll (OPEND-3784) (#8689)

* fix(share): keep anchored popover geometry current (OPEND-3783) (#8694)

* fix(share): expose freshness checks and parallelize state reads (OPEND-3791) (#8708)

* fix(share): preserve safe publish errors and actionable messages (OPEND-3796) (#8709)

* fix(share): preserve safe publish failure details and actionable messages

* test(share): align failure copy expectations and cover classified messages

* fix(share): retain Button binding required by CI merge

* fix(share): add bounded state-read and resume diagnostics (#8710)

* Revert "fix(share): expose freshness checks and parallelize state reads (OPEND-3791)" (#8711)

This reverts commit 8f6c09db39.

The reported latency bottleneck remains unproven. Revert the full PR at the user request and defer presentation optimization. Preserve the subsequent OPEND-3796 changes.

* fix(share): preserve entry binding checks when opening Share (OPEND-3594) (#8712)

* fix(share): report preflight failures and allow explicit retry (OPEND-3771) (#8716)

* fix(share): use green for update-link action (OPEND-3790) (#8717)

* fix(share): retain confirmed PDF export guide during history refresh (#8719)

* fix(analytics): add passive share and comment observations (#8723)

* fix(analytics): define passive share observations and contain measurement failures

* fix(analytics): observe share receipts and selected comments without changing actions

* fix(web): isolate after-export guide observation from actions

* fix(analytics): connect passive guide observations and document minimized fields

* fix(analytics): observe saved comment creation and run handoff passively

* fix(analytics): exclude edited composer drafts from saved-comment attribution

* fix(e2e): align Team run fixtures with authoritative workspace catalog

* fix(analytics): wait for comment acceptance readiness in regressions

* fix(share): 修复超限评论恢复与资源读取失败,补验四项验收问题 (#8721)

* fix(share): recover oversized comments and reject unreadable dependencies

* fix(share): close real export completion and cloud failure gaps

* test(share): align export and delete relay regression assertions

* fix(export): handle interrupted ZIP saves in preview actions

---------

Co-authored-by: lefarcen <935902669@qq.com>
Co-authored-by: a1chzt <chizblank@gmail.com>

* test(share): align export failure assertions with current copy

* fix(share): guard user-scoped export guide dismissal (OPEND-3579) (#8738)

* fix(share): guard user-scoped export guide dismissal (OPEND-3579)

* test(web): align export guide failure assertions with product copy

* test(web): settle restored share state before publish assertions

* fix(chat): keep artifact Share actions static (OPEND-3854) (#8747)

Keep ChatPanel artifact Share actions static and remove card-only publication state tracking. Preserve the existing share panel and readonly/generation gates.

* Merge pull request #8750 from nexu-io/fix/opend-3888-comment-age

fix(web): preserve comment creation age (OPEND-3888)

* fix(comments): persist new static comment identities (OPEND-3516) (#8768)

* fix(comments): preserve authored identities through manual and AI edits

* fix(comment): prepare durable static source identities (OPEND-3516)

* fix(comments): integrate durable identity lifecycle (OPEND-3516)

* fix(comment): validate authored HTML identity tokens (OPEND-3516)

* fix(comment): mask raw text before authored tokenization (OPEND-3516)

* fix(comments): enforce prepared document and publication barriers (OPEND-3516)

* fix(comments): revalidate pending documents and guard updates (OPEND-3516)

* fix(comments): require app-owned source preparation consent (OPEND-3516)

* fix(comments): contain preparation consent focus (OPEND-3516)

* fix(sharing): diagnose project bootstrap failures safely (OPEND-3516)

* fix(comments): retire stale publication guard alerts on explicit retry (OPEND-3516)

* fix(comments): locate Personal identities in the live preview (OPEND-3516)

* fix(comments): surface lost Owner identity anchors (OPEND-3516)

* fix(comments): unblock publication after strict target loss

* fix sharing to keep file publication independent of team visibility (OPEND-3855) (#8759)

* fix sharing to keep file publication independent of team visibility (OPEND-3855)

* refactor sharing to one explicit policy path (OPEND-3855)

* test: isolate metadata snapshots from the trailing identity directive

* fix: align collaboration consumers with explicit project visibility

Carry trusted catalog roots through content receivers and suppress stale Team rows after explicit privacy changes. Read shared design-system brand assets from their resource snapshots. Align stopped-publication ordering and protocol/browser fixtures with the existing contracts.

* test: await explicit comment preparation and independent UI states

Complete the existing source-version consent and real frame-readiness flow before opening the comment sidebar. Bind the response waiter to the UI actions so failures retain their real cause. Await the independently loaded account callout and assert exact catalog-bound materialization wiring.

* test: expect private policy when unsharing a team project

Await the explicit visibility-private command instead of deleting the retained root registration. Assert that remove was not invoked and preserve all member quarantine, directory and file-read privacy checks.

* fix(comments): synchronize file lifetimes across member and public shares (#8764)

* fix sharing to keep file publication independent of team visibility (OPEND-3855)

* fix(comments): synchronize current file lifetimes across members and shares

OPEND-3870 / OPEND-3871. Stack on published OPEND-3855 dependency; preserve reviewed source tree exactly. Local validation does not establish PostgreSQL or native acceptance.

* refactor sharing to one explicit policy path (OPEND-3855)

* fix(collab): adapt comment sync to converged project lifecycle

* test: isolate metadata snapshots from the trailing identity directive

* fix(collab): preserve nested publication and pulled file authority

* fix: align collaboration consumers with explicit project visibility

Carry trusted catalog roots through content receivers and suppress stale Team rows after explicit privacy changes. Read shared design-system brand assets from their resource snapshots. Align stopped-publication ordering and protocol/browser fixtures with the existing contracts.

* fix(collab): preserve relay identity and retire captured file batches atomically

* test: await explicit comment preparation and independent UI states

Complete the existing source-version consent and real frame-readiness flow before opening the comment sidebar. Bind the response waiter to the UI actions so failures retain their real cause. Await the independently loaded account callout and assert exact catalog-bound materialization wiring.

* test: expect private policy when unsharing a team project

Await the explicit visibility-private command instead of deleting the retained root registration. Assert that remove was not invoked and preserve all member quarantine, directory and file-read privacy checks.

* fix(collab): retain the actual native root during mirror materialization

* fix(collab): fail closed on unbound active share identity

* fix(collab): retire comment lifetimes after local mutation commits

* fix(collab): commit local artifact invalidation with working-root changes

* fix(collab): reconcile committed root retirement without stopping new links

* test(collab): align CI fixtures with admitted file identities and receipts

* test(collab): model receipt-backed root file admission in the shared hub

* test(collab): distinguish bound and unbound publication recovery

* chore(pack): pin compatible test CLI and exclude dependency dotenv files

* test(share): verify paired CLI policy and immutable share contracts

* fix(share): remove comment preparation interruptions

* fix(web): offer after-export guide when sharing history fails

* fix(comments): preserve unknown ages and repair synced creation times

* style(web): restore comment reference visuals without changing behavior

* test(web): verify colored avatars and document deployed parity

* fix(share): collect literal JavaScript image dependencies

* fix(web): match canvas comment pins to author avatar colors

* test(comments): align existing UI and E2E color expectations

* test(share): align collab flow and snapshot with direct comments UI

* test(campaign): await verification before advancing impression frames

* test(cms): await verification and mount before impression frames

* fix(web): match canvas comment initials to sidebar avatars

---------

Co-authored-by: PerishCode <perishcode@gmail.com>
Co-authored-by: open-design-crew[bot] <299007234+open-design-crew[bot]@users.noreply.github.com>
Co-authored-by: lefarcen <935902669@qq.com>
Co-authored-by: alchemist <alchemist@alchemistdeMacBook-Pro.local>
2026-10-19 06:15:28 +00:00

150 KiB
Raw Permalink Blame History

Changelog

All notable changes to this project are documented here.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

  • [Fixed] Owner sharing overlays, retained links, and publication progress now match the final sharing design. Published-link popovers retain comment-sync status text and retry actions without a bordered card; a previously published link stays copyable when workspace access is lost.
  • [Changed] Retired the official Nix flake, Home Manager/NixOS modules, and lockfile-coupled Nix CI maintenance. OpenDesign still discovers agent CLIs installed through Nix profiles on NixOS and nix-darwin hosts.

0.9.0 - 2026-05-29

🎉 310 PRs · 88 contributors · 7 days — Meet the install-and-create release. No more API-key scavenger hunts. No more asking teammates to install three different CLIs before their first prompt. OpenDesign AMR is now built into the app: sign in once, pick a model, and start building. Around that zero-config first run, 0.9.0 brings a bigger agent bench, faster model picking, a more discoverable plugin marketplace, richer review workflows, smoother Studio tools, and easier installs across Windows, macOS, and Linux. 🚀

Highlights

  • 🤖 OpenDesign AMR — official AI, one click from a fresh install. The old first-run tax was brutal: install a CLI, find an API key, paste secrets, test auth, debug the shell, then maybe start designing. 0.9.0 cuts that down to the thing users actually wanted: open the app, sign in to AMR, pick a model, and go. Onboarding leads with AMR, the desktop keeps sign-in visible, live model discovery keeps choices fresh, wallet/account states are handled in the UI, image attachments work, and the vela runtime is bundled in the installer. (#2355, #2979, #2980, #3012, #3019, #3048, #3073, #3076, #3088, #3092, #3094, #3097, #3099, #3117, #3127, #3158, #3198, #3226) Thanks @lefarcen, @nettee, @mrcfps, @pftom, @jinmeihong0201-gif, @Caprika.
  • 🆕 The agent bench gets much bigger. Aider, Trae CLI, Antigravity, and DeepSeek Reasonix all join the picker, giving builders more real local-agent paths instead of a single blessed workflow. Aider gets first-class branding, Trae runs over ACP in yolo mode, and the new adapters make OpenDesign feel less like one agent integration and more like the place where agents come to work. (#1970, #2729, #2856, #3157, #2952) Thanks @mrbeandev, @JasonYang0104, @lefarcen, @Bernardxu123.
  • 🔎 Model picking stops feeling like scrolling a phone book. Search now cuts through long model lists, and shared BYOK catalogs keep Settings and the inline switcher aligned so switching models feels fast instead of fiddly. (#3278) Thanks @AmyShang-alt.
  • 💬 Keep talking while the model is still working. Queue chat sends mid-stream, then let OpenDesign continue the conversation the moment the current turn finishes. Studio and Draw now follow the same flow, so capturing an idea does not depend on waiting for the previous response to end. (#2870, #3270, #1961) Thanks @zoeforfun, @lefarcen, @leno23.
  • 🧩 Skills become a real plugin ecosystem. Skill bundles now graduate into first-class Plugins: visible in the drawer, listable from the CLI, indexed on the site, and easier to explain to users. One extension model, one library, one mental model. (#3085) Thanks @Siri-Ray.
  • 🎨 Bigger creative moves are one install away. The official GSAP plugin brings serious web animation into the agent loop, while Research Decision Room turns research prompts into structured multi-role reviews instead of one long answer. (#3109, #3111, #2949) Thanks @Tuola-waj, @mturac.
  • 🌍 The plugin library starts feeling global. The on-site library now mirrors in-app categories and reads natively across supported locales, so visitors land on a catalog that feels built for them instead of partially translated around the edges. (#3010, #2926) Thanks @522700967-wq.
  • 🪟 Plugin and template pages become places you can actually try things. Interactive previews, share rows, and dual CTAs turn detail pages from static listings into a real discovery surface: preview, install, try, share. (#2958, #2924, #2969, #3185) Thanks @522700967-wq, @jinmeihong0201-gif, @ashleytheash.
  • 🪪 Design systems move from files to living assets. Rename them, pin your own to the top, read real swatches from their color tables, and connect design-system projects to GitHub without the zip-file shuffle. (#2812, #2817, #2820) Thanks @portseif.
  • 📅 Routines feel scheduled, not scripted. A real picker, natural-language summaries, newest-first ordering, auto-focus after create, localization, and duplicate-slot cleanup make automations easier to trust. (#2593, #2389, #3035, #2598, #1971) Thanks @Hetsavani, @leno23, @sasha1107, @bulai0408.
  • 💬 Review can keep moving while the artifact changes. Comment mode now supports attachments, live preview updates, and clean deselection, so screenshots and notes stay attached to the work instead of freezing the review flow. (#2869, #2844, #3144) Thanks @zoeforfun, @leessju, @feliciaZH.
  • 📦 Trying OpenDesign gets easier on Windows and Linux. Windows gets a portable zip path; Linux gets Docker/Podman Compose one-click setup. Less install friction, faster first run. (#2937, #2414) Thanks @PerishCode, @epicsagas.
  • 🔌 MCP clients can now do real workspace work. Write files, delete files, delete projects, resolve the active project directory, run generation loops, and bootstrap Codex from one place. External clients can now participate in the OpenDesign workspace instead of only observing it. (#2416, #2802, #3141) Thanks @YOMXXX, @papperrollinggery, @lefarcen.
  • 🛂 Network changes stop requiring a restart. Switch the system proxy while OpenDesign is running; the next outbound request picks it up automatically. (#3093) Thanks @mrcfps.
  • 🌐 The public site gets a proper community front door. Community, Discord/X, Ambassadors, a cleaner footer, localized template pages, and sharper brand details make the project easier to discover and share. (#3066, #3230, #3222, #3218, #3256) Thanks @leilei926524-tech, @522700967-wq.
  • ✏️ Studio's mark tool gets cleaner and faster. Draw and Screenshot merge into one Studio flow, with better preview interactions and clearer queue/screenshot modal behavior. (#3277, #3000, #3215) Thanks @lefarcen, @zoeforfun, @xxiaoxiong.

✨ Added

🤖 Agents & runtimes

  • OpenDesign AMR (vela) as a first-class ACP stdio agent, with onboarding, sign-in pill, and bundled vela CLI inside the installer. (#2355) Thanks @lefarcen.
  • AMR login state read from ~/.amr so the desktop reflects vela's source of truth. (#3048, #3073) Thanks @Caprika.
  • AMR sign-in pill hover-cancel. (#3158) Thanks @Caprika.
  • AMR account-failure and insufficient-balance UI, including wallet URL handling and duplicate recharge-link dedupe. (#2980, #3099, #3117) Thanks @Caprika.
  • AMR Link startup model discovery hardening and faster startup defaults. (#3088, #3092, #3198) Thanks @Caprika.
  • External image attachments staged into the workspace before AMR sends them. (#3226) Thanks @Caprika.
  • Vela CLI packaging updates, including 0.0.3-test.1 pinning, companion packaging, mac Dock identity, AMR label fixes, and 0.0.4 follow-up. (#2979, #3076, #3127, #3239) Thanks @Caprika.
  • Aider as a first-class agent adapter. (#1970) Thanks @mrbeandev.
  • Trae CLI as an ACP coding-agent adapter. (#2729, #2856) Thanks @JasonYang0104.
  • Antigravity agent adapter. (#3157) Thanks @lefarcen.
  • DeepSeek Reasonix CLI support via ACP. (#2952) Thanks @Bernardxu123.
  • CodeWhale detected as a DeepSeek TUI fallback binary. (#3025) Thanks @leno23.
  • Structured connection-test diagnostics so failed probes return why and what to do next. (#2419) Thanks @lefarcen.
  • AMR / Aider / Trae brand marks, plus a refreshed Pi mark. (#2956) Thanks @lefarcen.
  • Model picker search and shared BYOK catalogs across Settings and the inline switcher. (#3278) Thanks @AmyShang-alt.

💬 Chat, comments & Studio

  • Queue chat sends mid-stream, including Draw/Studio send actions while a run is active. (#2870, #3270, #1961) Thanks @zoeforfun, @lefarcen, @leno23.
  • Queue copy and @-mention UI localized for Chinese flows. (#3213, #3255) Thanks @xxiaoxiong, @Derrick-xn.
  • Render code comment directives as a first-class block. (#2871) Thanks @zoeforfun.
  • @-mention skills inject into the system prompt at send time. (#2552) Thanks @lefarcen.
  • Retry no longer duplicates the user message. (#2491) Thanks @leno23.
  • Enter to send in the project chat composer. (#2676) Thanks @leno23.
  • Conversation title editing. (#1926) Thanks @leno23.
  • Feedback prompt on every successful assistant turn. (#2529) Thanks @leno23.
  • Chat pane preserves scroll position when todo cards or long output grow. (#2299, #3187) Thanks @leno23, @leessju.
  • Restore full assistant turn after a mid-flight reload reattach. (#2383) Thanks @leno23.
  • Chat file links route to workspace preview instead of opening a new window. (#2576) Thanks @leno23.
  • Refresh chat skills after Settings skill mutations with no reload. (#3020) Thanks @leno23.
  • Trailing punctuation stays out of markdown and bare URL links. (#2591, #2678) Thanks @leno23.
  • Studio Draw + Screenshot merged into one mark tool. (#3277) Thanks @lefarcen.
  • Studio preview interactions refined. (#3000) Thanks @zoeforfun.
  • Queue screenshot preview modal backdrop improved. (#3215) Thanks @xxiaoxiong.
  • Comment attachment API, live-update preview during Comment mode, and comment deselection. (#2869, #2844, #3144) Thanks @zoeforfun, @leessju, @feliciaZH.

🧩 Skills, plugins, design systems & templates

  • Skill artifacts formalized as Plugins. (#3085) Thanks @Siri-Ray.
  • Official GSAP skill bundle plus review follow-ups. (#3109, #3111) Thanks @Tuola-waj.
  • Research Decision Room skill. (#2949) Thanks @mturac.
  • d3-visualization upstreamed to snow-d3 with expanded metadata. (#1981) Thanks @leessju.
  • Plugins library rebuilt to mirror in-app taxonomy and localized across 18 locales. (#2926, #3010) Thanks @522700967-wq.
  • Plugin detail pages with interactive preview, share row, and dual CTAs. (#2958, #2679) Thanks @522700967-wq.
  • Plugin details modal renders through a portal so deep scroll containers do not clip it. (#3065) Thanks @leno23.
  • Social sharing for template previews, template card grids, and share popovers. (#2924, #3108, #3185) Thanks @jinmeihong0201-gif, @522700967-wq.
  • Plugin authoring composer compacted. (#2492) Thanks @lefarcen.
  • Reject unsafe plugin manifest names and symlinked plugin assets. (#2757, #2036) Thanks @lefarcen.
  • "Add to My plugins" success affordance survives panel remount. (#2897) Thanks @leno23.
  • od-contribute skill guides non-coder contributors through issues, docs, i18n, design-system, and skill submissions. (#3172) Thanks @leilei926524-tech.
  • Design system rename, pinning, swatches, GitHub connect, source flow polish, review polish, published badges, and new-conversation action. (#2812, #2817, #2816, #2820, #3014, #2933, #2848, #2849, #2483) Thanks @portseif, @leno23.

🔌 Automations, MCP & integrations

  • Schedule picker + summary pill UI, newest-first sorting, auto-focus after save, localization, and duplicate-slot cleanup. (#2593, #2389, #3035, #2598, #1971) Thanks @Hetsavani, @leno23, @sasha1107, @bulai0408.
  • MCP write_file, delete_file, and delete_project tools. (#2416) Thanks @YOMXXX.
  • Resolved project directory exposed via MCP. (#2802) Thanks @papperrollinggery.
  • MCP generation loop + one-click Codex install. (#3141) Thanks @lefarcen.
  • Stale auth credentials expire, disallowed connector tools are rejected, connector drawers are polished, and Integrations copy is localized. (#2385, #2006, #2528, #2944, #2563) Thanks @lefarcen, @leno23.

🏠 Home, projects, files & landing

  • Home tab as a singleton, language-aware discovery, prompt cleanup when removing example chips, media fallback, empty-state CTA, status-detail links, and export toast confirmation. (#2580, #2534, #3045, #3070, #3060, #3208, #3183) Thanks @leno23, @lefarcen, @YOMXXX, @Derrick-xn, @Stoobyy.
  • Preview iframe keep-alive so source/preview switches avoid unnecessary reload flash. (#2190) Thanks @bulai0408.
  • Design files directory navigation, folder localization, folder import without entry files, and workspace loading fixes. (#2442, #2701, #2703) Thanks @leno23.
  • Projects-page buttons restored. (#3130) Thanks @leno23.
  • Landing community link, Ambassadors page, Discord/X header entries, footer restructure, community brand mark, template localization, and SEO/brand/favicon updates. (#3066, #3230, #3222, #3218, #3256, #2588, #2596, #2605, #2618, #2561, #2566) Thanks @leilei926524-tech, @522700967-wq, @lefarcen.

📦 Packaging, platform & diagnostics

  • Windows portable zip target alongside the NSIS installer. (#2937) Thanks @PerishCode.
  • Linux Docker / Podman Compose one-click installer. (#2414) Thanks @epicsagas.
  • Packaged diagnostics capture daemon and web logs. (#3126) Thanks @lefarcen.
  • PowerShell 7 fallback and native pnpm executable invocation fixes for Windows/package flows. (#2799, #2144) Thanks @lefarcen, @jinha-hwang-hajong.
  • Linux AppImage manifest fields completed, with download and host packages bundled into the AppImage assembly. (#2276, #2845) Thanks @PerishCode, @youcefzemmar.
  • Docker startup without OD_API_TOKEN fixed. (#2928) Thanks @epicsagas.
  • Packaged desktop honors OD_DATA_DIR. (#2162) Thanks @PerishCode.
  • Live system proxy changes. (#3093) Thanks @mrcfps.
  • Reconcile missing artifact manifests on run end. (#3110) Thanks @lefarcen.
  • HTTP keep-alive widened and hostname OD_ALLOWED_ORIGINS same-origin GETs honored. (#2557, #2478) Thanks @lefarcen.

🔁 Changed

  • Next.js Turbopack enabled for the dev server. (#2798) Thanks @leessju.
  • .jsx module previews point at their HTML entry and srcdoc artifacts render directly after leaving URL-load. (#2748, #3042) Thanks @lefarcen, @leno23.
  • Tracking for Automations, Plugin Detail, Loop, comment save/send, and GA4 landing config. (#3103, #3098, #2615) Thanks @lefarcen, @elihahah666.
  • French UI locale refreshed and additional template/plugin surfaces localized. (#2963, #3218, #3256) Thanks @davezfr, @522700967-wq.

🐛 Fixed

  • Codex launch permissions aligned on Windows / WSL and stale BYOK OPENAI_API_KEY stripped before spawn. (#3037, #2441) Thanks @lefarcen.
  • Gemini BYOK model URL normalization for full generateContent URLs. (#2761) Thanks @lefarcen.
  • Claude "Not logged in" maps to /login guidance. (#3050) Thanks @leno23.
  • Per-agent detection failures isolated so one bad probe cannot blank the picker. (#2444) Thanks @lefarcen.
  • Agent executable paths hidden from chat status. (#3046) Thanks @leno23.
  • BYOK API mode warning and Local CLI finalize BYOK requirements clarified. (#2943, #3041) Thanks @lefarcen, @leno23.
  • Preview iframe no longer steals focus on load. (#2792) Thanks @lefarcen.
  • Inline model switcher labels clip instead of overflowing. (#2732) Thanks @lefarcen.
  • Sketch save button shows saving/saved feedback. (#2500) Thanks @leno23.
  • RTL avatar popover anchors left so it does not clip off-screen. (#2764) Thanks @lefarcen.
  • HTML preview restored after Source toggle. (#2710) Thanks @lefarcen.
  • Subtab pill gaps, tab search dismissal, plugin-card Chinese actions, video badges/toolbars, attachment tray height, attachment modal stacking, tab dividers, and memory alignment polished. (#3134, #3102, #2424, #3252, #2896, #3168, #3186, #3105, #3175) Thanks @lefarcen, @leno23, @YUHAO-corn, @xxiaoxiong, @RoverKai, @Derrick-xn.

0.8.0 - 2026-05-20

The rebuilt-core release: everything is a plugin, headless by default, plugins create plugins. OpenDesign's research-preview architecture has been replaced with a small, boring engine plus a plugin surface — design systems, slices, prototypes, exports, and Figma itself all live in plugins now. The desktop app is a thin wrapper around the OD CLI, so the same engine runs in Claude Code, OpenClaw, Hermes Agent, and chat bots in Lark / Discord / Slack. Critique Theater matures through Phase 16 (rollout ratchet, conformance API, 9 Prometheus metrics, Grafana dashboard, M0 dark-launch by default). 149 design systems now ship with structured tokens.css + components manifests across 60+ new brand fixtures. Italian (it) locale + CJK font fallback. New media providers: Leonardo.ai, ElevenLabs, SenseAudio. Packaged auto-update lands on both macOS and Windows, battle-hardened through the preview cycle. Plus a top-to-bottom visual refresh, Quick-brief discovery overhaul, PostHog v2 analytics schema, manual edit UX overhaul (focus mode, uploads, remove-element patch), custom CLI agent profiles, and HTML Anything landing page. 305 merged PRs by 75 contributors since 0.7.0.

Added

Plugin engine, registry & publishing

  • Plugin engine rebuild with packages/plugin-runtime, packages/registry-protocol, and packages/host — the engine surfaces the plugin lifecycle through a small, neutral API so design systems, slices, prototypes, exports, and even Figma itself can live as plugins.
  • Plugin registry detail drawer with trust badges and marketplace metadata. (#2087)
  • GitHub rate-limit fallback for marketplace plugins keeps install / refresh flows reliable when GitHub API is throttled. (#2064)
  • Plugin Publish-repo flow creates the author's repo correctly. (#2332, #2363)
  • CLI plugin publish reads manifest version when the stored row is the 0.0.0 sentinel. (#1903)
  • Block raw publish CLIs from the authoring summary — keep agents on the OD publish path. (#2380)
  • Demote Plugins + Integrations to the nav rail footer so primary surface stays focused. (#1806, #2360, #2397)

Critique Theater (Phases 9 – 16)

  • Phase 9 — drop-in mount wrapper, native i18n for de / ja / ko / zh-TW. (#1315)
  • Phase 10 — daemon adapter conformance lab + degraded registry. (#1316)
  • Phase 11 — Playwright stage suite (happy path, interrupt, 3 viewports, a11y). (#1317, #1483)
  • Phase 12 — 9 Prometheus metrics + 6 log events + OTel span + Grafana dashboard. (#1485)
  • Phase 13 — reducer p99 benchmark + surface coverage walker. (#1318)
  • Phase 15 — rollout resolver + Settings toggle hook. (#1320)
  • Phase 16 — M-phase rollout ratchet + /api/critique/conformance. (#1499)
  • Wireup with M0 dark-launch by default. (#1338)
  • Settings toggle with dedicated section + i18n keys across 6 locales. (#1484)

Design systems & tokens

  • Token channel default-on (PR-D) so the new fixture pipeline is the default surface. (#1544)
  • Structured tokens.css for 60+ new brands across AI, devtool, SaaS, fintech, docs, consumer, hardware, cultural categories (Apple, Stripe, Airbnb, Vercel, Notion, Linear, GitHub, Figma, Slack, Discord, OpenAI, Shopify, Spotify, Uber, Cursor, and many more). (#1652, #1794, #1841, #2023, #2028, #2029, #2033)
  • Token fixture catalog — 20 brand + 20 product + remaining style fixtures, component-fixture coverage report. (#2037, #2040, #2043, #2049)
  • Component manifests — extract + consume manifests for design systems. (#2051)
  • Import design-system projects via the discovery flow.
  • Perplexity design system. (#1747)

Agents, providers & media

  • Local custom CLI agent profiles for arbitrary CLI agents. (#378)
  • Leonardo.ai image provider. (#1123)
  • ElevenLabs audio support. (#1384)
  • SenseAudio TTS provider + BYOK chat with image / video generation tools. (#1633, #2065)
  • User-configurable model alias for the media dispatcher. (#1277)
  • Cursor Agent live model id parsing + auth diagnostics. (#1538, #2228)

Web UI

  • Manual edit UX overhaul — focus mode, inline uploads, remove-element patch. (#1516)
  • Manual edit inspector. (#1448)
  • Tweaks toolbar bound to the artifact panel (toggle visibility from the panel chrome).
  • Custom select primitive for cleaner dropdowns.
  • Collapsible comment side panel.
  • Export as image in the share menu.
  • Render GFM tables in markdown artifacts and chat.
  • Surface saved Project instructions for review and retrieval.
  • Copy-to-clipboard for user messages.
  • Filter-by-kind dropdown on the design-files viewer.

Discovery & onboarding

  • Quick-brief: collapse freeform clarification into a single form. (#2226)
  • Plugin inputs as authoritative Quick-brief answers. (#2243)
  • Stabilize discovery brand answers in prompts. (#1861)
  • Daemon surfaces discovery form answers to agents. (#2071)

Desktop & packaging

  • Packaged auto-update for both macOS and Windows. (#2362, #2270, #2403)
  • Updater hardening through the preview cycle — release validation, deferred installer on Windows, applied-state clearing, download / install handoff hardening, smoke-recovery. (#2565, #2575, #2592, #2595, #2677, #2687, #2700)
  • Desktop updater UI flow — new in-app updater popup.
  • Packaged update apply observations captured for telemetry / debugging. (#2429)
  • Prerelease + preview package identity so beta installs don't collide with stable. (#2437)
  • macOS Dock icon stays put when desktop-pet window opens. (#2413)
  • Refresh OpenDesign app visuals — new app icons, logo, brand glyphs. (#2436)
  • Linux packaged client parity smoke coverage.
  • Ensure node binary dir is on PATH for agent sub-processes on Windows. (#1989)

Internationalization

  • Italian (it) locale — full UI translation, brings supported languages to 19. (#1323)
  • CJK font fallback for Chinese / Japanese / Korean. (#2227)
  • Refresh + polish French UI locale.
  • Translate template platform selection + Companion surfaces to Chinese. (#1491)
  • Localize accent controls in settings, comment-panel strings (#1390, #1392), and skill validation messages.

Analytics, observability & infra

  • PostHog v2 event schema. (#2285)
  • Unify page_name + onboarding / design-system page_views. (#2390)
  • Upgrade posthog-node 4 → 5 in the daemon. (#2309)
  • One-click log export from Settings → About.

Templates, landing & tutorials

  • HTML Anything page + responsive landing header. (#2452)
  • Rebuild /templates catalog from design-templates. (#2369)
  • Refresh templates + add tutorials channel on the landing site. (#2409)
  • Blog routes on the landing site.
  • Search Console reporting workflows + GSC report opportunities. (#2388)
  • WeRead year-in-review HyperFrames template.

Changed

  • Critique Theater dark-launched at M0 by default, gated through the new rollout ratchet so phases can be promoted independently.
  • Plugin trust badges unified across registry surfaces.
  • Plugins and Integrations moved to the nav rail footer (#1806, #2360, #2397) — keep primary surface focused.

Fixed

Web

  • Block pitch-deck placeholder publishes and unbreak framework decks.
  • Rename FileViewer "Share" button to "Export".
  • Confirm before deleting a saved template in New Project.
  • Restore consistent app header layout on the entry view. (#1519)
  • Refine preview and project dropdown controls. (#1514)
  • Pin chat during content growth.
  • Auto-scroll feedback form.
  • Routines history rows deep-link to their specific conversation. (Fixes #1505)
  • Hide resolved comments from preview overlays.
  • Keep filter pill hover labels readable.
  • Improve replace-modal button hover contrast.
  • Freeze completed run durations across conversations.
  • Align Home prompt overlay with textarea so caret lands on click.
  • Restore release-light background. (#1540)
  • Allow downloads from preview iframes; fall back to srcDoc when HTML preview needs sandbox shim.
  • Coalesce chokidar rewrite bursts before refreshing files.
  • Reveal memory editor after edit click; distinguish expanded memory preview action.
  • Auto-annotate imported HTML elements for Tweaks selection. (#892)
  • Stable shared frame screen paths from referrer.
  • Restore custom dropdown chevron for timezone selector in dark mode.
  • Daemon run recovery across reloads. (#2374)

Desktop & packaging

  • macOS Dock icon stays put when desktop-pet window opens. (#2413)
  • Align Windows smoke update root with portable installs. (#2376)
  • Prerelease release smoke identity. (#2446)
  • Improve desktop updater ready UI. (#2403)
  • Forward proxy env vars to packaged sidecars.
  • Detect mise-installed npm package bins.
  • Launch Windows updater fixture via Node. (#2364)
  • Desktop "Export PDF" opens a direct "Save as PDF" file dialog and writes the PDF to disk, instead of opening the macOS system print dialog. (Fixes #1774)
  • macOS close exits fullscreen before hiding.
  • Daemon's external-browser opener fixed on Windows.

Daemon, runtime & connectivity

  • Surface discovery form answers to agents. (#2071)
  • Stabilize discovery brand answers in prompts. (#1861)
  • ACP model detection timeout is configurable.
  • Wrap Claude smoke test stdin as stream-json.
  • Preserve Claude tool inputs. (#1476)
  • Codex CLI path fallback UX. (#1205)
  • Treat Codex reconnect events as warnings, not fatal errors. (#1482)
  • ACP config options used for model selection. (#1208)
  • Remove OpenCode stdin dash sentinel; soft empty API response handling.
  • Forward external MCP servers to OpenCode.

Documentation

  • Critique Theater Phase 14 user guide + 2 AGENTS module maps. (#1319)
  • Windows native setup notes in AGENTS.md.
  • Comprehensive contributor guide in TRANSLATIONS.md.
  • RTL_LOCALES UI guidance + es-ES alignment.
  • Sync zh-TW README with the English version.
  • Sync Windows troubleshooting link across locale READMEs.
  • Refresh contributors wall + GitHub metrics SVG.
  • Clarify Intel Mac ZIP packaging support (includes the Monterey verified path and the Finder PATH caveat for packaged CLI detection). (Fixes #327)
  • README inventory badges sync — skills 31 → 131, design-systems 72 → 149. (#1899)
  • 0.8.0-preview banner + Discussion #1727 pointer. (#1781)
  • Active 0.8.0 contributors point at main. (#1846)

Internal

  • Critique Theater Playwright stage suite (happy, interrupt, 3 viewports, a11y). (#1317, #1483)
  • Reducer p99 bench + surface coverage walker. (#1318)
  • Harden e2e extended coverage state assertions. (#2245)
  • Visual regression PR workflow (CI).
  • Component manifest extraction + daemon consume path. (#2051)
  • OD CLI wraps GitHub CLI (so plugins create plugins).
  • pnpm i18n:coverage informational report.
  • Issue templates: bug, feature, preview/v0.8.0 + chooser config. (#1708)

0.7.0 - 2026-05-12

A memory-plus-UI release: auto-memory store carries agent context across runs and projects, Critique Theater advances to Phase 7 (state machine + replay) with daemon-side Phase 6.2 artifact extraction, HyperFrames lands HTML-in-Canvas end-to-end, and the web UI gets a top-to-bottom Designs tab redesign, in-context preview comments, a unified Media tab, and a tweaks palette with HSL hue-shift recoloring. Plus responsive design handoff outputs, install/uninstall skills & design systems in-app, HTTP 206 range requests for video/audio, scheduled routines for unattended agent runs, macOS Intel (x64) builds, an official Nix flake, four new design systems (hud, loom, trading-terminal, WeChat), and an agent-browser skill. 107 merged PRs since 0.6.0.

Added

Critique Theater

  • Phase 7 — web client state machine. Reducer plus useCritiqueStream / useCritiqueReplay hooks so the critique UI replays cleanly and reconnects mid-run. (#1307)
  • Phase 6.2 — daemon artifact extraction + endpoint. Critique-tagged artifacts are extracted server-side and exposed via a dedicated endpoint. (#1085)

Web / UI

  • Designs tab redesign — cards with covers, tags, overflow menu, and multi-select. (#1161)
  • In-context comment thread for the artifact preview — comment directly on a preview without leaving the workspace. (#1276)
  • Unified Media tab — Image / Video / Audio entries consolidated into a single tab. (#1167)
  • Tweaks palette popover with HSL hue-shift recoloring — adjust hue/saturation/lightness inline. (#1292)
  • Responsive preview + design handoff outputs — tablet/mobile preview auto-fit, 2025–2026 responsive viewport matrix, screen-file-first export policy, DESIGN-HANDOFF / DESIGN-MANIFEST exports for coding-tool implementation. (#1224)
  • Thumbs-up / thumbs-down feedback widget under completed assistant turns. (#1308)
  • Cmd/Ctrl+, opens Settings with a platform shortcut badge in the menu. (#1173)
  • Finalize design package + Continue in CLI buttons. (#974)
  • Fetch models button for BYOK providers so newly added models discover themselves on demand. (#1034)
  • Provider-model fetch results sorted alphabetically. (#1097)
  • Collapsible MCP JSON field-mapping helper. (#1136)
  • Question forms scroll to top of viewport instead of pinning to the bottom. (#1044)
  • Sidebar tabs flush to the workspace edge with internal scrolling. (#1038)
  • Design file rename support. (#894)

Daemon, agents & runtime

  • Auto-memory store with chat-protocol-aware extraction. Agents accumulate durable context across runs and projects. (#999)
  • Install / uninstall for skills & design systems directly from the desktop app. (#1003)
  • HTTP 206 range request support for video / audio files (closes #784). (#1105)
  • Scheduled routines for unattended agent runs. (#1033)
  • Guard against agent-emitted stub artifact regressions. (#1171)
  • Reject filesystem root folder imports. (#1266)
  • Split agent runtime definitions into per-runtime modules. (#1063)
  • Split route registration into focused modules. (#1043)

HyperFrames

  • HTML-in-Canvas lands across web + skills. Embedded HTML renders inside canvas-mode templates end-to-end. (#866)

Skills, design systems & prompt templates

  • Generic skills + skills/design-templates split + finalize-design API. Skill model refactor that separates code-driven skills from design-template assets. (#955)
  • Reliable agent-browser skill. (#1284)
  • WeChat design system + login-flow skill (also fixes API-mode tool_calls bug). (#1083)
  • Three new design systems: hud, loom, trading-terminal with locale coverage. (#1069)
  • release-notes-one-pager skill with supporting docs. (#873)
  • Improve design files grouping. (#1082)
  • Replace time-specific Orbit greetings with neutral defaults. (#1291)

Design Systems infrastructure

  • tokens.css schema for design systems (default + kami). (#1231)

Settings & onboarding

  • Install onboarding links for unavailable local CLIs. (#985)

Packaging & distribution

  • macOS Intel (x64) build support in release workflows. (#759)
  • Official Nix flake with home-manager and NixOS support. (#402)
  • Beta release packaging cache optimizations. (#1095)

Internationalization

  • Traditional Chinese QUICKSTART + Chinese doc links fixed. (#753)

Changed

  • Conversation run isolation enforced — concurrent runs no longer cross-contaminate state. (#1271)
  • Default English resource i18n fallback so missing translations don't break agent prompts. (#1270)
  • [codex] Claude Code exit diagnostics improved. (#1267)
  • [codex] empty API responses handled as no output (not as errors). (#1244)
  • Codex CLI path fallback UX improvements. (#1205)

Fixed

Web

  • Persist Appearance accent color selection so swatch picks survive Settings close. (#1439)
  • Load Orbit template choices from design-templates instead of skills (aligns with the skill model refactor). (#1442)
  • Restore custom dropdown chevron for the timezone selector in dark mode. (#1368)
  • Polish EntryView UI — sidebar layout, folder tabs, slim form, blue selected token. (#1360)
  • Translate Design Files refresh strings instead of hardcoding English. (#1300)
  • Pretty-print JSON file previews. (#1206)
  • Keep Tweaks selection usable without annotations. (#1268)
  • Render static previews for sketch JSON files. (#1060)
  • Ignore <artifact> tags inside markdown code spans and fences. (#1132)
  • Refresh home projects after deleting a conversation. (#1219)
  • Complete finished tool calls missing results. (#1240)
  • Intercept prose-as-HTML artifacts before they hit disk. (#1144)
  • Truncate entry footer pet label. (#1150)
  • Surface connector auth errors and stop silent popup close. (#1128)
  • Center close button in MCP picker dialog. (#1137)
  • Prevent chat messages overflowing into the workspace area. (#1104)
  • Prevent creating a new conversation when the current one is empty. (#1086)
  • Dispatch Examples preview on od.preview.type. (#1001)
  • Scroll Settings content back to top on section change. (#997)
  • Allow pod-to-chat comment text to wrap instead of truncating. (#1156)
  • Localize MCP server settings panel for Chinese UI. (#760)
  • Restore media config from daemon on startup. (#687)
  • Suppress autosave indicator for draft-only Connector key edits. (#1232)
  • Fix link handling in example preview iframe sandbox. (#701)
  • Handle popup-blocked PDF export with native Electron print dialog. (#973)
  • Translate comments panel UI to Chinese. (#1139)
  • Hide surface filter tabs with zero count in Design Systems view. (#965)
  • Prevent design system filter popover from shifting position on reopen. (#960)

Desktop

  • Exit fullscreen before hiding window on macOS close. (#1249)
  • Enforce minimum window size on main client. (#1203)
  • Allow about:blank popup for PDF export fallback. (#1081)
  • Fix daemon browser opener on Windows. (#953)

Daemon, agents & contracts

  • Wire finalized assistant message writes to the Langfuse report bridge so reports settle reliably. (#1402)
  • Remove OpenCode stdin dash sentinel. (#1365)
  • Use ACP config options for model selection. (#1208)
  • Persist runStatus / endedAt on chat run termination. (#1230)
  • Prefer opencode-cli over opencode binary so OpenCode Desktop installs resolve to the CLI. (#818)
  • Support OpenCode Write tool display as card. (#1126)
  • Pin API-mode override above discovery layer. (#1207)

Packaging

  • Close running app before silent reinstall on Windows. (#1238)
  • Build desktop before packaged typecheck. (#1093)
  • Prevent project type selector arrows from overlapping tabs. (#1091)
  • Increase top padding in modal footer for better visual balance. (#957)

Misc

  • Clear stale upload failure banner when previewing files. (#797)
  • Remove Trump pet from bundled community pets. (#1103)
  • Add "When NOT to emit" guardrail to artifact handoff prompt. (#1145)
  • Pending prompt clearing for templates. (#1148)
  • Set writable OD_DATA_DIR default for nix run. (#1159)
  • Landing-page SEO: correct canonical, add robots.txt + favicons. (#1061)

Documentation

  • MAINTAINERS.md + CONTRIBUTING entry-point for maintainer rules. (#1290)
  • Traditional Chinese QUICKSTART + Chinese doc link fixes. (#753)

Internal

  • Stabilize extended Playwright coverage. (#1341)

  • Expand prerelease UI and desktop regression coverage. (#1256)

  • Harden e2e smoke and release reports. (#1140)

  • Expand entry and settings automation coverage. (#954)

  • Refreshed generated GitHub metrics SVG and contributors wall. (#1115, #1117, #1183, #1188, #1328, #1330)

  • Plugin & marketplace system — Phase 2A + 1 + 1.5 + 2B + 2C entry slice + 3 (full) + 4 (full incl. OD_BUNDLED_ATOM_PROMPTS default ON) + 5 (full incl. live S3 impl; postgres adapter still stubbed) + 6 (full incl. asset rasterisation) + 7 (all six code-migration atom impls landed; full pipeline e2e covered by smoke test) + 8 (full incl. GenUI \u2192 decision bridge + handoff promotion ladder bridge) + bundled scenarios + bundled-scenario fallback resolver. Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • od plugin events purge admin escape hatch (Phase 4). New purgePluginEventBuffer() returns pre-purge stats { purged, firstId, lastId, preNextId } so an operator can audit what was discarded. Loopback-only POST /api/plugins/events/purge route + od plugin events purge --confirm CLI subcommand (refuses to run without --confirm so a stray invocation never drops audit data accidentally).
    • od plugin manifest <id> + od plugin sources (Phase 4). New CLI subcommands for plugin authors and ops: od plugin manifest <id> prints just the parsed manifest JSON (no wrapper, no fsPath / installedAt noise) so authors can diff against their on-disk open-design.json. od plugin sources lists every distinct (sourceKind, source) tuple + plugin count, sorted by descending count then alphabetically, with the per-bucket plugin list sorted by id. New pluginSourceBuckets() pure helper backs the CLI for byte-deterministic output.
    • od plugin events snapshot/stats + tail filters (Phase 4). Extends §3.II1 with: GET /api/plugins/events/snapshot for non-SSE one-shot reads (dashboards that don't want a live connection); GET /api/plugins/events/stats returns a summarisePluginEvents() rollup (counts byKind, byPluginId — skipping empty ids — plus oldest/newest at + id range); --kind <k> and --plugin-id <id> filter flags work on both od plugin events tail (client-side post-render) and the new od plugin events snapshot subcommand. CLI pretty-prints the stats rollup with sorted-key counts for byte-determinism.
    • More plugin event producer hooks (Phase 4). Extends §3.II1 with: installPlugin accepts eventKind: 'installed' | 'upgraded' so the upgrade route distinguishes the operation in the live tail; POST /api/plugins/:id/trust emits plugin.trust-changed; POST /api/applied-plugins/prune emits plugin.snapshot-pruned when anything was actually removed; POST /api/marketplaces/:id/refresh emits plugin.marketplace-refreshed. Each hook is best-effort and never blocks the underlying mutation if the ring buffer throws.
    • Plugin event ring buffer + SSE tail (Phase 4). New apps/daemon/src/plugins/events.ts ships an in-memory FIFO ring buffer (capped at 1000 entries, monotonic ids, fan-out subscribers) for plugin lifecycle events: plugin.installed / .upgraded / .uninstalled / .trust-changed / .applied / .snapshot-pruned / .marketplace-refreshed. Producer hooks landed on the installer (install + uninstall). New GET /api/plugins/events SSE route emits the backlog on connect (with optional ?since=<id> trim) then forwards live events. CLI: od plugin events tail [-f] [--since <id>] [--json] — non-follow mode drains backlog + exits; -f keeps the stream open for ops dashboards.
    • od plugin doctor --strict + verify strict propagation (Phase 4). New --strict flag on od plugin doctor promotes warnings to failures (exit 4 distinguishes 'strict failed' from doctor errors at exit 1). The verifyPlugin() orchestrator gains a matching strict: true config knob that flows through the plugin verify config file so plugins can lock 'no warnings allowed' as a one-line CI policy.
    • od daemon db verify SQLite integrity check (Phase 5). New verifySqliteIntegrity() pure helper wraps PRAGMA integrity_check (or quick_check with --quick) + PRAGMA foreign_key_check. Returns a structured { ok, mode, issues[], elapsedMs, generatedAt } report with issues tagged kind='integrity' | 'foreign_key'. Loopback-only POST /api/daemon/db/verify route + od daemon db verify [--quick] CLI subcommand — exit 0 on ok=true, 4 on any issue, so CI can wire it into a pre-deploy check.
    • od daemon db vacuum (Phase 5). New loopback-only POST /api/daemon/db/vacuum runs SQLite VACUUM and reports before/after sizes + reclaimed bytes + elapsed ms. Useful after large delete batches (snapshot prune, plugin uninstall) shrink rows but leave space allocated to the file. CLI: od daemon db vacuum [--json].
    • od daemon db status SQLite inventory (Phase 5). New inspectSqliteDatabase() pure helper + GET /api/daemon/db route returns a structured report: kind ('sqlite'), file location, size on disk (primary + WAL + SHM), schema version (user_version PRAGMA), and per-table row counts (system tables excluded, lexicographic order). CLI: od daemon db status [--json] lets ops sanity-check deployments at a glance + compare expected-vs-actual table rosters across daemon upgrades.
    • od plugin verify <id> CI meta-command (Phase 4). New verifyPlugin() pure orchestrator aggregates doctor + simulate + canon --check into one pass/fail report. Reads the plugin verify config file (or --config <path>) so plugin authors commit their CI checks into their repo. Each check resolves to passed | failed | skipped | unsupported; aggregate passes iff every enabled check is passed or skipped (unsupported bubbles up as a fail to keep CI honest). One-liner CI workflow: od plugin verify my-plugin — exit 0 on pass, 4 on fail, 2 on CLI/config error.
    • od plugin simulate <id> pipeline dry-run (Phase 4). New simulatePipeline({ pipeline, signals, iterationCap? }) pure helper walks every stage in a plugin's pipeline against caller-supplied signals (constant snapshot OR per-iteration generator function) and reports outcome ∈ { single | converged | cap | unparsable } per stage plus aggregate outcome ∈ { all-converged | all-single | mixed | cap-hit | unparsable }. Companion parseSignalKv() parses repeatable -s key=value CLI flags into the closed UntilSignals vocabulary with typo guards. CLI: od plugin simulate <pluginId> [-s key=value ...] [--cap <n>] [--json] — exit 4 on cap-hit/unparsable so CI can hook this into a pipeline check.
    • od plugin stats inventory health report (Phase 4). New pluginInventoryStats() + snapshotInventoryStats() pure helpers aggregate installed-plugin counts (by sourceKind / trust / taskKind, bundled vs. third-party split, plugins with elevated capabilities — fs:write / subprocess / bash / network / connector:*) and snapshot health (status breakdown, project / run linkage, oldest / newest applied timestamps). New GET /api/plugins/stats route + od plugin stats [--json] CLI subcommand for at-a-glance fleet audit.
    • od plugin canon --check <expected-file> byte-equality fixtures (Phase 4). New --check mode on od plugin canon compares the canon output against an on-disk fixture and exits 4 on mismatch with a per-line diff preview. Lets plugin authors commit renderPluginBlock() regression fixtures into their own tests/ without writing a fresh test harness.
    • od plugin canon <snapshotId> show prompt block (Phase 4). New GET /api/applied-plugins/:snapshotId/canon route renders the canonical ## Active plugin / ## Plugin inputs block this snapshot splices into the system prompt, byte-equal to what the agent reads via composeSystemPrompt. Plain-text response when the request sends Accept: text/plain so shell pipes work cleanly; JSON wrapper otherwise. CLI: od plugin canon <snapshotId> [--json].
    • od plugin snapshots show / diff debugging (Phase 4). New GET /api/applied-plugins/<id>-backed od plugin snapshots show <snapshotId> dumps a full AppliedPluginSnapshot for inspection. New diffSnapshots() pure helper + od plugin snapshots diff <a> <b> compares two snapshots field-by-field with a digestEqual flag that surfaces the e2e-2 invariance check at-a-glance. Compares identity, inputs map, capabilities, resolvedContext, connectors, mcpServers, genuiSurfaces, pipeline (stages + per-stage atoms / until), assets. Entries sort lexicographically so output is byte-deterministic.
    • od plugin diff <a> <b> author tooling (Phase 4). New diffPlugins({ a, b }) pure-helper compares two InstalledPluginRecord values and returns a structured { pluginId?, entries[], added, removed, changed } report. Compares top-level fields (id, version, sourceKind, source, trust, capabilitiesGranted) plus manifest body (title / version / description / license / tags / kind / taskKind / mode / capabilities / inputs / context.skills / context.craft / context.assets / pipeline.stages + per-stage atoms / connectors.required / genui.surfaces). Collection diffs collapse to <n> added, <m> removed summaries; entries sort lexicographically by field path so the report is byte-deterministic across re-runs. CLI: od plugin diff <id-a> <id-b> [--json] renders +/-/~ glyphs.
    • od atoms info <id> + atom catalog drift fix (Phase 4). New GET /api/atoms/:id returns catalog metadata + the bundled SKILL.md body. Catalog promoted nine atoms (code-import, design-extract, figma-extract, token-map, rewrite-plan, patch-edit, diff-review, handoff) from status='planned' to 'implemented' to reflect the daemon impls landed across §3.N–§3.S; build-test added as a missing catalog row. Net: zero planned atoms remaining.
    • od plugin upgrade <id> re-install from recorded source (Phase 4). New POST /api/plugins/:id/upgrade route streams the same SSE shape as /install, internally calling installPlugin() against the plugin's recorded source string (so the github / https / local-folder byte path replays end-to-end). 409s with code='bundled-plugin' when the plugin shipped bundled with the daemon (those upgrade with the daemon image), and code='missing-source' when the recorded source is empty. CLI: od plugin upgrade <id> — exit 0 on success, 1 on installer error.
    • patch-edit atomic file writes (Phase 7 safety patch). Every fs.writeFile inside patch-edit (file content + plan/steps.json + plan/receipts/) now routes through an atomicWriteFile() helper that writes to a sibling tmp file and renames into place via POSIX rename(2). A daemon crash mid-write can no longer leave the source file truncated; rejection-path failures (context mismatch, etc.) leave the original byte-equal. Cleanup path unlinks the orphan tmp on failure so disk noise stays bounded.
    • od plugin search <query> + filters on od plugin list (Phase 4). New searchInstalledPlugins({ plugins, query?, taskKind?, mode?, tag?, trust?, bundled? }) pure-helper ranks free-text matches across id / title / description / tags (exact wins over substring; tag-exact ranks ahead of title-substring) and AND-combines with structural filters. CLI gains od plugin search <query> and adds --task-kind / --mode / --tag / --trust / --bundled / --no-bundled knobs to od plugin list. Search results show matched=[…] per row so the user can trace which field caught the hit.
    • od plugin pack <folder> distribution archive (Phase 4). New packPlugin({ folder, out? }) helper builds a gzip-compressed tar archive of an author's plugin folder, ready to install via the installer's HTTPS-tarball path. Default output path is <basename>-<manifest.version>.tgz beside the folder. Skiplist matches the installer's extract-time exclusions (node_modules, .git, dist, build, out, coverage, .turbo, .cache, .pnpm-store, .DS_Store, etc). Symlinks rejected at both walk and tar-filter passes so what authors pack matches what the installer accepts. The output archive itself is always excluded from its own contents (no spiral when --out lands inside the folder).
    • od plugin validate <folder> author-side lint (Phase 4). Pre-install lint pass against an unfinished plugin folder. New validatePluginFolder({ folder, registry? }) helper reads the folder via the same resolvePluginFolder() the installer uses (manifest parsing is byte-equal to install time) and runs doctorPlugin() against the supplied registry view. Surfaces atom-id mismatches, manifest parse errors, unparseable until expressions, and unresolved skill / DS / craft refs without dirtying the registry table. CLI takes --no-daemon for fully-offline runs and --json for machine-readable output. Exit code 4 when doctor.ok=false, 2 on CLI/folder errors, 0 on clean.
    • OD_BUNDLED_ATOM_PROMPTS default flipped to ON (Phase 4). With every Phase 6/7/8 atom impl shipping its own SKILL.md fragment (build-test, code-import, design-extract, figma-extract, token-map, rewrite-plan, patch-edit, diff-review, handoff), the audit-block on flipping the default is lifted. Runs with a plugin snapshot now ALWAYS get the matching ## Active stage blocks spliced into the system prompt without operators having to set the env var. Set OD_BUNDLED_ATOM_PROMPTS=0 to opt out (snapshot replay against pre-flip daemons, regression bisects that need byte-equal prompts). Runs without a snapshot stay byte-equal so the change is invisible outside the plugin path.
    • S3ProjectStorage live implementation via AWS SigV4 (Phase 5). New apps/daemon/src/storage/aws-sigv4.ts ships a minimal AWS Signature V4 signer using only node:crypto (no @aws-sdk/* dependency \u2014 keeps the daemon shippable as a small binary). The signer is verifiably correct against the AWS-published reference vector (GetObject example). S3ProjectStorage now implements all five ProjectStorage ops (read / write / list / delete / stat) via globalThis.fetch, with virtual-host-style URLs by default and path-style for OD_S3_ENDPOINT overrides (Aliyun OSS, Tencent COS, Huawei OBS, MinIO). LIST walks NextContinuationToken for paginated buckets. Credentials read from OD_S3_ACCESS_KEY_ID/SECRET/SESSION_TOKEN first, falling back to AWS_* env vars so existing IAM-role / aws configure setups drop in unchanged.
    • runHandoffAtom() pipeline-driven bridge (Phase 8 entry slice). New helper reads the canonical state previous atoms wrote (<cwd>/review/decision.json from diff-review, <cwd>/critique/build-test.json from build-test) and returns the updated ArtifactManifest with the right handoffKind / exportTargets[] attached. Promotion ladder (spec §11.5.1): reject \u2192 design-only; accept/partial no-build-test \u2192 implementation-plan; + build.passing\u2228tests.passing \u2192 patch; + both signals + docker/cli export \u2192 deployable-app. Monotonicity enforced via recordHandoff(); a manifest carrying 'patch' won't demote to 'design-only' even when a follow-up reject arrives.
    • runAndPersistHandoff() + auto-handoff from diff-review GenUI bridge (Phase 8 entry slice). New on-disk shell round-trips <cwd>/handoff/manifest.json: reads existing manifest (or falls back to manifestSeed, then to a minimal default), calls runHandoffAtom(), and writes back only when something changed (persistMode: 'created' | 'updated' | 'skipped'). The diff-review GenUI bridge now auto-invokes this after recording the user's decision, so the Phase 8 promotion ladder closes end-to-end without an agent turn: user clicks Accept all in the web composer → daemon writes review/decision.json AND handoff/manifest.json with the right handoffKind set; failure surfaces on the bridge result as handoffError without regressing the diff-review write.
    • figma-migration pipeline e2e smoke (Phase 6). New plugins-figma-migration-e2e.test.ts walks figma-extract (stubbed REST) → token-map → diff-review → handoff end-to-end. Locks the scenario folder roster (plugins/_official/scenarios/od-figma-migration/open-design.json stages: extract → tokens → generate → critique).
    • Full code-migration pipeline e2e smoke test (Phase 7-8). First integration test that walks every code-migration atom (code-import \u2192 design-extract \u2192 token-map \u2192 rewrite-plan \u2192 patch-edit \u2192 build-test \u2192 diff-review \u2192 handoff) on a Next.js fixture repo without any agent in the loop. Locks the inter-atom file contract so a future PR can't break the chain by silently renaming code/tokens.json or adding a required field to plan/steps.json without updating every downstream reader. Ends on handoffKind='deployable-app' for the happy path; second case verifies the reject ladder rung still demotes through to design-only.
    • Earlier in this changeset:
    • diff-review GenUI \u2192 review/decision.json bridge (Phase 8 entry slice). New apps/daemon/src/plugins/atoms/diff-review-genui-bridge.ts owns the __auto_diff_review_ prefix detection, strict JSON validation of the surface payload (rejects non-object payloads + unknown decisions; coerces non-string entries out of the file lists; forwards optional reason), and the end-to-end applyDiffReviewDecisionToCwd({ cwd, value, reviewer }) glue. POST /api/runs/:runId/genui/:surfaceId/respond now bridges the choice surface response into runDiffReview() so the user's decision lands on <cwd>/review/decision.json immediately. Best-effort: failures are surfaced on the response payload as diffReviewBridge: { ok: false, error } without regressing the GenUI respond contract.
    • Earlier in this changeset:
    • Native diff-review UI on GenUISurfaceRenderer (Phase 8 entry slice). New DiffReviewChoiceSurface component renders the auto-derived __auto_diff_review_<stageId> choice surface natively: three top-level buttons (Accept all / Reject all / Partial…), an optional notes textarea forwarded as decision.reason, and a per-file accept/reject checklist that appears when the user picks Partial. Local validation refuses Partial submission when any touched file is left undecided (mirrors the daemon's partial must cover every touched file contract). New GenericChoiceSurface renders any schema with a primary enum property as a button group; property literally named decision wins so plugin-author-customised diff-review schemas keep rendering as accept/reject/partial buttons. PendingSurface gains an optional context: { touchedFiles?: [] } field so future runtime context (per-stage hints) can plug in without bloating GenUISurfaceSpec.
    • figma-extract asset rasterisation second pass (Phase 6 entry slice). runFigmaExtract({ offlineAssets: false }) now downloads per-leaf-node assets via the Figma GET /v1/images endpoint. New knobs: assetFormat ('svg' | 'png' | 'jpg' | 'pdf', default svg), assetMaxBytes (default 5 MiB). 50-id chunks, per-id failure isolation (a single CDN hiccup doesn't lose the batch), every issue lands in meta.unsupportedNodes[] with reason='asset-too-large' | 'no download URL returned' | 'download <status>' | 'image fetch error: …' | 'figma error: …'. atomDigest is recomputed after the asset pass settles so the digest reflects which assets succeeded.
    • Earlier in this changeset:
    • token-map atom (Phase 6/7 entry slice). New runTokenMap({ cwd, source?, designSystem, strict? }) writes <cwd>/token-map/{colors,typography,spacing,radius,shadow,unmatched,meta}.json. Match strategies (deterministic, first-win): exact value match → normalised hex (#abc → #aabbcc) → fuzzy name (strips -- / ds- / odds- / theme- prefixes). Unmatched lands with 'no-target-equivalent', 'target-collision', or 'invalid-source'. parseDesignSystemTokens(body) heuristic helper lifts CSS custom properties + markdown table rows from a DESIGN.md body so daemon callers don't need a hand-curated bag.
    • figma-extract atom (Phase 6 entry slice). New runFigmaExtract({ cwd, fileUrl?, fileKey?, token, fetchFn?, offlineAssets? }) walks Figma's REST GET /v1/files/<key> into the canonical <cwd>/figma/{tree,tokens,meta}.json. Token lift heuristics: SOLID fills + strokes → colours, cornerRadius → radius, FRAME / GROUP heights → spacing candidates. Gradient + image fills land in meta.unsupportedNodes[] with a reason. fetchFn is pluggable (tests inject stubs); offline mode keeps the assets/ directory empty. The OAuth bearer token is forwarded via the Authorization header and never persisted by the runner — the daemon's connector-gate stays the only place that touches the token store.
    • Auto-derived choice surface for diff-review (Phase 8 entry slice). Mirrors the connector-gate's auto oauth-prompt pattern. When a stage in the EFFECTIVE pipeline (consumer-declared OR scenario-fallback) lists diff-review, applyPlugin() synthesises an implicit choice GenUI surface (__auto_diff_review_<stageId>, persist='run', 24h timeout, onTimeout='abort') so the user can accept / reject / partial without the plugin author having to declare the surface by hand. Plugin-author-declared surfaces with the same id win.
    • Earlier in this changeset:
    • Bundled-scenario pipeline fallback (spec §23.3.3). New pure helper resolveAppliedPipeline({ manifest, scenarios }) returns { pipeline, source: 'declared'|'scenario'|'none', scenarioId? }. applyPlugin() now consults the bundled scenarios surfaced by loadPluginRegistryView() and copies the matching scenario's pipeline verbatim into both the ApplyResult.pipeline and AppliedPluginSnapshot.pipeline. Scenario plugins themselves never recurse. manifestSourceDigest stays unchanged across the fallback so spec §11.5 e2e-2 invariance holds. Only rows with source_kind='bundled' AND od.kind='scenario' are eligible — third-party scenarios cannot promote themselves.
    • design-extract atom (Phase 6/7 entry slice). New runDesignExtract({ cwd, repoPath }) reads code/index.json, walks every scannable file (css/scss/ts/tsx/js/jsx/html/json), and writes the canonical token bag at <cwd>/code/tokens.json. Captures hex / rgba / hsla colours, CSS custom properties (--*-color/-bg/-accent/-primary/-secondary/-surface/-border/-muted), font-family declarations, px/rem/em spacing on padding/margin/gap/inset, border-radius, box-shadow, and Tailwind config quoted hex palette entries. Each token records sources[] (<path>:<line>) + usage[] so token-map has a precise audit trail.
    • rewrite-plan atom (Phase 7 entry slice). New runRewritePlan({ cwd, intent? }) produces <cwd>/plan/{plan.md, ownership.json, steps.json, meta.json}. Heuristic ownership classifier maps every imported file to a tier (leaf | shared | route | shell); the step generator emits one rewrite-<slug> per leaf component, bundles sibling stylesheets, prepends a tokens-alignment step when design-extract surfaced literals, marks shared/route touches medium risk, and always closes with a build-test step. meta.atomDigest is over a canonicalised view of code/index.json so re-walks that don't change the file roster don't invalidate the plan.
    • patch-edit atom (Phase 7 entry slice). New applyPatchForStep({ cwd, stepId, diff }), skipStep(), and readPlanProgress(). The applier parses unified-diff text (plain edits, /dev/null creation, /dev/null deletion, multi-file hunks) and enforces the spec §20.3 safety contract before any byte is written: (a) path-traversal guard, (b) every touched file MUST appear in step.files[], (c) shell-tier files require step.risk='high', (d) context lines must match exactly so stale patches surface context mismatch at line N without mutating disk. After a successful apply, the runner updates plan/steps.json[id].status='completed' and writes plan/receipts/step-<id>.json with files / added / removed / rationale.
    • diff-review atom (Phase 7-8 entry slice). New runDiffReview({ cwd, decision? }) reads plan/receipts/ and emits <cwd>/review/{diff.patch, summary.md, decision.json, meta.json}. Decision composition rules: accept defaults accepted_files to every touched file; reject defaults rejected_files to every touched file; partial MUST cover every touched file via the accepted_files ∪ rejected_files union or the runner throws missing <file> so the GenUI surface can re-prompt. decision.json is round-trippable so a re-run without an explicit decision returns the persisted choice.
    • Earlier in this changeset:
    • build-test atom (Phase 7 entry slice). New runBuildTest({ cwd, buildCommand?, testCommand? }) shells out to typecheck + test commands (overrides win; otherwise inferred from package.json scripts). Auto-detects pnpm / yarn / bun / npm from lockfile presence. Per-command timeout default 5 min, log budget 1 MiB. Emits build.passing + tests.passing signals (newly added to spec §22.4 vocabulary) plus the legacy critique.score. writeBuildTestReport() persists critique/build-test.json + critique/build-test.log.
    • code-import atom (Phase 7 entry slice). New runCodeImport({ repoPath, cwd }) walks a real repo and writes a normalised <cwd>/code/index.json snapshot. Honours a 60 s walk budget; skips node_modules, .git, dist, build, coverage, etc.; rejects symlinks. Detects framework (next / vite / remix / astro / sveltekit / cra / custom), package manager, style system, and routing model.
    • handoff atom + ArtifactManifest provenance fields (Phase 7-8 entry slice). ArtifactManifest gains the spec §11.5.1 reserved fields (sourcePluginSnapshotId, sourcePluginId, sourceTaskKind, parentArtifactId, artifactKind, renderKind, handoffKind, exportTargets[], deployTargets[]). New recordHandoff() helper enforces append-only exportTargets / deployTargets and monotonic handoffKind promotion (design-only < implementation-plan < patch < deployable-app). isDeployableAppEligible() centralises the §11.5.1 promotion rule (build.passing + tests.passing + at least one docker / cli export target). Contracts barrel index.ts switched to .js-suffixed re-exports so daemon NodeNext resolution picks every type up end-to-end.
    • Bundled scenario plugins (spec §23.3.3). Four od.kind: 'scenario' plugins under plugins/_official/scenarios/{od-new-generation,od-figma-migration,od-code-migration,od-tune-collab}/ lock the default reference pipeline per taskKind. Replacing a default for an enterprise / vertical edition is now a content edit rather than a daemon code change. The bundled boot walker registers them via the existing tier layout.
  • Plugin & marketplace system — earlier landing. Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • OD_SNAPSHOT_RETENTION_DAYS referenced-row TTL (PB2). pruneExpiredSnapshots now retires referenced snapshot rows whose project has been deleted AND whose applied_at is older than the configured window. Live-project rows stay pinned forever (reproducibility wins). The GC worker reads readPluginEnvKnobs().snapshotRetentionDays so the env-var contract spec PB2 reserved is now end-to-end.
    • OD_BUNDLED_ATOM_PROMPTS=1 activates composeDaemonSystemPrompt's atom-block path. When set AND the run carries an applied snapshot with a non-empty od.pipeline.stages[*], the daemon walks each stage, calls loadAtomBodies + renderActiveStageBlock, and threads the result as composeSystemPrompt({ activeStageBlocks }). Default behaviour (flag unset) is byte-equal to today's prompt.
    • Phase 6 / 7 / 8 atom SKILL.md substrate. Nine new plugins/_official/atoms/<atom>/{SKILL.md, open-design.json} pairs the bundled boot walker registers on startup: figma-extract, token-map (Phase 6); code-import, design-extract, rewrite-plan, patch-edit, diff-review, build-test (Phase 7); handoff (Phase 8). The fragments teach the agent what each (planned) atom expects so a plugin author who references one of these ids in od.pipeline.stages[*].atoms[] sees the canonical fragment without a doctor warning. The matching shell-out implementations stay scheduled per spec §16 Phase 6 / 7 / 8.
  • Plugin & marketplace system — earlier landing. Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • Per-cloud Helm value overrides. tools/pack/helm/open-design/values-{aws,gcp,azure,aliyun,tencent,huawei,self}.yaml ship the volume + ingress diffs spec §15.5 enumerates. Operators install with helm install od ./tools/pack/helm/open-design -f values-aws.yaml.
    • composeSystemPrompt({ activeStageBlocks }). Both daemon and contracts composers accept a pre-rendered list of ## Active stage blocks (produced by renderActiveStageBlock + loadAtomBodies). Substrate slice for the §23.3.2 prompt-fragment migration; the actual call-site wiring stays gated on the next phase so default behaviour is byte-equal to today's prompt.
    • Plugin-bundled component surface. GenUISurfaceRenderer mounts a sandbox="allow-scripts" iframe at /api/plugins/:id/asset/<path> when a surface declares od.genui.surfaces[].component. Communication is one-way via postMessage({ kind: 'genui:respond', surfaceId, value }). The daemon-side asset endpoint serves files from installed_plugins.fs_path under the §9.2 preview CSP (default-src 'none'; connect-src 'none'; frame-ancestors 'self') plus X-Content-Type-Options: nosniff.
    • ProjectStorage + DaemonDb adapter substrate. New apps/daemon/src/storage/ module ships the Phase 5 §15.6 interface contracts. LocalProjectStorage (v1 default) is fully wired and tested; S3ProjectStorage is an interface-locked stub that throws on every op until the AWS SDK wiring lands. resolveDaemonDbConfig({}) parses OD_DAEMON_DB / OD_PG_* env vars but the SQLite path remains the only reachable backend in v1.
  • Plugin & marketplace system — earlier landing. Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • Phase 5 bound-API-token guard. startServer() refuses to bind a non-loopback OD_BIND_HOST without OD_API_TOKEN; bearer middleware on /api/* rejects non-loopback peers without Authorization: Bearer <OD_API_TOKEN>. /api/health, /api/version, /api/daemon/status stay open so monitoring probes (kubelet, Compose) work without secrets.
    • Helm chart templates. tools/pack/helm/open-design/templates/ ships Deployment, Service, Secret, ConfigMap, two PVCs, optional Ingress, plus _helpers.tpl + NOTES.txt. The chart installs end-to-end with helm install od ./tools/pack/helm/open-design --set secrets.apiToken=$(openssl rand -hex 32).
    • od.genui.surfaces[].component. GenUISurfaceSpecSchema accepts a { path, export?, sandbox? } field; genui:custom-component joins KNOWN_TOP_LEVEL_CAPABILITIES; doctorPlugin() flags the missing-capability + path-traversal cases. The component path is the v1 substrate for spec §10.3.5 alignment-roadmap row 2; the web sandbox loader stays scheduled.
    • .github/workflows/docker-image.yml. Multi-arch (linux/amd64 + linux/arm64) build + push to ghcr.io: :edge on main, :<version> + :latest on tag, :sha-<short> on every push, smoke build on PRs. Authenticates via GITHUB_TOKEN with packages:write.
  • Plugin & marketplace system — earlier landing. Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • @open-design/agui-adapter workspace package + GET /api/runs/:runId/agui. Pure-TS bidirectional bridge between OD's native PersistedAgentEvent / GenUIEvent / PluginPipelineStageEvent union and the AG-UI canonical event protocol. The new SSE endpoint mirrors /api/runs/:id/events but pipes every record through encodeOdEventForAgui so a CopilotKit / AG-UI client consumes an OD run unmodified. v1 plugins need no change to be consumable inside the AG-UI ecosystem (spec §10.3.5).
    • renderActiveStageBlock + loadAtomBodies. Substrate slice for spec §23.3.2 patch 2: the daemon-side helper reads <bundled-fsPath>/SKILL.md for any registered bundled atom and the contracts-side renderer assembles a ## Active stage: <id> block. The composeSystemPrompt() rewiring that consumes them is the next PR; today the helpers are reachable, tested, and the bundled atom plugins from §3.I3 already ship the matching SKILL.md bodies.
    • Phase 5 Dockerfile + docker-compose + Helm chart entry slice. deploy/Dockerfile now bundles plugins/_official/ so registerBundledPlugins() finds the atom set inside the container. tools/pack/docker-compose.yml is the canonical hosted-mode manifest (two-volume layout, OD_API_TOKEN, /api/daemon/status healthcheck). tools/pack/helm/open-design/ pins the Helm chart parameter surface for the per-cloud value overrides spec §15.5 enumerates; templates land in the Phase 5 follow-up PR.
  • Plugin & marketplace system — earlier landing. Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • Pipeline runner wired into POST /api/runs. Plugin runs whose snapshot carries od.pipeline.stages[*] now emit pipeline_stage_started synchronously before the agent process spawns. Subsequent stage events (pipeline_stage_completed, per-stage run_devloop_iterations audit rows, devloop convergence) fire asynchronously while the agent runs. Stage runner is a converging stub (critique.score=4, preview.ok=true) so single-pass pipelines walk through every stage in O(stages) time; loop stages still respect OD_MAX_DEVLOOP_ITERATIONS. Errors surface as pipeline_stage_failed events and never block the agent. e2e-3 flips from entry-slice to the full §8 contract: apps/daemon/tests/plugins-headless-run.test.ts asserts the first SSE event on a pipeline-bearing plugin run is pipeline_stage_started.
    • od doctor. Repo-wide diagnostics: daemon status, installed-plugin doctor sweep, library inventory (skills / design-systems / atoms / craft). Exits 1 when any plugin doctor returns ok=false; exit 64 when the daemon is unreachable.
    • od config get/set/list/unset. Wraps GET/PUT /api/app-config. Top-level keys via positional or --value; nested values via --value-json.
    • Phase 4 / §23 entry slice — bundled atom plugins. New plugins/_official/atoms/{discovery-question-form,todo-write,direction-picker,critique-theater}/{SKILL.md,open-design.json} pairs, plus a daemon boot walker (apps/daemon/src/plugins/bundled.ts) that registers each folder under source_kind='bundled' / trust='bundled' on every startup. Idempotent (upserts), ENOENT-silent (works outside the dev tree). Lays the substrate for the §23.3.2 patch that lifts prompt fragments out of system.ts.
  • Plugin & marketplace system — Phase 2A + 1 + 1.5 + 2B + 2C entry slice + 3 (full) + 4 (scaffold / export / publish / atoms doc / library CLI) + early 5 (earlier landing). Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • Phase 4 publish. od plugin publish <id> --to anthropics-skills|awesome-agent-skills|clawhub|skills-sh [--repo <github>] [--open] builds the catalog submission URL + PR body and (with --open) auto-launches the system browser. The author still goes through the upstream review flow; OD never POSTs anywhere.
    • CLI parity remainder. od atoms list/show, od skills list/show, od design-systems list/show, od craft list/show, od status, od version. New HTTP routes GET /api/craft + GET /api/craft/:id walk the craft/ directory and return { id, label, bytes } summaries.
    • od marketplace search "<query>" [--tag <tag>]. Substring match over every configured marketplace's plugins[]; powered entirely by the catalog metadata od marketplace add already cached, so a code agent can discover plugins without being inside the desktop UI.
  • Plugin & marketplace system — Phase 2A + 1 + 1.5 + 2B (composer mount + marketplace deep UI) + 2C entry slice + 3 entry slice + 4 (scaffold / export / atoms doc) + early 5 (earlier landing). Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • Phase 4 author tooling. od plugin scaffold --id <id> writes a starter SKILL.md + open-design.json + README.md (optionally a .claude-plugin/plugin.json). od plugin export <projectId> --as od|claude-plugin|agent-skill --out <dir> materialises a publish-ready folder from the AppliedPluginSnapshot behind a project (or a snapshot id), so any chat that ran a plugin can be re-published to anthropics/skills, awesome-agent-skills, clawhub, or skills.sh without leaving the terminal. The output's open-design.json carries a provenance block (snapshotId + manifestSourceDigest + appliedAt) that reverse-resolves to the originating run.
    • Phase 2B marketplace deep UI. New routes /marketplace and /marketplace/<pluginId> rendered by MarketplaceView (catalog grid + trust filters + configured catalogs panel) and PluginDetailView (manifest, capability checklist, connector requirements, declared GenUI surfaces, "Use this plugin" → applyPlugin → Home). /plugins/<id> is a parsed alias so the public-site URL scheme reserved in spec §13 already works in-app.
    • Phase 2B ChatComposer mount. ChatComposer renders <PluginsSection variant="strip" /> above the input whenever a projectId is known. Apply hydrates the draft only when empty so a mid-typing user is never overwritten.
    • docs/atoms.md. New canonical reference for the first-party atom catalog: implemented vs planned ids, task-kind mapping, how the daemon resolves an atom at run time, the closed until-signal vocabulary, and the §22.5 community-plugin → first-party-atom promotion path.
  • Plugin & marketplace system — Phase 2A finished + Phase 1 follow-up + Phase 1.5 + Phase 2B (composer mount) + Phase 2C entry slice + Phase 3 entry slice + early Phase 5 (earlier landing). Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • CLI canonical agent-facing API (spec §11.7). Every UI action now has a CLI equivalent: od project create/list/info/delete, od run start/watch/cancel/list/info (with --plugin, --inputs, --grant-caps, --snapshot-id, --follow), od files list/read/write/upload/delete, od conversation list/info. Plugin runs route through the §3.A1 snapshot resolver.
    • Phase 1.5 headless lifecycle. New od daemon start [--headless] [--serve-web] [--port] [--host], od daemon status [--json], od daemon stop. Backed by new HTTP routes GET /api/daemon/status and POST /api/daemon/shutdown (loopback-only). The default od (no subcommand) keeps its desktop behaviour for back-compat. e2e-3's headless install→project→run loop is now anchored in a daemon supertest (apps/daemon/tests/plugins-headless-run.test.ts).
    • Phase 3 marketplace plugin install resolution. POST /api/plugins/install with a bare plugin name walks every configured plugin_marketplaces row in registration order and re-routes to the canonical github:… / https://… source recorded in the matched manifest. CLI: od plugin install <name> works against any catalog the operator added via od marketplace add <url>.
    • Web composer mount. New PluginsSection widget combines InlinePluginsRail, ContextChipStrip, PluginInputsForm, plus renderPluginBriefTemplate's {{var}} substitution. Mounted in NewProjectPanel below the project-name input as the Phase 2A discovery surface. Purely additive: the existing Send button rules are unchanged.
    • The earlier work in this changelog block remains in place (snapshot resolver, trust mutation, connector tool-token gate, fallback rejection, GitHub tarball + HTTPS install sources, pipeline runner with cross-conversation cache, marketplace registry, snapshot GC worker, web component primitives, definition-of-done suite, …).
  • Plugin & marketplace system — Phase 2A → entry slice of Phase 2B/2C/3 (earlier landing). Spec: docs/plugins-spec.md. Living plan: docs/plans/plugins-implementation.md.

    • Snapshot resolver wires applyPlugin() into POST /api/projects and POST /api/runs. Capability gate failures map to HTTP 409 / exit 66; missing inputs map to HTTP 422 / exit 67. The in-memory run object carries appliedPluginSnapshotId so connector / replay paths read a frozen view.
    • Trust mutation: new POST /api/plugins/:id/trust endpoint plus matching od plugin trust <id> --capabilities … CLI. Validates the spec §5.3 capability vocabulary (rejects unknown / malformed strings); preserves the implicit prompt:inject floor on revoke.
    • Connector tool-token gate: tokens minted for plugin runs carry pluginSnapshotId / pluginTrust / pluginCapabilitiesGranted. /api/tools/connectors/execute re-validates the §5.3 connector:<id> rule per call (CONNECTOR_NOT_GRANTED 403 on miss). Trusted plugins implicitly carry connector:*; restricted plugins must list each id explicitly.
    • API-fallback rejection: every /api/proxy/* entry returns 409 PLUGIN_REQUIRES_DAEMON when a body smuggles pluginId / appliedPluginSnapshotId.
    • Snapshot GC worker enforces the PB2 expires_at TTL (OD_SNAPSHOT_UNREFERENCED_TTL_DAYS / OD_SNAPSHOT_GC_INTERVAL_MS) at boot + on a periodic interval. Operator escape hatch: od plugin snapshots prune --before <ts>.
    • Installer accepts github:owner/repo[@ref][/subpath] (codeload tarball) and https://*.tar.gz archives in addition to local folders. Hard guards: symlink / hard-link rejection, path-traversal segments, 50 MiB size cap.
    • Pipeline runner emits pipeline_stage_* events per stage and persists every iteration into run_devloop_iterations. Pre-stage GenUI surfaces auto-derived for not-yet-connected required connectors hit the cross-conversation cache (e2e-5: a second conversation in the same project never re-broadcasts an already-resolved oauth-prompt).
    • Marketplace registry minimum verbs: od marketplace add/list/info/refresh/remove/trust plus the matching HTTP routes. Default trust tier is restricted per spec §9; the Phase 3 follow-up wires od plugin install <name> resolution + the trust UI.
    • Web composer surface: applyPlugin() state helper, InlinePluginsRail, ContextChipStrip, PluginInputsForm, GenUISurfaceRenderer (confirmation + oauth-prompt first-class; form / choice fall back to a JSON Schema preview until Phase 2A.5), GenUIInbox drawer.
    • od plugin run apply→start shorthand. CLI structured error helper maps recoverable HTTP 4xx envelopes to the §12.4 exit codes (64–73) so code agents get a stable retry contract.
    • Plan §8 e2es covered at the daemon level: e2e-2 (pure apply across runs), e2e-4 (replay invariance after plugin upgrade via renderPluginBlock(snapshot)), e2e-5 (GenUI cross-conversation cache), e2e-6 (connector trust gate; re-validated independently in the token-issuance + execute routes), e2e-7 (API-fallback rejection at every proxy entry), e2e-8 (apply purity regression: 100 applies → 0 FS mutation, +100 snapshot rows). e2e-3 (headless run) stays scheduled for Phase 1.5.
  • ib-pitch-book skill — investment-banking strategic-alternatives pitch book (Anthropic financial-services Pitch Agent workflow); ships example.html and IB layout references.

0.6.0 - 2026-05-09

A connectivity-and-iteration release: OpenDesign becomes a fully bidirectional MCP citizen (external MCP client with 39 templates), ships Cloudflare Pages deployment for generated artifacts (with custom domains), advances Critique Theater to Phase 6 (interrupt + project-keyed run registry), and lands a redesigned top bar, draggable file tabs, batch delete, vector PDF export, agent-callable research/search, and Orbit activity summaries. Hyperframes learns the HTML-in-Canvas API. New BYOK provider (Ollama Cloud), new agent capabilities (Gemini 3 preview + GPT-5.1 codex picker + DeepSeek v4), new design systems (BMW M, Slack, Cisco, Webex, Mission Control, Urdu Modern), eight new skill bundles, and Turkish + Thai locales. 136 merged PRs since 0.5.0.

Added

MCP, deployment & connectors

  • External MCP client with daemon-managed OAuth and 39 design-focused templates. OpenDesign can now consume MCP servers, not just expose itself as one. (#898)
  • Cloudflare Pages artifact deployment. One-shot publish of generated artifacts to Pages from the desktop app. (#729)
  • Cloudflare Pages custom domains. Bind your own domain to deployed artifacts. (#851)
  • Preserve OAuth state and advertised tool counts when reconnecting MCP/connector providers. (#1036)
  • Optimized Composio connector previews. (#907)

Critique Theater

  • Phase 6.1: critique interrupt endpoint + project-keyed run registry. Long critiques can now be interrupted cleanly per project. (#819)
  • Shared CritiqueRoundSummary / CritiqueRunStatus types via the @open-design/contracts package. (#1016)

Web / UI

  • Top bar redesign — Share/Present lifted to the top bar, zoom dropdown, and an explicit focus toggle. (#1048)
  • Draggable file tab reordering in the workspace. (#936)
  • Batch delete for selected design files. ([#783])
  • Sortable Design Files table columns. (#804)
  • Privacy consent choices made explicit at first launch. (#1031)
  • Differentiated "recent" vs "your designs" sorting. (#845)
  • Inspect / Picker now renders an empty-annotation state instead of a blank panel. (#1005)
  • Toggle to reveal saved media-provider API keys. (#867)

Desktop & artifacts

  • Direct PDF export for artifacts. (#532)
  • Hyperframes skill learns the HTML-in-Canvas API for richer in-canvas previews. (#852)
  • Consolidated Hyperframes video template updates. (#1079)
  • Inspect overlay support on Windows packaged builds. (#944)
  • Allow od:// URLs through setWindowOpenHandler so live-artifact previews open in a child window. (#933)

Daemon, agents & runtime

  • Import existing local folder as a project. (#624)
  • Agent-callable research command + /search. Agents can ask the project for grounded research without leaving the chat. (#615)
  • Orbit activity summaries. (#681)
  • Finalized the design-package endpoint (closes #450). (#832)
  • Closed pi adapter parity gaps (imagePaths, extraAllowedDirs, error events, sendAgentEvent routing). (#763)
  • Language-boost support for Minimax TTS. (#773)
  • Expose Gemini 3 preview models and Gemini 2.5 Flash Lite in the picker. (#986)
  • Add GPT-5.1 entries to the Codex picker. (#946)
  • Expand Codex picker coverage. (#757)
  • Stable prerelease promotion gate for [codex]. (#962)
  • VP_HOME environment variable support in agent resolution. (#859)
  • Auto-rebuild better-sqlite3 on Node.js ABI mismatch postinstall. (#813)
  • Increase agent inactivity timeout. (#1071)
  • Reset inactivity watchdog on raw stdout bytes, not just parsed events. (#976)

BYOK & integrations

  • Ollama Cloud as a BYOK provider. (#923)
  • Opt-in Langfuse telemetry. (#800)
  • Make Azure API version optional. (#941)

Skills, design systems & prompt templates

  • ib-pitch-book skill — investment-banking strategic-alternatives pitch book (Anthropic financial-services Pitch Agent port). (#888)
  • github-dashboard skill. (#666)
  • clinical-case-report skill. (#581)
  • social-media-matrix-tracker skill — live-artifact tracker. (#810)
  • trading-analysis live-artifact dashboard skill. (#824)
  • otd-operations-brief live-artifact template. (#794)
  • 32 zhangzara HTML deck templates. (#704)
  • 7 example dashboards + contract demo for the live-artifact skill. (#716)
  • after-hours-editorial template skill. (#1053)
  • swiss-user-research-video template skill. (#1054)
  • editorial-burgundy-principles template skill. (#1065)
  • swiss-creative-mode template skill. (#1068)
  • BMW M design system. (#579)
  • Slack design system. (#899)
  • Cisco and Webex design systems. (#991)
  • Mission Control design system. (#858)
  • Urdu Modern (Indus Script) design system. (#714)
  • Craft laws-of-ux module so generated UIs respect working-memory limits. (#809)
  • Craft typography-hierarchy and typography-hierarchy-editorial rules. (#975, #979)

Internationalization

  • Turkish README translation. (#843)
  • Full Thai (th) UI locale. (#1018)
  • Renamed live-artifact tab label in zh-CN and zh-TW. (#969)
  • Default id locale to English for keys not yet translated. (#822)
  • Trim BYOK proxy fallback line from zh-CN intro. (#915)

Packaging & deployment

  • Docker Compose deployment workflow. (#65)
  • Preserve beta e2e spec reports in R2. (#812)
  • Document the Colima build-swap helper. (#967)

Community

  • Vaunt contributor recognition (5-tier system). (#908)

Changed

  • Hardened security scan findings and upgraded dependencies. (#806)
  • Strengthened e2e PR coverage and entry/settings automation coverage. (#796, #811)
  • Refreshed contributors wall and GitHub metrics. (#856, #1004, #853, #998)
  • Refined typography-hierarchy craft docs — clarify edge cases and make lint measurable. (#979)

Fixed

MCP & connectors

  • MCP install snippet survives daemon port changes. (#846)
  • Pin the daemon data directory in /api/mcp/install-info env so the macOS-packaged MCP server stops failing on managed project storage. (#857)
  • Reserve clearance for the MCP server Copy button so it stops overlapping the snippet. (#847)
  • Give the MCP server Copy button a solid surface so it reads against the code block. (#840)
  • Stable curated tool count in the connector card badge. (#767)
  • Remove redundant "Connect GitHub" placeholder from the import menu. (#964)
  • Connector "Close window" button always gives feedback. (#995)
  • Confirm before clearing the saved Composio API key. (#877)
  • Keep saved Composio API key indicator visible while typing a replacement. (#751)
  • Confirm before clearing a saved Media provider API key. (#875)

Cloudflare Pages

  • Cloudflare Pages custom-domain lookup. (#958)

Web UI

  • Surface explicit error/retry state when example preview HTML fails to load. (#863)
  • Confirm before closing a dirty sketch so unsaved strokes are not lost. (#988)
  • Keep chat auto-scroll glued to the bottom across streaming chunks. (#989)
  • Preserve Chat scroll position across Chat/Comments tab switches. (#841, #886)
  • Differentiate selected, hover, and focus states in the language switcher. (#987)
  • Scroll the active workspace tab into view when the strip overflows. (#990)
  • Keep the Design Files tab visible when workspace tabs scroll. (#842)
  • Wrap long note text inside picker/comment popovers. (#830)
  • Wrap comment-popover action row so the Save/Sending button can't exceed the popover edge. (#829)
  • Prevent comment popover header overflow when the label is too long. (#833)
  • Truncate long Inspect-panel labels so they cannot spill past the panel edge. (#838)
  • Keep Inspect-panel close button on a stable single-line layout. (#839)
  • Increase project meta line-height to prevent descender clipping. (#834)
  • Give the deploy modal primary action more breathing room. (#992)
  • Hide the unsupported "Save comment" button on Pods selections. (#993)
  • Clear stale upload error banner when previewing existing files. (#994)
  • Expand design file row click target. (#1039)
  • Keep entry footer pills compact. (#1045)
  • Hide stale upload error banner when previewing other files. (#994)
  • Scope settings save validation + sanitize payload to the active sidebar section. (#827)
  • Ensure the Settings close button is always clickable. (#971)
  • Correct srcdoc injection and deck bridge for JS strings containing closing </script>. (#938)
  • Unbreak the Create button on plain HTTP / LAN-IP deployments. (#900)
  • Differentiate recent vs your-designs sorting. (#845)
  • Keep examples filter counts consistent. (#949)

Desktop & packaging

  • Cleanly quit the macOS packaged app. (#422)
  • Keep modal controls clickable in drag regions. (#1032)
  • Improve Orbit and packaged data-dir startup errors. (#1067)
  • Fix desktop preview interactions and connector auth feedback. (#864)
  • Fix desktop preview and packaged app interactions. (#879)
  • Fix desktop prompt template close hitbox. (#1056)
  • Pack/win: close detection gaps that let Open Design.exe stay locked at install time. (#823)
  • Tools-pack: mark blake3-wasm as external in the macOS prebundle. (#844)
  • Packaged: swallow harmless setTypeOfService EINVAL from undici. (#906)

Daemon

  • Settle completed runs and clean up shutdown children. (#924)
  • Fix stuck chat runs and unintended cancels. (#896)
  • Write SSE events atomically in createSseResponse.send. (#972)
  • Media generation task state survives daemon restart (#648). (#884)
  • Sync Orbit last run with the selected prompt template. (#937)
  • Image template creations execute the selected prompt automatically. (#752)
  • Serve Python files as text. (#947)
  • Type-check core server paths and leaf modules. (#943, #952)

Codex / OpenCode

  • OpenCode todowrite footer state. (#1046)

Skills & docs

  • Stale internal links across docs. (#950)

Documentation

  • Repository-wide code review guidelines. (#927)
  • Design system authoring guide. (#961)
  • Skills contributing guide. (#1035)
  • Docker setup instructions in QUICKSTART, CONTRIBUTING, and README. (#935)
  • Re-add awesome-design-md reference to QUICKSTART. (#940)
  • Update prompts path from web to daemon in README files. (#756)

Internal

  • Test: cover model option rendering. (#948)
  • Test: de-flake chat-scroll-preservation across tab switches. (#886)
  • Auto-generated metrics + contributors wall refreshes. (#853, #998, #856, #1004)
  • Release: OpenDesign 0.5.0 changelog landing. (#820)

0.5.0 - 2026-05-07

A minor release focused on iteration: live-data dashboards graduate to a first-class artifact category, an in-preview Inspect mode lands for per-element style tuning, the desktop launcher gets an accent color theme, Critique Theater advances to Phase 5, and Linux gains headless lifecycle support. New Qoder CLI agent, Nano Banana image provider, and Indonesian locale. 51 merged PRs since 0.4.1, accumulated across 16 beta cycles.

Added

Web / UI

  • Inspect mode — live per-element style tuning in the HTML preview. (#362)
  • Accent color control + launcher — a global accent persists across the desktop launcher and entry view. (#683)
  • Connection tests for execution settings — verify provider config without launching a chat. (#507)
  • Replaced the SketchEditor window.prompt() text tool with an in-app modal so long prompts stop getting clipped. (#738)

Skills, design systems & prompt templates

  • live-dashboard skill — generic Live Artifact dashboard template. (#778)
  • clinic-console live-artifact template. (#795)
  • FlowAI live dashboard template skill. (#801)
  • Notion-style team dashboard prompt template (Live Artifact). (#799)
  • waitlist-page skill. (#555)
  • social-media-dashboard skill + Totality Festival design system. (#678)
  • Five Orbit briefing prompt templates. (#671)
  • Craft form-validation module — generated forms follow modern RHF/Zod patterns instead of 2018 Formik habits. (#625)

Critique Theater

  • Phase 5 — panel prompt template + system composer wiring. (#524)

Daemon and agents

  • Qoder CLI agent adapter. (#626)
  • Project transcript export to disk for downstream tools (replay, audit, sharing) — prereq for #450. (#493)
  • Override the Codex executable path for nvm / mise / fnm-installed toolchains. (#755)
  • Codex image projects can use built-in imagegen. (#622)
  • DeepSeek v4 models in the model catalog. (#722)
  • OD_LEGACY_DATA_DIR migrator for 0.3.x → 0.4.x data recovery. (#712)

Media generation

  • Nano Banana image provider. (#631)
  • HyperFrames video previews, provider badge, and source filter on the templates surface. (#293)

Linux & packaging

  • Linux headless lifecycle — install / start / stop from CLI without a desktop session. (#686)
  • Improved Windows beta packaging and installer flow. (#768)
  • Migrated beta release publishing to R2. (#805)

Internationalization

  • Indonesian (id) UI locale. (#414)

Changed

  • Project file watcher now ignores .venv and other large dirs so Python projects stop overwhelming it. (#531)
  • Daemon CORS whitelist accepts portless Origin headers for Chrome compatibility. (#735)
  • Extended OpenAI image request timeouts so larger generations stop being killed mid-flight. (#788)
  • Surfaced the @nexudotio X account in README and entry sidebar. (#696)

Fixed

Daemon and agents

  • Delivered Copilot prompts via stdin to avoid Windows ENAMETOOLONG. (#727)
  • Surfaced OpenCode error frames; treated empty-output runs as failed instead of silently succeeding. (#700)
  • Discovered toolchain paths for GUI-launched agents on minimal PATH. (#614)

Web and desktop

  • Removed Tweaks-mode element-selector tooltip noise. (#697)
  • Fixed chat pane overflow. (#740)
  • Narrowed the ws-tabs-bar scrollbar so filenames stop overlapping. (#781)
  • Improved settings dialog scroll behavior. (#667)
  • Widened settings subtitle so the English copy fits on one line. (#747)
  • Persisted design system selection across sessions. (#621)
  • Aligned the design system default test fixture. (#708)
  • Showed an alert when the PDF export popup is blocked. (#664)
  • Fixed the Windows link-code-folder dialog. (#698)
  • Made desktop entry chrome consistent. (#655)

Packaging & runtime

  • Unbroke Claude Design ZIP import on Node 24 and raised the file ceiling. (#591)
  • Diagnosed missing Next package during tools-dev web startup. (#675)

Internationalization

  • Aligned README.es UI references to the es-ES.ts locale. (#611)
  • Fixed Ukrainian prompt template translations and removed duplicate keys. (#674, #680)

Miscellaneous

Documentation

  • Documented the Linux namespace env var in tools-pack. (#670)
  • Fixed broken pi-ai links after the package split. (#277)

Internal

  • Added desktop settings + project flow e2e coverage. (#306)
  • CI: notify Discord #resolved when issues are closed by a merged PR. (#685)
  • Refreshed generated GitHub metrics SVG and contributors wall. (#718, #720)

0.4.1 - 2026-05-06

0.4.1 is the startup hotfix for the broken 0.4.0 desktop packages. It restores packaged app startup on macOS and Windows, adds release validation so the failure mode is caught before publication, and includes the small UI, agent, documentation, i18n, and craft updates that landed while the hotfix was being verified.

Added

Web / UI

  • Manual edit mode for direct artifact edits. (#620)
  • Cmd/Ctrl+P quick file switcher for faster project navigation. (#556)
  • Resizable chat panel. (#563)

Daemon and agents

  • Added model name to PI initial status and RPC abort on cancel. (#618)

Craft and i18n

  • Craft accessibility-baseline module with opt-ins for dashboard, HR onboarding, and mobile onboarding. (#587)
  • Craft rtl-and-bidi module so artifacts handle Arabic, Hebrew, and Persian content more reliably. (#595)
  • Added i18n structure checks. (#608)

Changed

  • Updated README first-PR links so help-wanted issues are surfaced alongside good-first-issue. (#605)

Fixed

Packaging

  • Fixed packaged desktop startup by building @open-design/contracts to dist/*.mjs + .d.ts, pointing its exports at compiled JavaScript, and building contracts before all packaged lanes pack workspace tarballs. (#577)
  • Added packaged runtime beta gating so release candidates install, start, inspect /api/health, collect logs, stop, and uninstall before promotion. (#637)

Daemon and agents

  • Added the required stdio MCP server env field and recover from -32602 on session/set_model. (#627)
  • Normalized ACP mcpServers to the stdio shape for Kimi/Hermes ACP. (#612)
  • Fixed agent CLI configuration and workspace focus mode. (#604)

Web and desktop

  • Preserved error messages across conversation reloads. (#623)
  • Kept chat recoverable after conversation load failures. (#637)
  • Honored native macOS quit behavior in the packaged desktop shell. (#637)

Documentation

  • Documented daemon data directory migration to the Desktop app. (#570)
  • Added Chinese (Simplified) QUICKSTART. (#578)
  • Backported missing zh-TW README sections from the English README. (#586)
  • Synced and improved the Korean README. (#619)

Internal

  • Refined release workflows, CI scope, e2e layout, and packaged runtime smoke coverage for beta validation. (#637)
  • Refreshed generated GitHub metrics. (#592)

0.4.0 - 2026-05-05

A multi-protocol leap: OpenDesign now ships as an MCP server, ships Critique Theater (Design Jury) Phase 4, gains live-reload + Tweaks mode + live artifacts in the preview pane, and adds five new agent / runtime adapters. 71 merged PRs from 40+ contributors over two days. Linux AppImage packaging landed in tooling, but the stable Linux artifact is deferred from 0.4.0 while containerized release packaging is hardened.

Added

MCP & agent integration

  • od mcp — expose OpenDesign as a stdio MCP server. Coding agents in other repos (Claude Code, Codex, Cursor, VS Code, Antigravity, Zed, Windsurf) can read files from local OpenDesign projects directly, including the project the user has open in the OpenDesign app right now. (#399)
  • Link code folder support for agent context — point agents at any local code folder alongside the design project. (#455)
  • Kilo CLI (ACP) agent adapter. (#480)
  • DeepSeek TUI agent adapter. (#439)

Critique workflow

  • Critique Theater Phase 4 — persistence, transcript, and orchestrator. The "Design Jury" multi-panelist scoring pipeline is now end-to-end. (#481)
  • Critique Theater foundation — shared contracts and streaming v1 parser (Phases 0–2). (#387)

Preview pane

  • Live-reload preview iframes when project files change on disk. (#409)
  • Tweaks mode for HTML previews — element picker, pod selection, batched chat attachments. (#513)
  • URL-load HTML preview iframes by default (?forceInline=1 opt-out). (#384)
  • Live artifacts and Composio connector catalog. (#381)

Packaging & deployment

  • Linux x64 AppImage tooling in tools-pack; stable release artifact deferred from 0.4.0 while the containerized packaging lane is hardened. (#369)
  • Optimize packaged mac artifact size. (#424)

Daemon

  • OD_MEDIA_CONFIG_DIR to relocate media-config.json (Nix store, immutable images, sandboxes). (#411)
  • Modernized multi-provider API proxy routing (Anthropic, OpenAI-compatible, Azure OpenAI, Google Gemini). (#385)
  • Seed daemon with pre-baked decks and web prototypes. (#457)

Skills, design systems & prompt templates

  • Atelier Zero editorial collage landing-page design system. (#366)
  • open-design-landing rename, kami skill bundle, and landing OG assets. (#428)
  • Craft animation-discipline module + opt-ins on mobile-app, mobile-onboarding, gamified-app. (#515)
  • Craft state-coverage module + opt-ins on dashboard, mobile-app, kanban-board. (#502)

Web / UI

  • Skills & design systems management page in Settings. (#535)

Design Files

  • Batch ZIP download with multi-select. (#405)

Internationalization

  • Complete French localization, README, and Quickstart. (#326, #397, #434)
  • Ukrainian UI localization. (#395)
  • Russian UI locale refresh + README + gallery metadata. (#393, #396)
  • Brazilian Portuguese README translation. (#460)
  • Arabic README translation. (#458)

Changed

  • Refactor RUNTIME_DATA_DIR resolution logic. (#391)
  • Update Codex sandbox invocation. (#477)

Fixed

Security

  • Bind daemon to localhost by default + origin validation. (#365)
  • Strip ANTHROPIC_API_KEY when spawning Claude Code. (#400)
  • Preserve ANTHROPIC_API_KEY when ANTHROPIC_BASE_URL is set. (#514)
  • Preserve *_API_KEY env vars for CLI agents in packaged builds. (#404)
  • Normalize daemon proxy origins. (#392)

Daemon

  • Resolve daemon package.json from any compiled layout so the packaged app reports the correct version. (#537)
  • Correct Claude Code --add-dir capability detection. (#440)
  • Handle ACP -32603 errors gracefully in session/set_model. (#492)
  • Expose skill resources via cwd-relative aliases. (#435)
  • Support nested paths in project file serve route. (#401)
  • Respect baseUrl path verbatim in OpenAI-compat proxy. (#410)

Web UI

  • Prevent vertical scrollbar on artifact preview frame. (#453)
  • Prevent vertical scrollbar on ws-tabs-bar. (#448)
  • Language option button height truncation in Settings. (#447)
  • Aspect-ratio cards no longer overflow into siblings. (#476)
  • Add copy buttons for FileViewer code blocks. (#471)
  • Lowercase todowrite compatibility in ToolCard. (#523)
  • Cap htmlPreviewSlideState Map to prevent memory leak. (#488)
  • Isolate preview blob export paths. (#429)
  • Split execution-mode tabs and align active chip visuals. (#418)
  • Tighten entry-tab layout and design-system showcase color picker. (#412)
  • Lift coming-soon tip above sticky tabs and make it readable in dark theme. (#382)
  • Fix file tab wheel scrolling. (#549)

Design Files

  • Clear selection on project switch. (#465)

Agents

  • Copilot prompt processing with correct command format. (#466)
  • Codex Gemini CLI trust handling. (#352)

Desktop

  • Show window on macOS dock activate. (#270)

Packaging

  • Bundle prompt templates in packaged desktop resources. (#417)

Landing page

  • Deploy with npm wrangler. (#421)

Documentation

  • Discord invite badge in README. (#504)
  • Surface desktop downloads in README. (#522)
  • "Running the Project" section in README. (#468)
  • First-PR link points to /contribute page. (#494)
  • Defer README template-driven generation; capture #195 discussion. (#403)
  • Fix typo in zh-TW README. (#548)
  • Auto-generated metrics SVG and contributors wall refresh. (#406, #407, #489, #490)

Internal

  • Enforce test directory conventions. (#496)

0.3.0 - 2026-05-03

A fast follow-up to 0.2.0 focused on richer design workflows, packaged-agent reliability, export/deploy flows, and broader internationalization. 39 merged PRs from 25 contributors.

Added

Web / UI

  • Pet companion with Codex hatch-pet integration. (#296)
  • Brand design-system cards, thumbnails, and DESIGN.md side-by-side preview. (#289)
  • Per-tool renderer registry for generative UI. (#282)
  • Task completion sound and browser notification. (#359)

Agents & daemon

  • Persist code-agent startup state. (#255)
  • Mistral Vibe CLI agent adapter. (#354)
  • Devin for Terminal support. (#301)
  • OD_BIND_HOST and --host for interface binding. (#328)

Skills & exports

  • Taste-skill-derived web prototype and HTML PPT examples. (#358)
  • pptx-html-fidelity-audit skill wired into export prompts. (#307)
  • Broader PPTX fidelity script coverage beyond CJK. (#308)
  • Native desktop Save As dialog for .pptx downloads. (#330)
  • Export as Markdown from the share menu. (#345)

Deployment

  • /api/projects/:id/deploy/preflight for pre-upload inspection. (#320)

Internationalization

  • Arabic (ar) UI locale with RTL layout. (#316)
  • French (fr) UI locale. (#376)

Fixed

Agents, packaged runtime & Windows

  • Include nvm / fnm / mise agent CLI bins in packaged PATH. (#364)
  • Detect Codex and Gemini CLIs from user toolchain paths. (#346)
  • Upgrade better-sqlite3 for Node 24 Windows prebuilt support. (#357)
  • Lead Copilot spawn with -p - so prompt-via-stdin is consumed. (#351)
  • Drop literal - argv from Codex spawn so prompts deliver via stdin pipe alone. (#342)
  • Wrap cmd.exe shim invocations to survive /s /c quote stripping. (#339)

Web UI & files

  • Download as .zip now returns the actual project tree. (#341)
  • Keep Design Files view active after deleting a file. (#329)
  • Scroll workspace tabs in place instead of the window. (#363)
  • Treat inlined script content as literal in FileViewer. (#343)
  • Use response-order matching for bulk upload aggregation. (#323)
  • Serve .jsx / .tsx with JS-family MIME types so browser loaders accept them. (#340)
  • Fix macOS entry view drag region. (#373)

Daemon & deployment

  • Increase project upload limit from 20MB to 200MB. (#319)
  • Bundle and rewrite assets referenced from inline <style> blocks and style="" attributes. (#314)

Internationalization

  • Update locale coverage after main merge. (#251)
  • Add missing designFiles.showMore keys to ar, hu, ko, pl, and tr. (#335)

Documentation

  • Japanese documentation update. (#309)
  • README contributors wall refresh. (#360)
  • Spelling fixes in CLI comments, spec, and video prompt docs. (#300)

0.2.0 - 2026-05-02

A feature-heavy follow-up to 0.1.0 — dark mode, xAI Grok Imagine media generation, headless deploy mode, OpenClaude fallback, four new locales, and a much richer skill / design-system / prompt-template catalog. 45 merged PRs from 27 contributors.

Added

Web / UI

  • Dark mode with system / light / dark toggle. (#259)
  • Visible conversation timestamps. (#120)
  • React artifact output support. (#121)
  • Preview comment attachments. (#284)

Agents & daemon

  • Auto-detect OpenClaude as a fallback for Claude Code. (#263)
  • Standardize agent communication via stdin and remove Windows-specific shims. (#258)

Media generation

  • xAI Grok Imagine integration covering image, video, and native audio. (#276)

Skills, design systems & prompt templates

  • kami editorial paper design system with deck starter. (#226)
  • html-ppt skill (lewislulu/html-ppt-skill) with 15 per-template Examples cards. (#193)
  • design-brief skill with structured I-Lang input format. (#184)
  • Brand-agnostic craft references and Refero-derived lint rules. (#225)
  • 11 HyperFrames video prompt templates and media generation README section. (#227)
  • Three Kingdoms ARPG Seedance 2.0 video templates (3). (#212)
  • Three Kingdoms ARPG gameplay screenshot templates (3). (#207)
  • Otaku-dance choreography breakdown infographic template. (#209)
  • Anime fighting game screenshot template. (#208)

Deployment & tooling

  • --prod flag and OD_HOST for headless server deployment in tools-dev. (#222)
  • GitHub CI workflow. (#271)
  • Daemon kindFor / mimeFor file classifier tests. (#269)

Internationalization

  • Hungarian (hu) UI locale. (#288)
  • Polish (pl) UI locale. (#273)
  • Korean (ko) UI locale. (#253)
  • Turkish (tr) UI locale. (#233)

Changed

  • Image / video projects now pick from prompt templates (not design systems). (#192)
  • Optimize Electron release artifact size. (#249)

Fixed

Daemon

  • Restore startServer Promise contract — return url / { url, server }. (#268)
  • Emit tool_use from tool_execution_start in pi-rpc. (#186)
  • Clamp Codex reasoning effort to model-supported values. (#223)
  • Deliver Claude Code prompt via stdin to avoid spawn E2BIG / ENAMETOOLONG. (#143)
  • Include package.json in tarball so packaged app reports correct version. (#260)
  • Treat .py files as previewable code in Design Files. (#261)
  • OD_DAEMON_URL uses port 0 instead of actual allocated port (now reports the real port). (#240)
  • Quote agent bin path when spawning with shell:true on Windows. (#232)
  • Make max_tokens configurable. (#78)

Web UI

  • Suppress hydration warning on <body>. (#248)
  • Fix language dropdown overflow in Settings modal. (#281, #287)
  • Add scroll to Settings language menu when it overflows view. (#247)
  • Preserve deck preview pagination per file. (#119)
  • Fix deck preview pagination controls. (#112)

Cross-platform

  • Use junction instead of dir symlink on Windows in tools-dev. (#231)

Internationalization

  • Replace hardcoded Claude with 助手 in zh-TW assistant role copy. (#262)

Documentation

  • Traditional Chinese (繁體中文) README. (#194)

Internal

  • Auto-generated metrics SVG updates. (#228, #241)
  • Fix metrics workflow protected branch updates. (#219)

0.1.0 - 2026-05-01

First public release of OpenDesign — a local-first, open-source alternative to Anthropic's Claude Design. It detects your installed code-agent CLI, runs design skills against curated design systems, and streams artifacts into a sandboxed in-app preview.

Added

Agent runtimes & providers

  • Multi-agent runtime detection and dispatch: Claude Code, Codex, Cursor, Gemini CLI, OpenCode, Qwen, GitHub Copilot CLI, Hermes, Kimi CLI, Pi, and Kiro. (#28, #71, #117, #185)
  • Per-CLI model picker for local agents. (#14)
  • OpenAI-compatible provider support and Anthropic-compatible stream proxy for non-native providers. (#80, #180)
  • App version awareness shared across daemon and web. (#204)

Skills, design systems & prompt templates

  • 72 brand-grade design systems and 31 composable skills, including Xiaohongshu and Replit Deck (8 themes). (#24, #74)
  • 57 DESIGN.md specs imported from awesome-design-skills. (#92)
  • Dance storyboard and ancient-China MMO HUD prompt templates. (#187)

Artifacts & preview

  • Artifact platform foundation with sandboxed in-app preview. (#68)
  • First-class SVG and Markdown artifact renderers / viewer. (#73, #177)
  • HTML preview support for relative-asset references. (#156)
  • Document preview support for uploaded files and multi-file design uploads. (#31, #63)
  • Claude Design .zip import. (#46)
  • Image / video / audio media surfaces with unified od media generate dispatcher. (#12)

Packaging & deployment

  • Mac arm64 packaged runtime with signed/notarized DMG + update ZIP and beta release flow. (#170)
  • Windows x64 NSIS installer (unsigned beta) and release assets. (#191)
  • Vercel self-deploy flow with vercel.json configuration. (#167, #169)

Internationalization

Developer experience & tools

  • tools-dev / tools-pack workspace tooling for development and packaging, with native addon diagnostics and improved web startup flow. (#127, #128, #153)
  • dev:all auto-switches to a free port when defaults are busy. (#9)
  • UI end-to-end automation suite and reporting under apps/e2e. (#64, #102)
  • Frontend toolchain migrated from Vite to Next.js 16 App Router. (#66)
  • Project code migrated to TypeScript with shared contracts. (#118)
  • Refreshed desktop integration control plane. (#123)
  • Star-us prompt to surface GitHub repo. (#5)

Fixed

Stability & reliability

  • Chat runs survive web reconnects. (#146)
  • Daemon project-root resolution when launched from src via tsx. (#162)
  • SSE keepalive behind nginx. (#111)
  • Standalone pnpm binary supported in postinstall; install toolchain pinned. (#35, #151)
  • Surface unfinished todo runs in chat. (#76)

Cross-platform / Windows

  • Spawn agents via resolved absolute path on Windows. (#13)
  • Deliver prompts via stdin for non-Claude agents to avoid spawn ENAMETOOLONG. (#15)
  • Mitigate Windows ENAMETOOLONG and fix daemon crash on cleanup. (#75)
  • Fix PROMPT_TEMP_FILE() call and Claude Code stdin delivery on Windows. (#97)
  • Normalize web dev tsconfig paths on Windows for tools-dev. (#174)
  • Support Claude Code CLI <1.0.86 (avoid --include-partial-messages, parse assistant wrapper text). (#34)

Daemon & providers

  • CORS header on raw project file endpoint. (#140)
  • Preserve non-ASCII filenames on multipart upload. (#166)
  • Stop passing literal dash to cursor-agent. (#160)
  • Non-interactive permissions for agent CLIs in web UI. (#26)
  • Codex plugin disable env. (#133)
  • Codex assistant agent labels. (#70)

Web UI

  • Welcome dialog: stop overwriting user's agent pick on Save. (#4)
  • Allow Claude Code to read skill seeds and design-system specs. (#7)
  • Question form checkbox selection limits enforced. (#81)
  • SettingsDialog content overflow + scrolling, refactored layout and modal styling. (#83, #88)
  • Duplicate H. heading in discovery.ts (→ I.). (#87)
  • guizang-ppt: sync host slide counter on transform-paginated decks. (#19)
  • Toolbar button text wrapping prevented for CJK languages. (#178)
  • PreviewModal exits fullscreen on first Esc. (#168)
  • Dev indicator moved to bottom-right corner. (#108)
  • Design Files: align upload picker with dropzone, neutral agent copy, remove unsupported Figma copy. (#199, #200, #201)
  • Web locale registry test includes Japanese. (#202)

Documentation

  • README refresh with stats, agents, skills, and metrics workflow. (#173)
  • Korean (한국어) and Japanese README and docs translations. (#105, #183)
  • TRANSLATIONS.md i18n contribution guide. (#196)
  • Refresh environment setup guidance. (#104)
  • Xiaohongshu design-system docs review feedback. (#54)

Internal

  • Initial project structure, project rename "Open Claude Design" → "OpenDesign", naming optimization. (#1, #2)
  • Initial AGENTS.md and OpenCode agent instructions. (#114)
  • Beta release workflow placeholder. (#36)
  • Git commit co-author policy. (#131)