Commit Graph
274 Commits
Author SHA1 Message Date
open-design-release-bot[bot]GitHubopen-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com>
38c20ef6fa chore(release): bump main to 0.14.1 ahead of stable 0.14.0 (#5287)
Co-authored-by: open-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com>
2026-07-08 06:06:47 +00:00
8ca68bbfe6 feat(web): pre-run Open Design Cloud balance gate with subscription dialog (#5103)
* feat(web): Open Design Cloud pre-run balance gate — two-tier dialogs with auto-resume

Gate every AMR chat send before any run spawns. HARD tier (definitive
empty wallet <= $0, or signed out) blocks with a subscription-invitation
dialog: distinct copy and CTA per cause (console wallet page vs in-app
sign-in), benefits card, and auto-resume — a completed sign-in or a
recharge observed by a bounded wallet watch resumes the parked task on
its own. SOFT tier (<= $4) shows a dismissible reminder with top-up /
start-anyway / persisted don't-ask-again (never silences the hard tier).

Blocked project sends park in the conversation queue with their full
payload and pause auto-drain until a send passes the gate; home submits
hold the create open (continuation promise) so drafts and staged context
are never lost, and the home decision carries into the project's first
auto-send to avoid double-prompting. Fail-open contract: unknown or
unparseable wallet answers never block; hard blocks are confirmed with
refresh=1 before firing. New amr_entry sources cover both surfaces and
tiers; nine chat.amrBalanceGate.* / chat.amrLowBalance.* i18n key groups
ship in all 19 locales.

* fix(web): a stale wallet-refresh echo must not confirm a hard block

A failed refresh returns the previous cached snapshot with stale=true and
an error; treating it as a fresh definitive $0 (or signed-out) answer
violated the gate's fail-open contract and could strand a user who just
topped up while the wallet endpoint hiccuped. (Review feedback.)

* chore: retrigger ci

* test(e2e): align AMR UI checks with balance gate

* fix(web): preserve AMR gated sends across conversation switches

---------

Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>
Co-authored-by: Amy <1184569493@qq.com>
Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com>
2026-07-07 09:20:54 +00:00
Denis RedozubovandGitHub 8f177cf0f2 Expose run workspace provenance (#4264)
* Model orchestrator scratch workspaces

* Address scratch workspace contract review

* Expose run workspace provenance

* Type run workspace provenance responses

* fix(daemon): refresh routine run workspace metadata

* test(e2e): allow UI P0 cold start margin

* fix(daemon): allow realpath opencode cwd aliases
2026-07-05 14:35:43 +00:00
dde6228121 feat(daemon): add OD_ALLOWED_INTERNAL_HOSTS allowlist for internal provider endpoints (#4250)
* feat(daemon): add OD_ALLOWED_INTERNAL_HOSTS allowlist for internal provider endpoints

Fixes #3225. The 0.8.0 SSRF hardening blocks provider base URLs that
resolve to private/internal ranges, with no way to opt a trusted host
out -- breaking internally-hosted LiteLLM/Ollama gateways on VPN-only
RFC1918 addresses.

Add an operator opt-in: OD_ALLOWED_INTERNAL_HOSTS exempts listed hosts
from the internal-IP guard, threaded only into user-configured provider
endpoints (connection test, model discovery, BYOK chat). The
attacker-controllable asset-download guard stays strict. Malformed and
CIDR entries are dropped with a warning rather than silently trusted.
The Settings base-URL field no longer rejects internal-IP URLs
client-side, deferring the authoritative decision to the daemon.

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

* test(e2e): update BYOK base-URL P0 for relaxed internal-IP validation (#3225)

The P0 spec used an internal-IP URL as its 'invalid' example to assert the
client-side error; that URL is now accepted (the daemon owns the allowlist
decision). Switch to a non-http scheme, which is still rejected client-side.

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

* fix(web): defer internal-IP base URL to the daemon in BYOK draft validation (#3225)

isValidApiBaseUrl was relaxed, but the Test and model-fetch actions gate
on validateByokDraft -> blockingByokDraftIssues, which still treated any
validateBaseUrl error (including a forbidden internal IP) as a blocking
base_url_invalid. So the Test button appeared but the action stayed
blocked client-side and never reached the daemon allowlist. Treat a
syntactically-valid forbidden URL as non-blocking here too; genuinely
malformed / non-http URLs still block.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 14:35:28 +00:00
Christopher BeaulieuandGitHub f6fb7c204c fix(web): reattach spuriously-failed messages on reload when daemon run succeeded (#4607) (#4651)
* test(e2e): add red regression for #4607 artifact persistence after reload

Failing test that captures the user-reported symptom from #4607: when
a real daemon run is reloaded mid-flight, the run does not reach
'succeeded' and producedFiles is not populated on the assistant
message.

Separates the runStatus and producedFiles polls so that the race
between reattach and artifact-write finalisation is observable; the
existing bundled assertion in the prior test was masking the bug.

Refs #4607

* fix(web): reattach spuriously-failed messages on reload when daemon run succeeded (#4607)

When the SSE reconnect fallback fires while the daemon run is still
in flight (e.g. the client retried up to the reconnect limit before
any events arrived), the message's run_status is written as 'failed'.
The reconcileAssistantMessageOnRunEnd SQL UPDATE cannot rescue this
because its WHERE clause excludes the 'failed' state.

After page reload, attachRecoverableRuns skips these messages because
neither isActiveRunStatus nor shouldReplayTerminalRunMessage matches
a 'failed' entry, so no reattach fires, the artifact is never written,
and producedFiles stays empty.

Expand needsFullReplay to also cover messages with runStatus='failed'
that have a runId but no content and no producedFiles — the signature
of a spurious failure rather than a real one.  The existing status
fetch (fetchChatRunStatus) below the needsFullReplay check returns
the daemon's authoritative status; if the run was still active, it
updates the message to 'running', which triggers full reattach on
the next effect fire; if the run already succeeded, reattachDaemonRun
replays the buffered events and onDone writes the artifact.

Legitimately failed runs (with content or producedFiles) are not
affected, and completedReattachRunsRef prevents duplicate reattach.

Confirmed with: e2e/ui/real-daemon-run.test.ts lines 161, 187, 221, 278, 299 — all pass.

* review: address PR #4651 feedback

- heuristic gate: spuriouslyFailedPending is now a separate predicate
  from needsFullReplay; the recovery path is gated on the fetched daemon
  status (queued/running/succeeded) — genuine failed runs are left intact
  with their persisted error context and resumable flag

- register live-stream failures in completedReattachRunsRef via a new
  currentRunId closure variable so attachRecoverableRuns never races a run
  the client already knows failed (fixes threads-resumable-flag regression)

- error clear: setError(null) is called when spuriously-failed recovery
  moves back to running/succeeded (on detection and on reattach onDone)
  so the stale "daemon stream disconnected" banner does not persist after
  successful reattach

- e2e test tighten: capture {projectId,conversationId} BEFORE page.reload()
  so the post-reload polls assert the same conversation; use
  expectRestoredDelayedAssistantMessage with requireRunId:true to prove
  the assistant was reattached in-place rather than recreated

* review: address PR #4651 round 2 feedback

- apply status.resumable from daemon when daemon-confirmed failed
- assert runId equality post-reload to prove in-place reattach

* review: address PR #4651 round 3 — keep null-status retryable

Only add runId to completedReattachRunsRef when fetchChatRunStatus
returns an authoritative status. A null return covers any non-OK
response or fetch exception (per providers/daemon.ts:686), not
only authoritative not-found, so a transient reload-time hiccup
must remain retryable on the next effect tick.

* review: address PR #4651 round 4 — bounded retry on transient null status

When fetchChatRunStatus returns null for a spuriouslyFailedPending
row, schedule up to MAX_TRANSIENT_RETRIES (2) retries via a 3s
timer that triggers effect re-entry. After the cap is reached the
run is added to completedReattachRunsRef so we never spin
indefinitely. Timers are tracked in a ref Set and cleared on
effect cleanup.

* review: fix unbounded retry — preserve attempt counts across effect re-entry

The Map reset at effect entry zeroed retry counts every time the
timer-driven recoveryTick bumped, so attempts >= MAX_TRANSIENT_RETRIES
never held and the cap promotion to completedReattachRunsRef never
fired. Refs persist across renders, so the unconditional reset was
both redundant and the source of the unboundedness.

Also clear stale Map entries on success and on cap promotion so the
Map doesn't grow without bound for long-lived sessions.

Caught by local codex review before merge.

* review: clear endedAt on spurious-failure recovery (#4651)

streamViaDaemon.onError stamps endedAt at SSE-disconnect time. When
attachRecoverableRuns later flips the row from 'failed' back to a
live daemon status, the stale disconnect timestamp lingered and the
replay finalizers (prev.endedAt ?? Date.now()) preserved it,
skewing the assistant elapsed-time footer for exactly the runs this
PR repairs.

Clear endedAt at the recovery-detection site so the existing
finalizers naturally stamp Date.now() when replay completes.

* review: scope endedAt to spurious-failure + don't seal generic disconnects (#4651)

- endedAt: undefined clear is now gated on spuriouslyFailedPending,
  so already-succeeded rows reaching needsFullReplay via
  shouldReplayTerminalRunMessage keep their original terminal time.
  Fixes UI P0 regression where unconditional clear broke workspace
  mount and drifted persisted run durations forward.

- streamViaDaemon.onError now only adds runId to
  completedReattachRunsRef on authoritative terminal failures.
  Generic "daemon stream disconnected before run completed"
  (browser SSE reconnect-budget exhausted while daemon still
  queued/running) stays eligible for attachRecoverableRuns to
  re-query daemon authoritative status next tick. Supersedes
  follow-up issue #4717.

* review: mirror generic-disconnect gate to reattach onError (#4651)

- Bound generic-disconnect retries by reusing transientFailedRetriesRef
  / MAX_TRANSIENT_RETRIES so a flapping daemon can't trigger an unbounded
  reattach loop. Caught by local codex review before push.
- Use a separate genericDisconnectRetriesRef so null-status and
  generic-disconnect transient errors don't share one per-run budget
  and cause premature sealing. Caught by second codex pass.

* review: reset transient retry budgets on recovery (#4651)

Round-8 nettee BLOCKING + codex P2. The genericDisconnectRetriesRef
counter was only cleared on terminal outcomes, so two intermittent
SSE disconnects separated by a clean recovery still hit the cap and
sealed an otherwise-live daemon run.

Reset both transient retry budgets at three recovery sites:
- attachRecoverableRuns: after a successful fetchChatRunStatus
  for a still-active run (queued/running/succeeded).
- reattachDaemonRun onRunStatus: when the live stream reports
  running/succeeded.
- startDaemonRun onRunStatus: same, guarded by currentRunId.

Cap behavior now matches reviewer intent — fires only on
*consecutive* disconnects without intervening recovery.

* review: reset retry budgets on stream progress, not status fetch (#4651)

Round-9 nettee BLOCKING + codex P2. The round-8 fix reset
genericDisconnectRetriesRef on fetchChatRunStatus recoverable
result and on onRunStatus running/succeeded, but neither is
real stream progress: status fetch is REST-only, onRunStatus
transitions to running before any SSE data. If SSE keeps
flapping while daemon REST stays running, every reattach tick
reset the counter and the cap never fired — infinite reattach
loop.

Move resets to actual stream-data callbacks (onDelta and
onAgentEvent) in both reattach and live-stream paths. SSE
payload arrival is real recovery; a queued→running transition
is not.

Remove the three round-8 resets; add resets at four stream-data
callbacks (reattach onDelta + onAgentEvent, live-stream onDelta
+ onAgentEvent). Cap now correctly fires on consecutive SSE
disconnects without intervening data, and never fires when
the daemon is actually delivering events.

* fix(web): keep active disconnect recovery retryable

* refactor(web): share generic disconnect marker

* fix(web): preserve terminal success after disconnect

* fix(web): back off active disconnect retries

* fix: preserve terminal daemon status after disconnect

* fix: preserve recovered daemon terminal state

* fix: harden daemon disconnect recovery

* fix: clear transient disconnect error pills

* fix: use structured daemon disconnect errors

* fix: replay truncated reattach terminal success

* test: pin generic disconnect code path

* fix: preserve canceled reload recovery state

* test: align daemon disconnect mocks

* fix: replay partial live disconnect success

* fix: clear reattach streaming before status probe

* fix: preserve replay terminal status

* fix: keep live disconnect recovery eligible

* fix(web): finalize canceled reattach telemetry

* fix(web): defer reattach telemetry finalization

* test(web): lock reload recovery from spurious-failed run daemon already succeeded

Regression test for the #4607 reload-artifact-recovery path, covering the
client/daemon mismatch raised in review on #4651: a persisted assistant row at
runStatus=failed with empty producedFiles while the daemon run authoritatively
reached succeeded before the client's reload/reattach recovery pass runs.

Asserts post-reload recovery: content restored, producedFiles repopulated, run
reattached in-place (same runId + conversationId, not recreated), artifact
project file present. Distinct timing window from the existing "reload while
still running" coverage in real-daemon-run.test.ts.

Browser SSE fault-injected via page.route (connectionreset on /api/runs GET +
events); daemon process untouched; state seeded through production HTTP only, no
source-level backdoor. content:'' is informational only — the daemon persists
content via an internal write path the browser network stack (and thus
page.route) cannot intercept, so it cannot be held empty reliably at the UI
layer; the precondition gate holds on runStatus/producedFiles/runId/daemonStatus,
which reproduce the artifact-missing symptom class stably.

* fix(web): reconcile endedAt from daemon terminal time on generic-disconnect recovery

When a run's live SSE stream or a reattach stream hits the generic
daemon-disconnect error, the client stamps endedAt to the disconnect
time. After the retry budget is exhausted it probes fetchChatRunStatus;
when that probe returns an authoritative terminal status, the
reconciliation kept the stale disconnect-time endedAt via
`prev.endedAt ?? ...`, leaving persisted duration/finalization wrong
after recovery.

Adopt the daemon's authoritative terminal timestamp
(ChatRunStatusResponse.updatedAt) for endedAt in the generic-disconnect
terminal reconciliation branches:

- Live onError retry-exhaustion terminal branches -> latestRunStatus.updatedAt.
- Reattach onError retry-exhaustion terminal branches -> latestRunStatus.updatedAt.
- Reattach effect loop-level probe: onDone finalization now uses
  status.updatedAt; the canceled short-circuit now sets endedAt
  (previously left unset, preserving the stale disconnect stamp).

Adds regression tests locking endedAt to the daemon terminal time for
reattach-succeeded, reattach-canceled, and live-succeeded generic
disconnect recovery. Addresses review feedback on #4607.

* fix(web): stamp recovered endedAt from daemon terminal time, not stale snapshots (#4607)

Reload-during-active-run reattach and generic-disconnect recovery were
persisting endedAt from a non-authoritative timestamp -- the pre-reattach
`running` snapshot or the disconnect-time stamp -- instead of the daemon's
terminal `updatedAt`. This corrupted persisted run duration and reordered
conversations after recovery.

Introduce resolveTerminalEndedAt(runId, candidate) whose docblock states the
invariant: every terminal-recovery branch stamps endedAt from an authoritative
terminal updatedAt observed at the end of recovery, threaded to both the
assistant message row and updateConversationLatestRun. Route the recovery
branches through it; thread the terminal updatedAt into the conversation-level
metadata on generic-disconnect recovery; and on the reattach onError
recoverable-artifact branch overwrite endedAt only on confirmed-succeeded --
the earlier synchronous disconnect stamp otherwise poisoned a `prev.endedAt`
nullish guard, silently keeping the stale time.

Adds 4 app-local regressions pinning: reload-active endedAt advances past the
running snapshot; conversation latestRun metadata reconciles; generic-disconnect
message row + conversation both advance; and reattach-artifact-after-non-generic-
error adopts the terminal time.

Addresses review threads at ProjectView.tsx:3940, :5313, :5278.

* fix(web): recognize legacy no-code generic-disconnect events in reload recovery (#4607)

hasGenericDisconnectFailureEvent matched only the new structured
DAEMON_STREAM_DISCONNECTED code, so failed messages persisted before
this PR started tagging disconnects with a code (legacy status/error
events carrying only the detail string, no code) were skipped by
attachRecoverableRuns on reload even when the daemon reported the run
succeeded. Broaden the predicate to also match the canonical
GENERIC_DAEMON_DISCONNECT_MESSAGE detail, mirroring isGenericDaemonDisconnect
which already checks code-or-message on live Error objects.

Adds a frozen regression seeding a failed row with partial content plus a
message-only legacy disconnect event; verified red with the impl stashed
(reattach never entered) and green with the fix.
2026-07-05 14:33:23 +00:00
32011bdfcc [codex] Route BYOK API providers through OpenCode (#4876)
* feat(byok): route API providers through OpenCode

* test(web): align BYOK OpenCode expectations

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(byok): preserve OpenCode media defaults

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(byok): forward OpenCode media defaults

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* test(e2e): align API empty stream smoke with daemon runs

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(byok): guard OpenCode API reroute

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* test(e2e): expose BYOK OpenCode in API smoke

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(byok): preserve Azure and Ollama provider contracts

* fix(byok): preserve media default fallbacks

* fix(byok): keep api history for opencode routing

* fix(daemon): alias byok-opencode detection env

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* fix(daemon): isolate BYOK provider config

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* fix(web): keep Bedrock BYOK unsupported client-side

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* fix(byok): allow keyless local Ollama OpenCode runs

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* fix(web): label remaining BYOK OpenCode agents

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* fix(daemon): reject Azure BYOK without base URL

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* fix(byok): preserve keyless vLLM OpenCode runs

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* fix(byok): omit auth for keyless OpenCode providers

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-07-03 09:18:08 +00:00
b5b2b2c974 fix(web): stop MCP server snippet code from inheriting the inline-code chip (#4516)
In Settings -> MCP server, the generated CLI command (`claude mcp add-json …`)
renders inside a dark `<pre><code>` block, but the inner `<code>` had no style
of its own and inherited the global inline-`code` chip rule from primitives.css
(`background: var(--bg-subtle)` + `padding: 1px 5px` + rounded corners). On the
wrapped bash one-liner each wrapped segment painted its own light rounded
rectangle, reading as permanent selection highlights (#4509).

Reset the inner `<code>` to a transparent, padding-less, inherit-color/font
wrapper so it stops inheriting the inline-code chip styling and instead picks up
the dark block's own color and monospace font. Applies to every CLI client
(Claude / Codex / …) since they share the snippet block.

Add a Playwright regression check that the snippet `<code>` computed background
is transparent (it was the light `--bg-subtle` chip before this change).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 02:59:42 +00:00
AmyandGitHub cd6fb44c4d test: expand e2e coverage for main updates (#4998)
* test: expand e2e coverage for main updates

* test: remove unrelated landing contract coverage
2026-07-03 02:58:21 +00:00
ab45324124 [codex] Restore Kimi ACP and retire Gemini CLI runtime (#5023)
* fix(runtime): restore kimi acp and retire gemini cli

* test(e2e): drop retired gemini fake runtime from p0 sweep

* fix(daemon): clear retired Gemini config

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-07-02 07:47:05 +00:00
e0d7a77230 fix(release): align stable notes public origin (#5037)
Co-authored-by: Siri-Ray <2667192167@qq.com>
2026-07-02 06:10:41 +00:00
6132da1d79 [codex] Restore stable release publish mode (#5031)
* fix(release): restore stable publish dispatch mode

* test(e2e): cover stable publish dispatch mode

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
2026-07-02 04:30:58 +00:00
CaprikaandGitHub 8d83d24907 [codex] Keep AMR settings card actions inline (#4993)
* Keep AMR settings card actions inline

* Keep AMR plan badge outside clipped benefits

* Move AMR plan badge to account row

* Prevent AMR benefit badges from clipping

* Remove lower cost AMR badge

* Rename AMR console action to manage

* Carry over logo preview and AMR manage fixes
2026-07-01 09:08:43 +00:00
Tom HuangGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>Amy
6be4e56544 feat(workspace): add plan mode and Excalidraw sketch flows (#4862)
* feat(daemon): add 'plan' session mode and update related functionality

- Introduced a new session mode 'plan' alongside existing 'design' and 'chat' modes, allowing for editable document creation.
- Updated various functions and interfaces to accommodate the new session mode, including normalization and usage in commands.
- Enhanced markdown rendering capabilities by integrating micromark and its GFM extension for improved markdown processing.
- Added new input types for question forms, expanding the range of user interactions.
- Updated UI components to reflect changes in session modes and ensure proper handling of next steps based on the current mode.

* feat(chat): integrate SessionModeToggle into ChatComposer and HomeHero

- Added SessionModeToggle component to both ChatComposer and HomeHero for improved session management.
- Updated HomeHero styles to accommodate the new mode switcher layout.
- Adjusted countdown timer in QuestionsPanel to extend the skip countdown from 120 seconds to 10 minutes, enhancing user experience.
- Added tests to ensure the countdown functionality works as expected.

* feat(FileViewer): implement synchronized scrolling for Markdown editor and preview

- Added functionality to synchronize scrolling between the Markdown editor and preview panes.
- Introduced new types and utility functions for managing scroll behavior.
- Enhanced the MarkdownViewer component to handle scroll events and maintain scroll position across different modes.
- Updated the component's state management to accommodate the new scrolling features.

* feat(excalidraw): integrate Excalidraw into the project

- Added @excalidraw/excalidraw as a dependency in package.json.
- Updated vitest configuration to include an alias for Excalidraw.
- Imported Excalidraw CSS in the layout component for styling.
- Modified AssistantMessage component to handle optional projectId.
- Enhanced FileOpsSummary to display delete operations.
- Implemented new Excalidraw scene management in SketchEditor and FileWorkspace components.
- Updated MarkdownViewer to support file mentions and improved file handling.
- Refactored various components to accommodate Excalidraw integration and ensure compatibility with existing features.

* feat(files): improve sketch and file handoff flows

* test: align post-merge expectations

* chore(nix): refresh pnpm deps hash

* fix(workspace): stabilize sketch persistence and ci checks

* fix(workspace): address review blockers in editable files

* fix(workspace): persist cleared sketch scenes

* test(workspace): type sketch editor mock scene

* fix(workspace): serialize sketch autosaves

* fix(workspace): keep sketch save revisions current

* test(e2e): stabilize project workspace smoke flows

* fix(analytics): preserve plan mode for BYOK runs

* test(e2e): stabilize new project rail interactions

* fix(files): stop bash delete parsing at shell operators

* test(e2e): stabilize ui cold-start suites

* fix(viewer): preserve absolute markdown image sources

* feat(workspace): preload sketches and enhance markdown save options

- Added functionality to preload persisted sketches before opening the tab.
- Introduced new MarkdownSaveOptions type to manage save behavior.
- Updated saveMarkdownText to handle options for refreshing files and showing saving state.
- Enhanced FileViewer to maintain focus and selection during metadata refresh.
- Implemented a Toast component for user feedback on save and export actions.

* fix(web): stabilize markdown and sketch editor polish

* fix(web): finish sketch editor merge resolution

* chore(nix): refresh pnpm deps hash

* fix(plan): bypass discovery and stabilize markdown sync

* fix(web): simplify scene retrieval in SketchEditor component

* feat(web): enhance markdown viewer with auto-save functionality

- Implemented passive auto-save status in the MarkdownViewer component, replacing the manual Save button with an auto-save indicator.
- Introduced new hooks and state management for tracking auto-save events and displaying the last saved time.
- Added support for synchronized scrolling between the markdown editor and preview.
- Created a new markdown-scroll-sync module to handle scroll synchronization logic.
- Updated localization files to include new strings for auto-save messages.
- Added a SketchEnginePrewarm component to optimize Excalidraw loading times.

* feat(web): enhance session mode toggle with cost indicators

- Added cost tiers for each session mode in the SessionModeToggle component, providing users with a visual representation of usage costs.
- Introduced a new ModeCostTag component to display cost information alongside session mode labels.
- Updated localization files to include new keys for cost labels and notes.
- Enhanced styling for cost indicators to improve user experience and clarity.
- Refactored EntryShell to open the new project modal instead of creating a blank project directly from the rail.
- Implemented a utility function in markdown-scroll-sync to check for vertical progression in block offsets.

* feat(web): add max height adjustment for session mode description card

- Introduced maxHeight prop to the ModeDescriptionCard component to control the height of the description card based on available space.
- Implemented useLayoutEffect in SessionModeToggle to dynamically calculate and set the maximum height of the description card, ensuring it does not overlap with the project tab bar.
- Updated tests to verify that session modes display their expected usage/cost correctly in the UI.
- Enhanced localization files to include new cost-related strings for various languages.

* feat(web): implement goBack function for improved navigation and update auto-open logic

- Added a new `goBack` function to handle in-app navigation, allowing users to return to the previous route instead of a hardcoded destination.
- Updated the `navigate` function to maintain history state for better back navigation.
- Refactored auto-open logic to prioritize produced artifacts, allowing markdown files to be opened alongside HTML files.
- Updated tests to cover new navigation behavior and artifact selection logic.
- Enhanced localization files to include new descriptions for workspace actions.

* refactor(web): remove create design system functionality and update design files panel actions

- Removed the `onCreateDesignSystem` prop and associated button from the DesignFilesPanel component.
- Updated the empty state actions to include a button for creating a new document via the `onPaste` function.
- Adjusted tests to reflect the removal of the design system creation action and ensure the new document button is functional.
- Enhanced the MarkdownViewer component by adding a placeholder for the text area and removing the header bar for a cleaner interface.
- Updated localization files to include a new placeholder string for the markdown editor.

* fix(web): restore markdown placeholder translations

* refactor(web): streamline MarkdownViewer and enhance localization

- Removed unnecessary state management and reload functionality from the MarkdownViewer component for improved performance.
- Added a placeholder text for the markdown editor in multiple localization files to enhance user guidance.
- Updated styles for the save state indicator in the viewer to improve visual clarity and alignment.
- Adjusted tests to reflect changes in the MarkdownViewer and ensure proper functionality.

* fix(web): adjust SketchEditor button size and remove shortcut hints

- Reduced the icon size in the SketchEditor component from 13 to 12 for better alignment.
- Updated the removeSketchMermaidShortcutHints function to also remove the submit shortcut hints from the dialog, enhancing the user interface by decluttering unnecessary elements.
- Adjusted tests to verify the absence of shortcut hints in the modal after updates.

* fix(web): address plan mode follow-up polish

* fix(web): align ci expectations after merge

* test(e2e): stabilize project workspace helpers

* test(e2e): scale settings visual timeout

* fix(prompts): lock ElevenLabs voice picker choices

* test(e2e): pin project workspace P0 worker

* test(e2e): use rail new project entry

* test(e2e): relax app restoration startup waits

* fix(web): limit markdown pipe escaping to tables

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
Co-authored-by: Amy <1184569493@qq.com>
2026-07-01 04:38:31 +00:00
Tom HuangandGitHub be701787d6 fix(web): clarify plugin remix actions (#4969) 2026-07-01 03:29:12 +00:00
AmyandGitHub 56c410e9ef [codex] strengthen daemon diagnostics coverage (#4948)
* test: expand automation coverage gaps

* test: cover additional automation gaps

* test: strengthen daemon diagnostics coverage

* fix(e2e): align media provider project modal helper
2026-06-30 16:25:17 +00:00
Tom HuangandGitHub ca89845d03 feat(brands): surface the draft design system live and browse images in a lightbox (#4777)
* feat(brand-extraction): enhance programmatic extraction with reconciliation and UI improvements

- Introduced `reconcileProgrammaticExtractionTranscript` to manage the state of brand extraction transcripts, ensuring accurate status updates during extraction processes.
- Updated the `cancel-extraction` route to handle asynchronous operations, allowing for better user feedback and immediate reconciliation of extraction states.
- Enhanced the `startBrandExtraction` function to support draft design system creation, ensuring that users can see their systems immediately, even if extraction fails or is stopped.
- Improved error handling and logging for reconciliation processes, providing clearer insights into failures during brand extraction.
- Added tests to validate the new reconciliation logic and ensure that the extraction process behaves as expected under various scenarios.

These changes improve the overall user experience by providing more robust handling of brand extraction states and enhancing the visibility of the extraction process.

* feat(brand-extraction): enhance brand preview rendering and UI feedback

- Added functionality to render brand previews into projects when a draft is stopped, improving user experience by providing immediate visual feedback.
- Updated the `startBrandExtraction` function to include metadata for design system IDs and statuses, ensuring accurate tracking of brand extraction states.
- Enhanced the `renderBrandPreviewIntoProject` function to support new status types, including 'draft', allowing for better representation of brand states.
- Introduced new localization strings for draft-related messages, improving clarity for users during the brand extraction process.
- Added tests to validate the new preview rendering and status handling, ensuring robustness in the brand extraction workflow.

These changes improve the overall user experience by providing clearer feedback and enhancing the visibility of brand extraction processes.

* feat(design-kit): browse design-system images in a lightbox carousel

The image-preview lightbox showed a single static image with no way to move
between samples. Replace the single-image state with an index into the
visible, non-broken samples so the modal becomes a carousel: prev/next
chevrons, an "N / total" counter, ArrowLeft/ArrowRight keys, and wrap-around.
Broken tiles are filtered once through a shared lightboxItems memo so the
gallery and lightbox indices stay aligned with the delete handler.

* feat(brands): reveal the draft design system the moment extraction starts

A brand extraction pre-creates a draft `user:` design system, but the project
view only rendered it once the registry summary had refreshed, leaving the
Design System tab empty for the whole extraction. Build a fallback summary
from project metadata so the draft renders immediately, auto-open the Design
System tab when extraction begins or is cancelled, and hydrate the freshly
created project with its brand metadata. Draft pre-create now records the
source URL in provenance and fails loudly instead of swallowing the error.

Because ProjectView imports DESIGN_SYSTEM_TAB at module scope, every
FileWorkspace test mock must export the constant.

* fix(design-system-picker): restore fullscreen preview styles dropped in routines.css reorg

The brand-extraction routines.css reorganization accidentally removed the
`.project-ds-picker-fullscreen` rule block, its `project-ds-picker-fullscreen-in`
keyframes, and its responsive overrides. The modal-window drag guard and two
style specs still depend on those rules (the fullscreen design-system preview
must stay above modal chrome), so they went red. Restore the block verbatim to
match main.

* feat(brand-extraction): add continue extraction functionality for incomplete brands

- Introduced a new API endpoint to restart the deterministic programmatic extraction for existing brands without creating duplicates.
- Implemented a tracking mechanism for ongoing extractions to manage their state effectively.
- Enhanced the UI components to support the new continue extraction action, including loading states and callbacks.
- Updated relevant tests to ensure the new functionality works as expected and integrates smoothly with existing workflows.

These changes improve the user experience by allowing users to resume brand extractions seamlessly, enhancing the overall efficiency of the brand management process.

* feat(skills): integrate skill management into the application

- Added a new SkillDetailsModal component to display detailed information about skills.
- Enhanced the ChatComposer and HomeHero components to support skill selection and details viewing.
- Updated the ComposerPlusMenu to include a skills submenu, allowing users to search and pick skills.
- Implemented functionality to handle skill details in various components, improving user interaction with skills.
- Refactored existing components to accommodate skill-related features, ensuring a cohesive user experience.

These changes enhance the application's capabilities by providing users with better access to skill information and management, streamlining workflows involving skills.

* feat(brand-extraction): improve brand assistant message handling and UI feedback

- Enhanced the AssistantMessage component to better manage events and display content, ensuring that messages are rendered correctly based on their content and events.
- Updated the ChatPane component to hide empty brand assistant messages and improve the visibility of relevant content, enhancing user experience during brand extraction processes.
- Introduced new utility functions to determine the visibility of brand assistant events and content, streamlining the logic for displaying messages.
- Refactored the ProjectView component to handle brand browser assist confirmations more effectively, ensuring that the UI responds appropriately to user interactions.
- Added tests to validate the new message handling logic and ensure that brand extraction messages are displayed correctly.

These changes enhance the overall user experience by providing clearer feedback and improving the management of brand assistant messages during extraction workflows.

* fix(brands): address browser assist review blockers

* fix(web): restore new project modal entrypoints

* fix(web): switch to replacement brand retry conversations

* fix(web): restore skill-change preview invalidation

* fix(brand): harden retry completion flow

* fix(brand): close extraction lifecycle races

* test(e2e): wait for privacy banner dismissal

* fix(brand): guard retry startup races

* fix(brand): isolate extraction retry attempts

* fix(design-systems): clean up partial draft creation

* fix(brands): let stop supersede stalled transcript

* fix(brands): fail thin browser html retries

* fix(brands): preserve stopped html retries

* fix(brands): reject off-path browser retries

* fix(brands): expose deterministic retry in cli

* fix(brands): serialize cancel with finalization

* fix(brands): bound cancel settlement wait

* fix(brands): bound retry takeover waits

* refactor(next-step-actions): replace titles with localized keys and update related prompts

* fix(brands): retry after browser read failures

* fix(brands): accept canonical browser redirects

* fix(brands): reset retry assist stall timer

* test(web): align picker compact preview assertions

* fix(brands): harden design kit cleanup paths

* test(e2e): stabilize AMR runtime recovery checks

* feat(brands): enhance brand extraction from post-wall pages

- Introduced `browserHarvestIsUnusable` function to determine if a harvested page lacks usable brand signals, allowing for more permissive extraction from content-rich pages that may embed anti-bot widgets.
- Updated `extractBrandFromHtml` to utilize the new function, ensuring that valid post-wall pages are processed correctly.
- Enhanced `isChallengePage` to differentiate between genuine content-rich pages and bot protection interstitials, improving the accuracy of brand extraction.
- Added tests to validate the new behavior, ensuring that pages with minimal color palettes or embedded widgets are handled appropriately.

* feat(brands): improve handling of recoverable extraction states

- Refactored brand extraction logic to maintain a `needs_input` status for recoverable scenarios, such as blocked pages or mid-load content, instead of marking them as `failed`.
- Updated related functions and routes to reflect this change, ensuring that the user interface presents a calm, retryable state rather than a terminal failure.
- Enhanced tests to validate the new behavior, confirming that the system correctly identifies and manages recoverable extraction attempts.

* feat(projects): add design-system project creation from existing projects

- Implemented a new command `od project create-design-system` to duplicate a regular project as a design-system workspace, including a prompt for design-system generation.
- Enhanced the API to support the creation of design-system projects, ensuring proper metadata and file copying from the source project.
- Added a detailed prompt structure for the design-system generation process, outlining requirements and expected outputs.
- Introduced tests to validate the new functionality, ensuring that design-system projects are created correctly with the appropriate context and files.
- Updated the UI components to integrate the new design-system creation functionality, allowing users to initiate the process seamlessly.

* feat(projects): add project duplication workflow

* fix(brand): keep browser assist recovery retryable

* fix(plugins): roll back failed template duplicates

* feat(brand): enhance browser assist functionality and improve user guidance

- Introduced a new `brandBrowserAssistOdCard` function to generate browser assist cards for better user interaction.
- Updated the content structure in `reconcileProgrammaticExtractionTranscript` to include browser assist cards when applicable.
- Enhanced user guidance in error messages for stalled and stopped extraction processes, providing clearer instructions for recovery actions.
- Added tests to ensure the new browser assist card functionality is correctly integrated and displayed in the chat interface.
- Improved localization for various user prompts and messages related to browser assist actions.

* feat(design-browser): enhance design file handling and user notifications

- Added support for opening design files directly from the status message in the DesignBrowserPanel.
- Introduced a new `actionTarget` property to differentiate between design files and regular files in status messages.
- Implemented a `stopLoading` function to halt page loading when necessary, improving user experience during snapshot captures.
- Updated localization files to include new status messages and project type labels.
- Enhanced toast notifications for browser snapshots with improved styling and functionality.
- Added tests to ensure new features work as expected and do not interfere with existing functionality.

* feat(app): implement design system project creation with preset prompts

- Updated project creation logic to route through the default design router, ensuring user interaction prompts for design system selection.
- Introduced a new localization key for the design system creation prompt, enhancing user guidance.
- Adjusted plugin ID to avoid assumptions about prototypes, improving the flexibility of project creation.
- Updated tests to reflect changes in project creation behavior and ensure correct routing and prompt handling.

* feat(chat): update icon sizes and implement draft persistence in HomeView

- Increased icon sizes in ChatComposer from 13px to 16px for better visibility.
- Added localStorage persistence for the Home composer prompt and design system selection to retain user input across tab switches.
- Implemented functions to read and write drafts to localStorage, ensuring a seamless user experience.
- Cleared drafts upon run creation to prevent reappearance of previous inputs.
- Enhanced Toast component to handle auto-dismissal correctly, even with changing parent components.

* fix(pr): address review blockers

* fix(web): preserve authoring prompt confirmation

* test(daemon): cover project CLI copy commands

* fix(web): surface skill detail load failures
2026-06-30 13:22:29 +00:00
lefarcenandGitHub 6c424f4ad5 fix(release): populate github.commit in prerelease metadata so the changelog has a baseline (#4960)
The Feishu release card (notify-release-feishu.yml) builds its changelog as
`git log <previous>..<current>`, reading <previous> from the published
prerelease/latest/metadata.json `.github.commit`. That field is written by
githubInfo(), which reads the RELEASE_COMMIT env. release-beta sets
RELEASE_COMMIT on every build + publish job; release-prerelease set it on none,
so every prerelease published an empty github.commit. Each prerelease then
found no baseline and the card always rendered "首个 Prerelease 包,无上个版本可
对比" — the changelog was permanently empty, not just for a fresh release line.

Add RELEASE_COMMIT to the four prerelease build jobs and the publish job,
mirroring release-beta. Both are required: publish-metadata rejects a
per-platform manifest whose github.commit disagrees with the metadata commit,
so the build jobs (which write per-platform manifests) and the publish job
(which writes the final metadata.json) must stamp the same built commit.

Red spec in packaged-smoke-workflow.test.ts asserts each prerelease build +
publish job carries RELEASE_COMMIT (red on main, green here).
2026-06-30 13:08:39 +00:00
e75cb416fc [codex] Add BYOK provider presets (#4902)
* Add BYOK provider presets

* Fix BYOK preset picker accessibility names

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* Hide unsupported Bedrock BYOK preset

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* Restore BYOK preset test coverage

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* Restore BYOK protocol fallback coverage

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* Hide Bedrock from BYOK protocol tabs

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Downgrade unsupported Bedrock chat configs

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Clear Bedrock secrets during config downgrade

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Scrub nested Bedrock config on downgrade

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Persist Bedrock config downgrade migration

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Disable MiMo model discovery fetch

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Tighten Bedrock runtime URL detection

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Scope BYOK key drafts to provider presets

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Preserve BYOK provider form drafts

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Filter BYOK media models from chat discovery

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Fix BYOK provider draft persistence

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* Fix BYOK account model load status

* Fix BYOK provider status coverage

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-06-30 11:22:05 +00:00
365553fc5d [codex] Fix deck keyboard double-step navigation (#4897)
* fix deck keyboard double navigation

* fix deck slide handler detection

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* Defer deck bridge slide fallback

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* Fix self-handling deck navigation fixture

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* Keep deck bridge fallback synchronous

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* Preserve synchronous deck bridge fallback

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix deck bridge constant slide handlers

* fix deck bridge stopped-handler state sync

* Fix focused deck keyboard double navigation

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-06-30 11:21:23 +00:00
3fb3e3ca4a ci(release): finalize-release — bump main + drop backport label after a stable ships (#4936)
* ci(release): finalize-release — bump main + drop backport label after a stable ships

* ci(release): arm merge queue + request core-maintainer review on the bump PR

main enforces require_code_owner_review and the bump touches CODEOWNERS-owned
release manifests, so a bot approval can't merge it hands-free (unlike
backport-automerge / bake-plugin-previews-automerge, whose targets/diffs avoid
code-owner review). Open the PR, arm the squash merge queue (gh pr merge --auto,
proven on main by bake-plugin-previews-automerge), and request the
@nexu-io/core-maintainers review that the code-owner rule needs.

* ci(release): trigger finalize via workflow_run, not release:published

release-stable.yml promotes its own draft to published with gh release edit
using GITHUB_TOKEN, and a release event minted by GITHUB_TOKEN does not trigger
other workflows — so a release:published trigger would never fire. React to
release-stable completing instead (the backport-automerge / bake pattern), and
gate on the open-design-vX.Y.Z release actually being published (draft=false)
so dry-runs (metadata/prepublish) correctly no-op.

* ci(release): resolve version from latest published release, not head_branch

PerishCode review (blocking): release-stable supports building one ref while
dispatched from another via its `ref` input, so workflow_run.head_branch can be
the dispatch ref, not the built release branch — parsing it would miss-fire or
read the wrong version. Read the version from the latest published release
instead (release-stable marks it --latest), which is branch-independent. Drop
the head_branch job-if guard; the draft/prerelease/X.Y.Z guards + bump
idempotency keep dry-runs a clean no-op.

* test(release): pin finalize-release workflow topology

PerishCode review (blocking): .github/AGENTS.md asks cross-workflow behavior
changes to update the topology tests, not rely on YAML review alone. Add a
focused [P2] test in packaged-smoke-workflow.test.ts (alongside the backport /
bake automerge coverage) that pins finalize-release's trusted-consumer contract:
workflows: [release-stable] + success/dispatch gating, App-token use, version
resolved via gh release view (latest/tag) with NO workflow_run.head_branch
dependency, draft/prerelease/X.Y.Z + skip handling, forward-only synchronized
bump, core-maintainer review request, and SHA-pinned merge-queue arming.

---------

Co-authored-by: lefarcen <ontf116@gmail.com>
2026-06-30 09:04:49 +00:00
3652fb7237 [codex] restore AMR wallet balance follow-ups (#4771)
* feat: show AMR wallet balance

* fix: preserve AMR wallet reauth state

* chore: retrigger PR validation

* fix: bound AMR wallet balance fetch

* chore: retrigger wallet validation

* chore: bump bundled Vela CLI to 0.0.18

* chore: retrigger Vela CLI validation

* chore(nix): refresh pnpm deps hash

* feat(web): reposition AMR as Open Design's cloud subscription (#4684)

* feat(web): reposition AMR as Open Design's cloud subscription

Fold the standalone "AMR" product into Open Design's own cloud service:
unify all user-facing copy, surface the signed-in account's plan +
wallet balance, and add upgrade CTAs.

- Copy: user-facing "AMR" / "Open Design AMR" -> "Open Design" across
  19 locales, agent labels, and daemon error strings (balance/recharge
  wording preserved).
- Account: daemon reads plan + balance via `vela billing summary`
  (CLI route, control-key) and injects them into the vela status; web
  shows plan + balance on the settings card, the model-switcher account
  row, and the runtime-switcher avatar row (balance field aligned with
  the console wallet's headline balanceUsd).
- Upgrade: filled "Upgrade" CTA next to the plan on all three surfaces
  when the tier is below max; opens the console wallet at ?view=plans.
- Runtime-switcher Open Design row restructured to carry account info
  inline, with a selected-state highlight.
- Analytics: new amr_entry elements settings_amr_upgrade /
  inline_amr_upgrade / avatar_amr_upgrade / avatar_amr_agent_card.
- specs/: design doc for the Open Design account/avatar direction.

* fix(daemon): surface live account for env-backed auth + isolate cache per credential

Address @nettee review on #4684:
- applyVelaLiveAccount() now populates plan/balance even when the login
  status has user:null (VELA_RUNTIME_KEY / VELA_LINK_URL env auth), so
  env-backed Open Design sessions no longer drop the fetched billing data.
- Key the live-account cache by the full credential revision (auth source
  + profile + userId + config mtime), not just profile, and clear it on
  logout — a logout / account switch can no longer surface the previous
  account's plan or wallet balance before the background refresh runs.
- Regression tests for both in tests/integrations/vela.test.ts.

Sync tests to the AMR -> Open Design copy:
- web: tests/providers/sse.test.ts error-string assertions.
- e2e: onboarding / logout / run-failure / login-pill specs + visual fixture.

* fix(i18n): drop duplicated "Open Design" in switchBody fallback copy

The AMR -> Open Design rename sweep collided with the existing brand
mention in chat.amrCard.switchBody, rendering "Open Design official
Open Design model service" across 13 locales (zh-TW, ja, ko, ar, es-ES,
hu, id, fa, pl, tr, ru, pt-BR, uk). Remove the redundant second mention
so the retry/switch card reads cleanly. Flagged by @nettee on #4684.

Also start aligning InlineModelSwitcher unit tests to the new copy
(agent display name AMR -> Open Design, compact error string).

* fix(amr): resolve live billing before first /status; hide Upgrade until plan known

Address @nettee review on #4684 (two correctness issues in the new
plan/balance path):

- Cold cache: the signed-in /status handler now BLOCKS the first response
  on fetchVelaBillingSummary() when the cache is empty, instead of
  returning config-only and refreshing in the background. The new account
  surfaces read /status once and never re-poll, so the old flow left
  plan/balance hidden until the user refocused. Warm cache keeps the
  non-blocking fast path. fake-vela gains a `billing summary` handler and
  vela.routes.test.ts covers the cold-cache-first-open case.
- Upgrade CTA: canUpgradeVelaPlan() now treats an unknown/empty plan as
  NOT upgradeable, so a signed-in session whose live summary hasn't
  resolved no longer flashes Upgrade at top-tier users. Unit-tested.

* style(amr): bold the plan/balance text in the switcher + avatar account rows

The settings card already renders both bold (the plan value reuses
.agent-card-amr-balance-value); align the inline switcher account row and
the runtime-switcher avatar row so plan + balance read with the same
emphasis everywhere.

* fix(amr): single-flight cold billing fetch + surface plan/balance on its own field

Address @nettee follow-up review on #4684:

- Race: the cold-cache decision keyed off the refresh throttle, so a second
  /status arriving during the first billing fetch saw refreshAccount=false,
  fell through, and returned config-only — which the read-once surfaces can't
  recover from. Now `peekVelaLiveAccount(key) === null` is the cold signal and
  the billing fetch is single-flighted per credential revision: concurrent
  cold callers await the same in-flight promise. setVelaLiveAccount stamps the
  fetch time so the warm path doesn't immediately re-refresh.
- Identity: applyVelaLiveAccount no longer fabricates a `{ id: '', email: '' }`
  user for env-backed sessions. The live billing projection now rides on a new
  VelaLoginStatus.account field, so `user` stays null (preserving the analytics
  `user.id` null signal) and plan/balance are surfaced uniformly. SettingsDialog
  / InlineModelSwitcher / AvatarMenu read status.account.{plan,balanceUsd}.

Regression coverage updated in vela.test.ts (applyVelaLiveAccount) and
vela.routes.test.ts (cold-cache first open now asserts body.account).

* style(amr): match the switcher plan text color to the balance

The inline switcher account row showed the balance in --text-strong but
the plan in --text-muted, so "$247.51 Plus" read with two different
weights of grey. Use --text-strong for the plan too so balance + plan
sit at the same emphasis.

* fix(amr): treat tier-less billing summary as free; register new entry sources

Address @nettee review on #4684:

- Free vs unknown: fetchVelaBillingSummary() returned plan:undefined when
  `membershipTier` was absent, but that field is omitted for free accounts. The
  new surfaces read status.account.plan for both display and canUpgradeVelaPlan,
  so a successful free-account read rendered as "unknown" — plan hidden and the
  Upgrade CTA suppressed for the exact users who should see it. A successful
  summary without a tier now normalizes to the explicit 'free' sentinel;
  "unknown" stays signalled by a null account (failed fetch). Regression in
  vela.routes.test.ts (balance, no tier → account.plan === 'free').
- Analytics: the four entry sources this PR adds (settings_amr_upgrade,
  inline_amr_upgrade, avatar_amr_upgrade, avatar_amr_agent_card) were in the
  source→page map but not in the AMR_ENTRY_SOURCES validator set, so
  parseAmrEntryAnalyticsPayload rejected them and /analytics-entry 400ed for the
  new buttons. Added them to the set; parser regression covers all four.

* fix(amr): invalidate the env-backed live-account cache on config/account change

Address @nettee 11:19 review (the inline comment did not post, but the flagged
env-backed live-account cache path has a real leak): readVelaCredentialRevision
forced configMtimeMs to null for env-backed auth, so the live-account cache key
became `env|profile|1||` with nothing to distinguish accounts. Because the live
billing summary is fetched with the config profile's controlKey, a config
rewrite (account switch) left an env-backed (VELA_RUNTIME_KEY) session serving
the previous account's plan/balance. Keep configMtimeMs for env auth too so a
config/account change yields a fresh key. Regression added in vela.test.ts.

* fix(amr): fingerprint the configured AMR env in the live-account cache key

Address @nettee 11:35 review on #4684: configMtimeMs alone wasn't enough. The
AMR env credentials (VELA_RUNTIME_KEY / VELA_LINK_URL) can come from
agentCliEnv.amr in app-config, and env-backed sessions report user:null — so
switching the Settings-backed credential from account A to B on the same
profile, without touching ~/.amr/config.json, left the revision at
`env|profile|1||<same-mtime>` and let B reuse A's cached plan/balance.

readVelaCredentialRevision now adds a non-secret credentialFingerprint (sha256
of the runtime key + link url, truncated) and velaLiveAccountCacheKey includes
it. Regressions: a unit test (env key differs by fingerprint) and a route test
that rewrites agentCliEnv.amr's VELA_RUNTIME_KEY without touching the config and
asserts the second account does not inherit the first's cached billing.

* feat(amr): label + stack balance/plan in the switcher account row; fix its tests

UI: the inline switcher Open Design account row rendered "$247.51 Plus" with no
labels, so users couldn't tell what the two values were. Balance and plan now
stack on two labelled lines ("可用余额 / Available balance" and "当前套餐 /
Current plan"), with the Upgrade CTA on the plan line. The settings.amrBalance /
settings.amrPlan labels are made descriptive across all 19 locales (the settings
card reuses them), so the meaning is explicit everywhere.

Tests: rewrite InlineModelSwitcher.test.tsx for the account-row structure — the
agent radio now carries status only in its aria-label (icon-only visible), and
the sign-in / signing-in / cancel affordances live on the
inline-model-switcher-account-action button. Redundant within(amrButton)
getByText assertions are dropped (the radio-name query already asserts status);
pending/error assertions target the account row. 22/22 green.

* test(amr): align AmrLoginPill tests to the Open Design copy

Account-status group is labelled 'Open Design account status', the console
link is 'Console', and the compact sign-in error reads 'Sign-in failed.' —
update the queries to match. 29/29 green.

* feat(amr): redesign the account row — tier badge + balance subtitle; short labels

Iterated on the Open Design account row in both the inline model switcher and
the runtime-switcher (avatar) list so the plan/balance no longer collide:

- Plan tier renders as a small neutral pill right after the "Open Design" name
  (identity + tier read as one group, à la Claude's "name · Max").
- Balance moves to a second line below the name, labelled "余额 $247.51"
  (small grey label + bold value) so users know what the number is.
- Upgrade stays as the trailing action, vertically centred against the two
  lines. Account logo bumped to 24px to balance the taller row.
- Revert the amrBalance / amrPlan labels back to the short "余额 / 套餐"
  ("Balance" / "Plan") across all 19 locales.

* test(amr): align remaining web specs to the Open Design copy + account row

Update the rest of the web component specs that still asserted the pre-rename
wording / old account-row shape (the `Web workspace tests` red CI @nettee
flagged):

- EntryShell.onboarding: "Sign in to Open Design Cloud" -> "Sign in to Open
  Design"; "AMR sign-in failed." -> "Sign-in failed.".
- SettingsDialog.execution: AMR card queried as /^Open Design\b/ (was
  /^Open Design AMR\b/).
- HandoffButton: agent label "Open Design"; website link "打开 Open Design 官网".
- AvatarMenu: drop the two obsolete avatar console-shortcut specs (the avatar
  console link was removed when the row was consolidated; the upgrade entry
  remains covered by the daemon parser + contract).

Full web suite green: 3403 passed.

* test(amr): cover the avatar-row upgrade CTA (plan/balance + stamped link)

Replaces the two removed avatar console-shortcut specs with focused coverage
for the new runtime-switcher upgrade path (@nettee): mocks a signed-in
/api/integrations/vela/status, opens the menu on the non-prod `test` profile,
asserts the rendered plan badge + balance, and verifies the clicked upgrade
link carries view=plans and od_entry_source=avatar_amr_upgrade attribution.

* test(e2e): update avatar visual spec to the redesigned account row

The Playwright visual (settings-workspace) job was red on a functional
assertion, not a pixel diff: the spec queried the removed
`.avatar-amr-account-link` console entry and 'AMR account' / 'Balance &
recharge' copy. Rewrite it to override the signed-out status with a signed-in
plan/balance, then assert the Open Design row renders the Plus badge + $247.51
and the upgrade link points at ?view=plans.

* chore: re-trigger CI on updated main — needs-validation gate moved to merge_group (#4714)

* test: capture Open Design account balance visuals

* chore: retrigger workspace validation

---------

Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>
Co-authored-by: lefarcen <935902669@qq.com>
Co-authored-by: a1chzt <chizblank@gmail.com>

* [codex] keep AMR wallet balance stable during refresh (#4730)

* fix: keep AMR wallet balance visible during refresh

* fix: remove AMR wallet refresh hover tooltip

* chore: retrigger validation

* fix(ci): align AMR wallet branch validation

* feat(web): Open Design 云账户入口 UI 优化(名牌/置顶/新 logo) (#4787)

* feat(web): polish Open Design cloud account surfaces (plan badge, pin-to-top, new logo)

- 模型切换器(InlineModelSwitcher): Open Design 账号卡片上位为可点选入口并置顶, 移除重复的代理行
- 头像菜单(AvatarMenu): Open Design 置顶代码代理列表; 执行模式选中态改为勾选标记(不再与代理选中态争色块)
- 统一套餐名牌: 新增共享 PlanBadge 组件(全局 CSS, 跟随主题色), 替换设置页/切换器/头像菜单三处各自实现
- 设置页 CLI 卡片: 删除无用的钱包刷新按钮与"更新于…缓存"行; 余额统一为两位小数
- 全局替换 AMR logo 资产(amr.svg)

* test(web): align AgentIcon AMR assertion with new logo fill

---------

Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>

* fix(web): preserve AMR wallet balance in switcher

* fix(amr): avoid stale wallet status on refresh failures

* fix(web): align avatar AMR balance row

* fix(web): avoid stale AMR wallet snapshots

* fix(web): keep avatar AMR console link

* fix(amr): support wallet fallback for older vela cli

---------

Co-authored-by: michaeltaxman911-hue <michaeltaxman911@gmail.com>
Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>
Co-authored-by: lefarcen <935902669@qq.com>
2026-06-30 03:04:52 +00:00
54de349c47 fix: replace expired Discord invite (#4849)
Co-authored-by: koki yanlai xu <koki@kokideMacBook-Air.local>
2026-06-29 15:00:45 +00:00
lefarcenandGitHub 0fecb15baa chore(release): bump main to 0.12.1 ahead of stable 0.12.0 (#4900)
Stable shipped 0.12.0 (tag open-design-v0.12.0, 2026-06-26) but main's
packaged base version stayed at 0.11.1. release-beta's metadata guard
requires the packaged base version to be strictly greater than the latest
stable, so the daily beta build (notify-daily-feishu, 01:00 UTC) has failed
every morning since 2026-06-27 with:

  packaged base version 0.11.1 must be strictly greater than latest stable 0.12.0

Bump all 18 workspace package.json versions in lockstep to 0.12.1 so main
leads stable again. Verified the real prepare-beta guard now resolves
0.12.1-beta.1 (EXIT 0) against the live stable/beta feeds.
2026-06-29 14:47:46 +00:00
lefarcenandGitHub 211ec5df5a ci: make the Nix hash check advisory on PRs, enforce it only at merge time (#4896)
* ci: make the Nix hash check advisory on PRs, enforce it only at merge time

A stale nix/pnpm-deps.nix hash is the routine, self-healing consequence of any
pnpm-lock.yaml change: the nix_validation job already regenerates the hash and
hands it to autofix.atom.yml, which pushes the refresh back to the PR branch.

Failing the job on pull_request turned that transient, auto-fixed drift into a
red check that flipped the PR to BLOCKED and read as a real CI failure to the
review bot — the same deadlock the needs-validation gate already documents. The
contributor then waited on the autofix round-trip every time the lockfile moved.

Now nix_validation stays green on pull_request (the autofix still self-heals the
hash in the background) and the hash is enforced as a hard gate only at merge
time: merge_group and manual full runs fail closed, so a genuinely broken Nix
build can never reach main. The Validate workspace gate ignores a red Nix job on
PRs (both the blanket failure scan and the required-jobs check) and keeps
requiring it everywhere else.

* test(e2e): cover the Nix-advisory-on-PR Validate workspace gate

Extract the two jq programs the `validate` job runs (the blanket failure scan
and the required-jobs scan) straight out of ci.yml and feed representative
`needs` JSON through the real `jq`, so the cross-workflow gate behavior is
asserted rather than reviewed by eye:

- pull_request + failed nix_validation -> gate passes (advisory),
- merge_group / workflow_dispatch full + failed nix_validation -> gate fails
  closed,
- pull_request + another failed required job -> gate still fails (the PR
  exception is scoped to nix_validation only).

A future edit that makes Nix advisory on merge_group, or stops ignoring it on
PRs, now fails this test instead of slipping through YAML review.
2026-06-29 13:33:08 +00:00
AmyandGitHub b55376554b test(e2e): expand onboarding, AMR, automation, and home coverage (#4759)
* test(e2e): cover onboarding and automation flows

* test(e2e): stabilize AMR project runtime P0 group

* test(e2e): cover updated home creation surface

* test(e2e): align onboarding coverage with build step

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)
2026-06-29 03:56:19 +00:00
aad9771a6f fix(e2e): reduce noisy visual regression diffs (#4744)
* fix(e2e): reduce noisy visual regression diffs

* fix(e2e): keep visual captures full viewport

* test(e2e): add entry popover visual targets

* test(e2e): tighten model dropdown visual targets

* Revert "test(e2e): tighten model dropdown visual targets"

This reverts commit 52172f05591e1a5c2bed7b6fc12d05d364d5b27d.

* fix(e2e): relax visual report diff ratio threshold

* fix(e2e): render visual report comparisons horizontally

* fix(e2e): tighten accepted visual diff ratio

* fix(e2e): surface missing visual baselines as new cases

* fix(e2e): render new visual baselines in a grid

* fix(e2e): compact visual report case layout

* fix(e2e): keep large visual diffs surfaced

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)

* fix(e2e): seed privacy consent in project-runtime tests

* fix(e2e): harden project-runtime settings mocks

* fix(e2e): stabilize project-runtime follow-ups

---------

Co-authored-by: mrcfps <mrc@powerformer.com>
2026-06-26 10:30:22 +00:00
fd408c533f [codex] Clean up onboarding brand UI (#4813)
* fix(web): clean release onboarding brand UI

* fix(web): clear stale loading shell after mount

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* test(e2e): align home composer router coverage

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-06-26 09:58:30 +00:00
488e3d6a6d [codex] Refine rich design-system chip previews (#4806)
* fix(web): refine design system chip preview

* fix(e2e): extend home catalog visual capture budget

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(web): restore home ask mode toggle

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(e2e): dismiss consent before settings menu click

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(web): remove Hyundai preset brand

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-06-26 06:37:25 +00:00
PerishFireandGitHub 514ddba8ed ci: consolidate runner selection contract (#4803)
* ci: consolidate runner selection contract

* test: stabilize connector settings privacy state
2026-06-26 04:33:11 +00:00
273bacfc78 [codex] Add design-system tracking analytics (#4783)
* feat(analytics): design-system tracking foundation (project_kind, entry source, brand picker)

Implements the first slices of the design-system tracking spec:

- §1 project_kind: brand-extraction backing projects now report
  project_kind=design_system so DS-project runs (creation + edits)
  drill down cleanly instead of collapsing into 'brand'.
- U3/U4: report design_system_source from the real selection source
  (request->user_selected, plugin->template_inherited, project->
  project_saved, app-default->default) instead of the hard-wired
  unknown/not_applicable; add design_system_kind (official/custom,
  derived from the user:<id> shape) and design_system_slug (official).
- §3.1 create entry source: thread the real entry (onboarding /
  design_systems_page / composer_picker / home_card / library /
  project_canvas) from each navigate() call site into the create
  page_view and create_result, replacing the binary heuristic.
- C7-C9 preset-brand picker: instrument the "Start from a brand"
  trigger click, picker surface_view, and brand_pick with
  preset_brand_category (never the domain).

Contracts: add TrackingDesignSystemKind, TrackingDesignSystemEditSurface,
DesignSystemsPresetBrandPicker{Click,SurfaceView}Props; extend
RunCreatedProps with design_system_kind/slug/edit_surface and both
entry_from enums with composer_picker/project_canvas/library.

Adds docs/design-system-tracking-spec.md.

Verified: contracts build + 170 tests, web typecheck + full suite (3463),
daemon typecheck + run-analytics/brand-extraction tests (70).

* feat(analytics): track AI-optimize (design_system_enrich) click

C13 (tracking spec §3.3): instrument the "AI Optimize" banner CTA on a
programmatically-extracted DS-as-project so the AI-conversion rate
(clicked ÷ programmatic creates) is visible.

Contracts: add design_system_enrich area, DesignSystemEnrichClickProps,
DesignSystemEnrichResultProps, and the design_system_enrich_result event
name (result event reserved for the C14 follow-up).

Adds an implementation-status table to the spec. C14 (enrich_result) and
C15 (ProjectMetadata.enrichmentStatus) remain follow-ups since they need
correlation with the async enrichment run's completion.

Verified: contracts build, web typecheck, brand-enrichment + analytics
tests (89).

* feat(analytics): report all design-system create sources (ds_source_origins)

Comment ②: multi-source creates were flattened to a single `mixed`
design_system_source, hiding which sources combined. Add a comma-joined
`ds_source_origins` (e.g. `source_url,local_code`) on create_result that
lists every source actually used, keeping the singular field for
back-compat. Mirrors the target_platforms/connectors multi-value convention.

Verified: contracts build, web typecheck.

* feat(analytics): distinguish onboarding build-DS vs skip-to-home (C2)

C2 (tracking spec §3.1): the final onboarding step forks into "Build a
design system" and going straight Home, but both reported
completion_type=completed_without_design_system, so the skip rate was
invisible. Parameterize runOnboardingCompletion so the build fork reports
completed_with_design_system and the home forks report
completed_without_design_system. Update the EntryShell onboarding test,
which clicked "Build a design system" yet asserted the without value.

Verified: web typecheck, EntryShell onboarding tests (24).

* feat(analytics): instrument design-system edit lifecycle (E1-E3)

Tracking spec §3.4/§3.6:

- E3 (direct module edits, ui_click area=design_system_edit, all carrying
  edit_surface=direct_module + artifact_kind=design_system + the DS id):
  DesignSystemsTab general ops (edit-with-agent / refresh / download / reset),
  DesignKitView module buttons via the parent handlers (logo/font/image
  upload incl. paste, color edit, logo/image delete, design_md edit-save),
  and the BrandPreviewCard three actions (use-in-chat / open-project / delete).
- E1 (agent-routed edits): a DS-project run editing an EXISTING design system
  now carries edit_surface — comment/mark from their entry_from, otherwise
  chat. First-generation runs get none.

Contracts: DesignSystemEditClickProps (+ union); reuses the
TrackingDesignSystemEditSurface added earlier and RunCreatedProps.edit_surface.

Not wired (enum reserved / no handler today): kit_import, kit_open (external
link in the stateless kit view), design_md_copy/upload.

Verified: contracts build; web + daemon typecheck; DS/Brand/Kit web tests
(89) + daemon run-analytics tests (44).

* feat(analytics): AI-optimize result + enrichment status (C14/C15)

Closes the programmatic-vs-ai_refined comparison gap (tracking spec §6):

- C14: the AI-optimize run is tagged (analyticsHints.dsEnrichment) from the
  ProjectView banner CTA; when it settles, the daemon emits
  design_system_enrich_result (result / design_system_id / project_id /
  run_id / duration_ms / error_code).
- C15: on a successful enrichment run the daemon flags the backing project
  metadata enrichmentStatus='ai_refined' + enrichmentCompletedAt, so the two
  DS cohorts can be split for retention/usage analysis.

Contracts: ProjectMetadata.enrichmentStatus/enrichmentCompletedAt; reuses
the design_system_enrich_result event + DesignSystemEnrichResultProps added
earlier. ProjectChatSendMeta gains dsEnrichment.

Verified: contracts build; web + daemon typecheck; daemon run-analytics +
brand-extraction tests (70); web brand-enrichment tests.

* fix(contracts): declare dsEnrichment hint in ChatAnalyticsHints

The daemon run route reads analyticsHints.dsEnrichment and ProjectView's
AI-optimize path sends it, but the shared ChatAnalyticsHints DTO did not
declare the flag — leaving the web/daemon HTTP shape ahead of the contract
layer so typed ChatRequest callers could not discover or type-check the hint
that drives design_system_enrich_result and the ai_refined metadata stamp.

Declare dsEnrichment?: boolean with analytics-only semantics and add a
request-shape test that compile-fails if the field is dropped again.

Addresses review feedback on PR #4740.

* chore: ignore .playwright-mcp capture output

Local Playwright MCP runs write YAML page snapshots and PNG screenshots to
.playwright-mcp/. These are ad-hoc local visual-check output (including local
UI/session state), not a maintained test fixture, and per the root AGENTS.md
local runtime data must stay out of git. Add the ignore rule so the directory
is never accidentally staged again.

* fix: repair AI optimize enrichment tracking

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix: track design kit module edit clicks

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix: stabilize project P0 catalog loading

Stub unrelated catalog endpoints in the scoped project workspace and runtime Playwright flows so the P0 checks assert their target behavior instead of waiting on large registry responses.\n\nGenerated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix: guard design-system tracking repairs

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

---------

Co-authored-by: free666799 <293857035+free666799@users.noreply.github.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-06-26 02:57:18 +00:00
AmyandGitHub 5333098611 [codex] reduce noisy visual report matches (#4772)
* fix(e2e): reduce noisy visual report matches

* fix(e2e): use normalized visual diff area

Generated-By: looper 0.9.11 (runner=fixer, agent=codex)
2026-06-25 10:10:51 +00:00
8b9b169673 ci: add cost-sensitive runner tiering (#4764)
* test persistent contabo runner poc

* test persistent runner web workspace poc

* test persistent runner local cache setup

* test persistent runner aggressive setup poc

* test persistent runner web postinstall profile

* test restore stable persistent runner poc

* test keep persistent runner web tests default

* test route persistent poc to zxiyun runner

* test relax web vitest timeout on persistent poc

* test harden web vitest cases for persistent runner

* test add runner perf probe

* test isolate runner perf probe dispatch

* test pin runner perf probe node

* test expand runner perf probe matrix

* ci: route heavy jobs through runner modes

* ci: keep web workspace tests on blacksmith

* test: update web runner mode assertion

* ci: route cost-sensitive runner tiers

* ci: move preflight to hosted runner

* ci: downshift blacksmith ui runners to 4vcpu

* test: fix file viewer timeout after merge

* ci: route e2e vitest to hosted default

* ci: centralize runner profile selection

* test: type runner profile assertions

* ci: route e2e vitest to default blacksmith

* ci: remove runner experiment probes

* test: restore postinstall guard coverage

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* test: cover postinstall script contract

* test: move postinstall guard to e2e

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-25 08:55:51 +00:00
lefarcenandGitHub 4c6f6549c6 ci: add landing QA Feishu summary (#4763) 2026-06-25 07:59:45 +00:00
AmyandGitHub a5e2f1bcff test(e2e): add target-level visual baselines (#4765) 2026-06-25 07:10:03 +00:00
lefarcenandGitHub a4dea37a22 fix(ci): harden backport automerge provenance (#4761) 2026-06-25 07:07:04 +00:00
lefarcenandGitHub 60d187b79f feat(ci): auto-merge the rolling plugin-preview manifest PR on green CI (#4727)
* feat(ci): auto-merge the rolling plugin-preview manifest PR on green CI

The post-merge bake opens ONE rolling PR (chore/plugin-previews) whenever a
baked preview changes (spec slice 1). Its diff is pure generated data
(data/plugin-previews/manifest.json; the clips already live on R2), yet someone
still had to merge it by hand — the last manual step the pipeline rework left
behind, and exactly the kind of bot PR that used to pile up.

Two coupled changes:

1. bake-plugin-previews.yml now authors the rolling branch + PR with the
   release-bot App token instead of GITHUB_TOKEN. A GITHUB_TOKEN-authored push
   does not trigger workflows, so the manifest PR never ran `Validate workspace`
   (main's required check) and could not clear the merge queue at all — even
   manually. (PREVIEW_BAKE_TOKEN, the old fallback, was never configured.)

2. bake-plugin-previews-automerge.yml reacts to ci.yml completing for the rolling
   branch and, for a non-draft same-repo PR authored by the release bot with only
   bot commits (pristine) at the exact SHA CI passed, approves it as
   github-actions[bot] (main requires one review; an author cannot approve its own
   PR) and enqueues it with `gh pr merge --auto --squash --match-head-commit`.
   CI failure pings Feishu instead.

Modeled on backport-automerge.yml, adapted for main: main has a merge queue, so
the merge is GitHub-native --auto (enqueue) rather than a direct squash, and
pristine checks the git committer email (the bot email maps to no GitHub user, so
.committer.login is null).

* fix(review): GitHub-backed pristine provenance + correct Feishu sign secret

Address two blocking review findings on the auto-merge path:

1. pristine could be satisfied by spoofed commit metadata. The bake commit was
   authored with a plaintext `git config user.email "bot@open-design.ai"`, and
   the reactor trusted that raw committer email — any collaborator who can push
   to chore/plugin-previews could set the same email and fake pristine=true,
   landing unreviewed commits on main. Commit AS github-actions[bot] (its
   canonical noreply email) and gate on GitHub's resolved `.committer.login`,
   the same GitHub-backed field backport-automerge.yml trusts.

2. the failure Feishu ping signed with secrets.FEISHU_WEBHOOK_SECRET, but every
   other FEISHU_RELEASE_WEBHOOK caller (notify-release-feishu, notify-daily-feishu,
   backport-automerge) signs with FEISHU_RELEASE_SIGN_SECRET; the wrong secret
   resolves empty and the signed release webhook rejects the unsigned request.
   Use FEISHU_RELEASE_SIGN_SECRET and assert the pairing in the topology test.

* test(ci): lock the auto-merge conclusion gates + bake producer invariants

Address nettee's non-blocking coverage follow-up so a regression can't pass the
topology test green:

- Assert the approve and enqueue steps gate on
  `workflow_run.conclusion == 'success'`, and the Feishu step on `== 'failure'`,
  so dropping a conclusion predicate fails the test.
- Add a companion `[P2]` block over bake-plugin-previews.yml asserting the three
  producer invariants the downstream pristine/auto-merge path depends on:
  the create-github-app-token mint, the App-token push URL, and the
  github-actions[bot] commit identity. Regressing the producer back to a
  GITHUB_TOKEN push or a different commit identity now fails here.

* fix(review): bind the Feishu failure alert to the run's exact SHA

A stale failed CI run can finish after the force-pushed rolling branch has
advanced to a newer (green) head. The Feishu step only gated on author/cross +
conclusion==failure, so it would page about a SHA that is no longer the PR head.
Add the same steps.pr.outputs.head_oid == workflow_run.head_sha guard the
approve/enqueue steps use, and lock it in the topology test.

* fix(review): prove bot provenance with GitHub signatures, not git headers

nettee correctly escalated: .committer.login is still a spoofable git-header
identity. A collaborator who can push to chore/plugin-previews can mint a local
commit with the bot's noreply email, GitHub then reports
.committer.login == github-actions[bot], and pristine=true passes — landing
unreviewed human changes on main.

Switch to non-spoofable provenance:

- bake-plugin-previews.yml now creates the manifest commit through the Git Data
  API (blob -> tree -> commit -> ref) with the App token, instead of a local
  `git commit` + push. A commit created through the API is signed by GitHub
  (verification.verified=true) and committed by the App bot (committer.type=Bot).
  Building the tree on the live main HEAD also rebases the rolling branch onto
  the latest main each run.
- the reactor's pristine gate now requires EVERY commit to be both
  verification.verified==true AND committer.type==Bot. A human's locally pushed
  commit is unsigned (verified=false) even with the bot email; a human signing
  with their own key is committer.type=User. Neither can be both, so the pair is
  forge-proof.
- topology test updated to lock the API-commit producer path (and forbid a
  regression to a local push) and the verified+Bot pristine check.

* fix(review): parent the manifest commit on the rendered revision, not live main

The previous change based the Git Data API commit on live main HEAD, but $NEW is
rendered from the job's checkout at start. The bake can run ~90min and main can
advance (unrelated merges) meanwhile, so basing the commit on live main would
publish a manifest from an older checkout on top of newer commits — the rolling
PR would look current while carrying stale preview data and could overwrite
newer plugin-preview changes.

Parent on `git rev-parse HEAD` (the exact revision that produced $NEW) instead,
so GitHub correctly shows the PR as behind once main moves and the next bake
re-renders on the newer HEAD. Topology test locks the rendered-revision base and
forbids regressing to a live-main base.

* fix(review): require the rolling PR's rendered base to equal main before merge

main's merge queue does not require an up-to-date branch, so a bake rendered
against an older main could be re-tested on the latest main and squash-merged
with a stale manifest.json — overwriting a newer manifest that landed meanwhile.
head_oid only proves the PR head is the SHA CI validated, not that it is current.

Add a base-freshness gate: the rolling commit's first parent is the revision the
manifest was rendered against, so require it to equal current main HEAD before
approving/enqueuing. A behind PR is skipped and refreshed by the next bake (the
nightly re-renders on the newer tip even if no plugin changed). Locked in the
topology test.

* fix(review): page Feishu on timed_out bakes too, not just failure

workflow_run has terminal RED states beyond 'failure'. ci.yml defines many
timeout-minutes jobs, so a timed-out run on the rolling manifest PR would block
auto-merge with no Feishu page. Broaden the alert predicate to
failure OR timed_out, and include the conclusion in the message. Deliberately
exclude 'cancelled': a force-push of the rolling branch routinely cancels the
prior in-flight run, so alerting on it would page on every normal refresh. Test
locks the failure+timed_out set and the cancelled exclusion.

* fix(review): gate auto-merge on a manifest-only diff, not commit signatures

nettee is right that the Git Data API does not sign commits — I verified it
empirically: a blob/tree/commit created via POST /git/commits comes back
verification.verified=false / reason=unsigned (and the Contents API is unsigned
too for non-web callers). So the previous verified==true && committer.type==Bot
pristine gate would never pass and the rolling PR could never auto-merge.

There is no per-commit field that is both bot-set and unforgeable without a
signature, so stop trying to prove "the bot authored every commit" and instead
bound what auto-merge can land: require the PR's changed files to be EXACTLY
data/plugin-previews/manifest.json. A human with push access can still push to
the rolling branch, but any commit that touches a code file (or anything but the
manifest) makes the file set != the single manifest path, so pristine=false and
the PR falls back to human review. The worst a forged manifest-only change can do
is point a gallery preview at a bad video URL — generated data the next bake
overwrites — never unreviewed code on main.

With signing no longer relied upon, revert the producer from the Git Data API
dance back to a plain App-token git push (still parented on the checked-out HEAD
per the base-freshness fix, still pushed with the App token so CI runs). Tests
updated to lock the manifest-only file gate and the simplified producer.

* test(ci): update secretless-bake assertion for the App-token producer

Merge main (#4691 landed the read-only pre-merge bake + its topology test). That
test asserted the post-merge workflow contains PREVIEW_BAKE_TOKEN as the
write-capable credential; this PR replaced that never-configured fallback with
the release-bot App, so assert secrets.RELEASE_BOT_APP_ID (and that
PREVIEW_BAKE_TOKEN is gone) instead.
2026-06-25 05:53:01 +00:00
Tom HuangGitHubCursorxne998808-aiPerishCodeopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>lefarcenSiri-Ray
29b138f7a3 feat(brands): turn any brand into a reusable design system (#4691)
* Implement brand management routes and CLI support

- Added `brand-routes.ts` to handle HTTP endpoints for brand operations: listing, extracting, retrieving details, deleting, and serving logos.
- Introduced `brands-cli-help.ts` for CLI commands related to brand management, including usage instructions for listing, creating, retrieving, and deleting brands.
- Updated `cli.ts` to integrate brand commands into the existing CLI structure, allowing headless management of brands via the command line.
- Created supporting files for brand metadata handling, including `design-md.ts` for rendering brand information in markdown format and `index.ts` for the brand engine API.
- Implemented `prefetch.ts` to fetch and process brand material from specified URLs, ensuring a streamlined extraction process.
- Enhanced server setup in `server.ts` to register brand routes and manage brand-related data effectively.

This commit establishes a comprehensive framework for managing brands within the application, facilitating both HTTP and CLI interactions.

* Enhance memory management and onboarding experience

- Introduced canonical profile labels to ensure consistent handling of user input in profile forms, preventing duplicate entries.
- Updated the `parseProfileBody` and `captureProfileFromForm` functions to utilize the new canonical label matching.
- Added a memory callout section in the onboarding view to highlight the benefits of memory usage, including personalized responses and reduced setup questions.
- Implemented new UI elements in the onboarding view to improve user engagement with memory features.
- Expanded i18n support for new onboarding messages related to memory benefits across multiple languages.

* Refactor onboarding flow and enhance design system integration

- Updated the onboarding process to include a new brand extraction step, replacing the previous newsletter step.
- Adjusted the tracking logic to reflect the new onboarding steps, ensuring accurate analytics for user progress.
- Improved the UI for the onboarding view, including new input fields for email collection during the brand extraction phase.
- Refined the EntryShell component to remove outdated comments and clarify the onboarding renderer's purpose.
- Enhanced CSS styles for the onboarding steps to improve layout and user experience.
- Updated internationalization strings across multiple languages to reflect changes in the onboarding flow and brand extraction messaging.

* Add brand management features and enhance font handling

- Introduced new modules for managing brand assets, including `chrome.ts` for headless Chrome operations and `fonts.ts` for self-hosting web fonts.
- Implemented `prefetch.ts` to streamline the brand material extraction process, allowing for efficient harvesting of colors, fonts, and logos.
- Enhanced the brand system with new schema definitions in `schema.ts` to support brand color and font management.
- Developed the `engine` module to integrate brand building and rendering processes, including token derivation and artifact generation.
- Improved the overall structure and organization of brand-related files for better maintainability and scalability.

* Enhance brand extraction and project management features

- Updated `brand-routes.ts` to include new dependencies for project management, allowing for the registration of brand-related projects.
- Modified the `extractBrand` function to support project ID and system files, improving the brand extraction process.
- Enhanced the CLI commands in `cli.ts` to handle project IDs during brand creation, enabling better tracking of brand projects.
- Updated the server setup in `server.ts` to register new project-related routes.
- Improved the UI components to display project information associated with brands, including buttons for opening projects in the `BrandDetailView` and `BrandsTab`.
- Added new metadata fields in the contracts to support project tracking and management for brands.

This commit establishes a more robust framework for managing brand projects, enhancing both backend and frontend functionalities.

* Enhance onboarding profile management and memory persistence

- Added new canonical profile labels for 'Organization size', 'Use cases', and 'Discovery source' to improve user input consistency.
- Introduced `OnboardingProfileState` type to manage onboarding profile data more effectively.
- Implemented functions to build and persist the onboarding profile body to memory, ensuring user selections are saved accurately.
- Updated the `OnboardingView` component to handle profile persistence during navigation and submission steps.
- Enhanced tests to verify that user selections are correctly persisted to the memory profile.

This commit improves the onboarding experience by ensuring that user inputs are consistently captured and stored, enhancing overall user engagement with the application.

* Reflow brand extraction into an agent-driven, live flow

Replace the deterministic SSE prefetch/preview/system pipeline with an
agent-driven extraction: POST /api/brands now reserves the brand and stands
up a backing project with the target site open in an in-app browser tab plus
a seeded prompt, so the agent measures, synthesizes brand.json incrementally,
and the user can clear anti-bot walls by hand. New /preview and /finalize
routes let the agent render the kit page live and register the resulting
user:<id> design system, so extracted brand facts persist as a structured,
reusable brand kit instead of a one-shot deterministic guess.

Adds the brand-extract skill (SKILL.md + brand-kit.html template), kit-render
engine, brand-extraction-engine tests, brand project covers in the Designs
tab, onboarding extract handoff, and the matching od brand extract/preview/
finalize CLI subcommands and contract updates.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Sediment finalized brands into structured memory

Reflow a finalized brand into the memory store (brandToMemoryEntries +
reflowBrandToMemory) so future chats can ground vague requests in the
brand's palette, type, voice and rules. finalizeBrand now wires through
the runtime dataDir and best-effort persists the brand, MemoryChangeEvent
gains a 'brand' source, and the brand kit render hardens its inline JSON
escaping. Adds brand.previewEmpty / brand.viewDetails across all locales.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Implement logo fallback and imagery support in brand extraction

- Introduced a deterministic logo fallback mechanism to ensure that brand extraction processes can retrieve and save site logos, even when the agent fails to do so.
- Enhanced the `startBrandExtraction` and `finalizeBrand` functions to utilize the new logo fallback, allowing for better handling of logo assets.
- Added support for imagery samples in brand validation, enabling the inclusion of representative images in the brand kit.
- Updated the brand kit rendering to include self-hosted fonts and imagery, improving the overall presentation of brand assets.

This commit strengthens the brand extraction workflow by ensuring that logos and imagery are reliably captured and displayed, enhancing the user experience in brand management.

* Enhance memory management with rule proposal and verification features

- Introduced new functionality for distilling annotations into rule proposals, allowing users to suggest rules based on in-canvas annotations through the `od memory rule suggest` command.
- Implemented a verification system that programmatically enforces compliance with active rules during artifact generation, ensuring that all active rules are covered in the self-verify scorecard.
- Added endpoints for managing verification outcomes, including listing, removing, and clearing verification records, enhancing the transparency of the verification process.
- Updated the memory management system to support the retrieval of active rule entries, ensuring that only linked rules are considered during verification.
- Enhanced tests for both rule proposal generation and verification processes to ensure reliability and correctness.

This commit strengthens the memory management capabilities by integrating rule proposals and verification, improving the overall user experience in managing design rules and ensuring compliance.

* Distill review annotations into memory and enforce self-verify scorecard

Add distillAnnotationsToMemory to mine inline preview comments/highlights/
marks into durable feedback + rule memory via a dedicated distiller prompt,
threaded through the existing extract pipeline with an 'annotation' change
source. Tighten the self-verify prompt (daemon + contracts) to state the
daemon programmatically checks the scorecard, so a missing or uncovered
scorecard on an artifact turn is an enforcement failure. Cover the rule
suggest and verification-history routes with tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Apply brand design system through web config on "Use in new chat"

Thread onApplyDesignSystem from the entry shell into BrandsTab so the brand's
registered design system is applied via the web config channel instead of a
bare daemon PATCH that left the Home composer stale. Add a transient
home-intent latch + event so the Brands tab can request the Prototype chip on
the already-mounted HomeView, which consumes it once the plugin catalog loads.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Wire annotation distillation into background memory extraction

Add a background distill pass that mines inline review annotations
(comments / highlights / drawn marks) from a turn into durable memory
alongside the general LLM extraction, surface an `annotation` memory
toast source in the web UI, and cover the flow with a unit test.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix brand design system not applying to composer on "Use in new chat"

Selecting a brand's "Use in new chat" applies the brand's design system as
the default and fires the Prototype chip intent in the same synchronous click
handler. HomeView consumed that intent inside the event listener, so `pickChip`
ran before React committed the config change and seeded the composer's
design-system field from the stale (empty) default — the composer showed
"No design system" instead of the brand until a reload.

Split the intent handling: the listener now only bumps a tick, and a separate
effect consumes the chip after the re-render lands, so the seeded design system
reflects the freshly-applied brand. Add the previously-untracked home-intent
latch test coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(web): rework Brands into Brand Kit and add Home create entry

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(brands): harvest real cover/hero images for the Images module

The brand kit's Images gallery only populated when the extraction agent
remembered to save imagery — so a forgetful or bot-blocked agent (and the
pre-imagery "Open Design" brand) left it empty. Add a deterministic,
server-side imagery fallback (imagery-fallback.ts), mirroring the logo
fallback: it parses og:image/twitter:image, large <img> (highest-res
srcset/<picture>), <link rel=preload as=image>, and CSS background-image
hero blocks, fetches candidates with browser-shaped headers, decodes
PNG/GIF/JPEG/WebP dimensions to keep only big representative images
(dropping icons/sprites/logos/tracking pixels), dedupes by content hash,
and saves up to 8 of the largest into imagery/ with labeled samples.

finalizeBrand runs it as a timeout-bounded, failure-tolerant safety net
(injectable so tests stay offline) when the agent captured too few
samples, first adopting any on-disk images. The extraction prompt and
brand-extract SKILL now explicitly direct the agent to harvest the site's
large/cover/hero images, filtered by rendered size.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(qa): implement deck layout validation and safety checks

Add a new QA module for validating the layout of generated brand decks to ensure robustness against clipping and truncation issues. The `analyseDeckLayout` function checks for critical layout invariants, including the presence of `.slide` sections, correct container types, and necessary runtime layers. Introduce `assertDeckLayoutSafe` to enforce these checks during brand system rebuilds, preventing the deployment of decks that fail validation. Additionally, create comprehensive tests to verify the functionality of the new layout validation features.

* fix(brands): apply deck shrink-to-fit synchronously so slides never clip

The no-clip runtime scheduled its fit pass through requestAnimationFrame,
whose callbacks are throttled while the deck is offscreen or occluded. A
slide could therefore stay unscaled — and clip its content — until first
paint. Fit synchronously on resize/load/fonts-ready with a trailing
setTimeout settle pass for late reflow, removing the rAF dependency.

Verified at the previously-broken 1024x620 viewport: container-type:size,
zero truncations, runtime auto-applies scale (Problem 0.71, ASK 0.87,
Product 0.97, Competition 0.97) and frame clip count drops to 0.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(web): let New Brand modal embed scrollable brand reference picker

Add a fillHeight mode to BrandReferencePicker so the heading, quick-pick
row and controls stay pinned while only the gallery scrolls inside a
bounded-height parent. Wire it into NewBrandModal with a stable, spacious
dialog and refresh the related newBrand/brandPicker copy across all 18
locales.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(brands): enhance brand extraction with deterministic seed harvesting

Introduce a new `seed-fallback` module to provide a server-side deterministic palette and typography seed during brand extraction. This ensures that the brand kit's initial display includes a harvested logo, an approximate color palette, and font families, improving the user experience by reducing the all-skeleton appearance during the first paint. Update the `startBrandExtraction` function to utilize this new module, allowing for a more seamless and visually appealing brand extraction process.

Additionally, enhance the `BrandReferencePicker` component to reflect loading states and errors during brand extraction, ensuring users receive immediate feedback on their actions. Update related tests to verify the idempotency of the `finalizeBrand` function, ensuring that re-finalizing a brand correctly reuses the existing design system without duplication.

* feat(brand-extract): enhance BrandReferencePicker and localization updates

Updated the BrandReferencePicker component to reflect loading states and errors during brand extraction, improving user feedback. Added a new localization key for the brand extraction process and updated existing translations in English, Simplified Chinese, and Traditional Chinese to enhance clarity and user experience. Additionally, introduced new styles for better interaction with brand assets in the brand kit template.

* feat(brands): wire in-page lightbox/masonry/asset preview + refine seed

Brand-kit preview improvements for the live extraction kit:
- brand-kit.html: add in-page overlay system (sandboxed iframe has no
  top-nav) — clickable image lightbox with prev/next, a "view all"
  masonry modal, and a full-page asset preview modal that loads
  system/artifacts/<kind>.html in an iframe. Defer auto-reload while an
  overlay is open so it never yanks the modal out mid-interaction.
- seed-fallback.ts: prefer vivid mid-luminance hues for the seeded
  accent/accent-secondary, and drop icon/symbol faces (Remix Icon etc.)
  from the typography seed so specimens never render glyph soup.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(brands): wire in-page lightbox/masonry/asset preview + refine seed

Brand-kit preview improvements for the live extraction kit:
- brand-kit.html: add in-page overlay system (sandboxed iframe has no
  top-nav) — clickable image lightbox with prev/next, a "view all"
  masonry modal, and a full-page asset preview modal that loads
  system/artifacts/<kind>.html in an iframe. Defer auto-reload while an
  overlay is open so it never yanks the modal out mid-interaction.
- seed-fallback.ts: prefer vivid mid-luminance hues for the seeded
  accent/accent-secondary, and drop icon/symbol faces (Remix Icon etc.)
  from the typography seed so specimens never render glyph soup.

Co-authored-by: Cursor <cursoragent@cursor.com>

* i18n(web): add brandPicker.opening across remaining locales + picker test

Completes the brand-reference picker i18n key that was committed only for
en/zh-CN/zh-TW, so every locale satisfies the typed Dict, and lands the
BrandReferencePicker extraction-feedback test left untracked by the
concurrent worker.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(EntryShell): enhance AMR cloud card visibility post-detection

Updated the EntryShell component to ensure the AMR cloud card remains visible after detection settles, even when the AMR runtime is unavailable. This change prevents the card from disappearing and allows it to degrade gracefully to fallback content and sign-in flow. Additionally, added tests to verify the new behavior, ensuring a better user experience during onboarding.

* feat(library): OD Library asset registry + OD Clipper extension

Add a global, cross-project asset registry (OD Library) and a Chrome MV3
capture extension (OD Clipper), wiring the full HTTP + CLI + Web UI three-track
loop per specs/od-clipper.md.

- contracts: LibraryAsset/Source/Kind, ingest, search, pairing, task DTOs
- daemon: 6 additive SQLite tables, content-addressed owned storage, the
  idempotent registerLibraryAsset hook (hash dedup + append-source),
  programmatic enrichment (mime/size/image dims/domain/tags), pairing tokens
  with a persisted extension-origin allowlist, /api/library/* routes, and
  /api/tools/library/{search,apply} for in-task agent reuse
- cli: `od library list|get|rm|search|import|pair`
- web: Library tab (grid, source badges, filters, search, live SSE updates,
  extension pairing affordance)
- clipper/: standalone MV3 extension (background SW, content toolbar, popup)
- skills/library-curator: utility skill for agent-driven asset reuse

Origin middleware now honors paired chrome-extension:// origins (seeded from
SQLite on boot) and exempts the pairing-confirm handshake. Enrichment AI stages
(caption/OCR/embedding) are recorded as skipped pending a configured model.

* feat(brands): programmatic-first design system extraction + rename

Make brand extraction two-phase so a usable design system is ready the
moment the user enters a URL — the instant "aha" — instead of waiting on
the AI agent:

1. PROGRAMMATIC-FIRST (synchronous): startBrandExtraction now harvests the
   site deterministically (logo, palette, typography, one-line description,
   cover imagery, source URL) via prefetchBrand, synthesizes a valid design
   system with brandFromMaterial (no LLM), and finalizes + registers it
   before returning. finalizeBrand is refactored into a reusable
   finalizeBrandCore shared by both the programmatic path and the agent path.
2. ASYNC AI ENRICHMENT: the seeded agent prompt is reframed to enrich the
   already-usable design system and re-finalize in place (same user:<id>),
   updating every artifact/template.

Bounded + best-effort: a blocked/unreachable origin skips phase 1 and stays
`extracting` for the agent to drive. Gated on userDesignSystemsRoot so the
legacy agent-only path stays intact for tests.

Also rename the user-facing "Brand Kit" surface to "Design System" across
en + zh-CN strings, project names, and the enrichment prompt.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(library): enhance asset import and management features

- Updated the `import` command to allow multiple local files and remote URLs, with restrictions on supported formats.
- Added new commands: `apply` for copying assets into project design files, `edit-as-page` for converting HTML assets into editable projects, and `figma` for exporting Figma captures.
- Introduced sidecar functionality for storing derived data alongside owned assets, including Figma capture IR and element HTML.
- Enhanced server configuration to support larger ingest payloads for asset captures.
- Improved error handling and user feedback during asset import and application processes.

* feat(asset-management): enhance asset dropzone and introduce chat-to-design feature

- Updated the DesignSystemAssetDropzone component to improve file preview handling with new functions for creating and revoking object URLs.
- Adjusted CSS for better layout and spacing in the asset dropzone.
- Added a new "Chat to design" button in the LibrarySection component, allowing users to send selected assets to the Home chat composer for project creation.
- Updated localization strings across multiple languages to reflect changes in asset import terminology.
- Enhanced the HomeView component to handle asset staging from the chat composer.

* feat(library): enhance asset application with element markup support

- Updated the `applyLibraryAsset` function to include an `includeElement` option, allowing the capture of element markup alongside assets.
- Modified related components (e.g., `ChatComposer`, `LibrarySection`, `FileWorkspace`) to handle the new element markup feature, ensuring both asset paths and optional element paths are returned and processed.
- Introduced a new function, `fetchLibraryAssetElementHtml`, to retrieve the captured HTML for element-pick assets.
- Enhanced the UI to display element markup inline within the chat composer, improving user interaction with captured elements.
- Updated API contracts to reflect changes in asset application responses, including optional element markup paths.

* feat(library): enhance asset filtering and preview handling

- Updated the LibraryPicker and LibrarySection components to implement a badge-aware kind filter, allowing for more precise asset filtering based on badge kind.
- Introduced a new `matchesKindFilter` function to streamline the filtering logic across components.
- Enhanced the DesignSystemAssetDropzone to ensure proper handling of image previews, addressing issues with broken thumbnails under React StrictMode.
- Added CSS styles for kind badges to improve asset representation in the UI.
- Implemented tests for the DesignSystemAssetDropzone to ensure correct preview lifecycle management.

* feat(library): hydrate single asset on SSE ingest

Add fetchLibraryAsset(id) so the Library grid can merge just the one
asset an `ingest` SSE event references instead of refetching the whole
list on every capture. Returns null on miss/error.

* feat(clipper): richer in-page image picker

Collect CSS background-image url()s in addition to <img> (so hero/section
art painted as backgrounds is no longer silently missed), defer thumbnail
decode to visible cells via IntersectionObserver, draw downscaled canvas
thumbnails instead of second full-res decodes, and add locate-on-page
highlighting so a picked image can be traced back to its DOM source.

* feat(library): implement lazy loading for thumbnails and enhance asset filtering

- Introduced a `LibraryThumb` component to lazily load heavy content (images, videos, iframes) only when they are near the viewport, improving performance.
- Added a debounced search feature to optimize asset filtering, reducing unnecessary network requests during rapid input.
- Enhanced the asset filtering logic to track active filters using a ref, ensuring efficient updates during live events.
- Updated the `snapshotCardRects` and `cardIdsInBand` functions to support improved hit-testing for drag-and-drop interactions.

* feat(library): lazy picker thumbnails + debounced search

Extend the Library grid's lazy-thumbnail + 250ms debounced-search pattern
to the composer LibraryPicker so opening it no longer fires one full-bytes
request per asset, and tidy the clipper content-script image collection.

* feat(clipper): compress and budget capture inlining

Re-encode large raster images to downscaled WebP and inline smallest-first
within a fixed budget, dropping only the secondary Figma IR past a safe body
size, so an image-heavy page (e.g. a news front page) always saves as an
editable HTML capture instead of 413-failing the ingest.

* test(library): LibraryPicker debounce + lazy-thumbnail coverage

Cover the composer picker's 250ms debounced search and its lazy <img>
mount (deferred until the card is in view), matching the grid's perf test.

* feat(design-system): enhance asset handling and UI for design systems

- Updated the CLI to support additional asset kinds, including 'design-system'.
- Enhanced the DesignSystemProvenance type to include source URLs, improving provenance tracking.
- Modified the design system generation jobs to correctly summarize source links and GitHub repositories.
- Updated UI components to reflect changes in asset handling, including new source link management in the DesignSystemFlow.
- Improved tests to cover new functionality for adding source links and ensuring proper handling of design system assets.

* refactor(library): rename 'design-system' to 'brand kit' and enhance thumbnail loading

- Updated labels and filters in Library components to replace 'design-system' with 'brand kit'.
- Introduced a shimmer skeleton for lazy-loaded thumbnails in the LibraryPicker to improve user experience during asset loading.
- Enhanced the PickerCard component for better performance by memoizing individual asset cards.
- Updated tests to ensure proper handling of brand kit assets and their visibility in the LibraryPicker.

* feat(clipper): implement internationalization for toolbar and popup

- Added i18n support to the clipper, enabling localization of UI elements and tooltips.
- Introduced a new i18n.js file to manage translations for various languages.
- Updated content.js and popup.js to utilize the i18n functions for dynamic text rendering.
- Enhanced accessibility by ensuring aria-labels and tooltips are also localized.
- Improved user experience by providing localized messages for actions and statuses.

* feat(clipper): enhance brand kit extraction and localization support

- Updated the brand kit extraction process to include improved handling of assets and localization for various UI elements.
- Added internationalization support for the brand kit feature, allowing for dynamic text rendering based on user locale.
- Enhanced the user experience by ensuring that all relevant messages and tooltips are localized.
- Updated tests to cover new localization features and ensure proper functionality of the brand kit extraction process.

* feat(clipper): enhance brand color derivation and update localization

- Introduced new functions for color manipulation, including linear interpolation and clamping, to improve brand color derivation.
- Updated the deriveBrandColors function to better map observed palettes to semantic roles, ensuring consistent brand representation.
- Revised localization strings in i18n.js to reflect changes from 'brand kit' to 'design system', enhancing clarity and user experience.
- Improved overall code organization and readability by refactoring existing functions and adding new utility methods.

* refactor(clipper): update terminology from 'brand kit' to 'design system'

- Replaced all instances of 'brand kit' with 'design system' across various components and localization files for consistency.
- Updated UI elements, tooltips, and documentation to reflect the new terminology.
- Enhanced user experience by ensuring clarity in the design system extraction process and related functionalities.
- Adjusted localization strings in multiple languages to align with the updated terminology.

* feat(clipper): enhance image fill handling and normalization

- Introduced functions to normalize image fills by converting non-PNG/JPEG formats (SVG, WebP, GIF, AVIF) to PNG before import, ensuring all images are properly rendered in Figma.
- Updated the UI to report the number of images converted and dropped during the import process, improving user feedback.
- Enhanced the overall image processing workflow to prevent silent failures when unsupported formats are encountered.
- Revised documentation to reflect the new image handling capabilities and supported formats.

* feat(clipper): enhance UI kit and busy state feedback

- Updated the UI kit to include new components such as inputs, selection, and overlays, improving the overall design system representation.
- Enhanced the busy state feedback during capture processes with localized messages and a step-by-step progress indicator, providing users with clearer status updates.
- Revised localization strings to support new UI elements and improve user experience across multiple languages.
- Improved documentation to reflect changes in the UI kit and busy state handling.

* fix(brands): restore design-systems nav entry + reconcile BrandsTab on re-activation

Address review feedback on PR #4260:

1. EntryNavRail dropped the only control that reached view==='design-systems'
   when Brands replaced it in the rail, leaving the still-rendered/routed
   design-system manager deep-link only (the entry-nav-design-systems e2e
   specs assert this). Restore a reachable rail entry (blocks icon, existing
   navDesignSystems key) alongside Brands.

2. BrandsTab only fetched once on mount, but EntryShell keeps sub-views
   mounted and toggles visibility, so a brand finishing extraction in its
   backing project never reconciled until a full reload. Refresh whenever the
   Brands view becomes active again, and poll while any brand is extracting
   (torn down once settled / when hidden).

Red spec: tests/components/BrandsTab.refresh.test.tsx (fails pre-fix:
fetchBrands called once, not twice).

* Update clipper/brand-capture.js

* fix(clipper): improve busy state handling and UI feedback

- Adjusted the spinner CSS to use flex properties for better layout control.
- Enhanced the reclampIfMoved function to preserve user position during busy state transitions.
- Added loading toast notifications for popup-launched captures to ensure progress visibility even when the on-page bar is hidden.

* feat(daemon): add kiwi-schema dependency and enhance Figma API integration

- Added kiwi-schema package to the daemon for improved schema handling.
- Updated FigmaApiNode interface and related functions to support shared functionality with the offline decoder, ensuring consistency in node processing.
- Refactored capture functions in clipper to maintain UI visibility during DOM/IR snapshots, enhancing user experience during capture operations.

* fix(web): surface missing backing projects

* fix(web): re-enable brand actions after use

* fix(daemon): serve brand logos from data roots

* fix(brands): reconcile failed extractions

* feat(daemon): implement offline Figma import and decoding functionality

- Added support for importing `.fig` files directly into the daemon, enabling offline processing without requiring a Figma account.
- Introduced a new `fig-decode.ts` module for decoding `.fig` files, handling both ZIP-wrapped and raw formats.
- Created `figma-import.ts` to orchestrate the import process, generating a canonical snapshot and associated metadata.
- Enhanced the server to handle Figma file uploads and integrate with the new decoding logic.
- Updated package dependencies to include `kiwi-schema`, `html2canvas`, and `jspdf` for improved functionality.
- Added tests for the new Figma import features to ensure reliability and correctness.

* feat(clipper): reload-proof capture progress badge on the extension icon

The on-page progress strip dies if a page reloads itself mid-capture
(aggressive paywall sites like economist.com do this), leaving no
loading signal. Add a per-tab '•••' badge on the extension icon for the
lifetime of any capture message — it lives on the action icon, so a page
navigation can't take it down. Verified end-to-end via a real loaded
extension.

* feat(daemon): add export functionality for Figma and enhance PDF export process

- Introduced `runFigma` command for importing Figma designs, supporting both local `.fig` files and Figma URLs.
- Added detailed usage instructions for the `od figma import` command.
- Implemented `runExport` command for programmatic export of HTML/deck artifacts to PDF, PPTX, or image formats.
- Enhanced error handling and user feedback during export processes.
- Removed obsolete `build-pptx-export-prompt` module and related tests to streamline the codebase.

* feat(daemon): enhance library synchronization and export capabilities

- Implemented `reconcileLibrary` to mirror design systems and agent-produced project deliverables into the Library as referenced assets.
- Added support for programmatic export of artifacts via the `od export` command, including detailed usage instructions.
- Introduced new functions for handling Figma imports and exports, improving integration with design workflows.
- Enhanced error handling and user feedback during synchronization and export processes.
- Added tests for new features to ensure reliability and correctness.

* feat(web): PPTX export for any shareable artifact + Library toolbar tooltips

* chore(nix): refresh pnpm deps hash

* refactor(web): enhance onboarding view and file export progress indicators

- Updated the onboarding view layout for improved accessibility and visual hierarchy, including adjustments to spacing, typography, and button styles.
- Introduced a loading toast for file export progress, displaying elapsed time and estimated time remaining for slide exports.
- Added new translation keys for export progress messages in multiple languages.
- Refactored the export progress handling to provide real-time updates during the export process, improving user feedback and experience.

* refactor(web): streamline export capture bridge and update connector styles

- Removed unused loading logic for html2canvas in the export capture bridge, simplifying the code.
- Updated CSS for the onboarding view connector to improve visual clarity and ensure it does not overlap with node numbers.

* refactor(web): remove html2canvas dependency and enhance Figma URL handling

- Removed the html2canvas package from the project, including its references in the lock file and related components.
- Added functionality to manage Figma URLs within the Design System flow, allowing users to add, remove, and validate Figma file links.
- Improved drag-and-drop handling to prevent unintended file navigation when dropping files outside designated areas.
- Updated UI components to accommodate new Figma URL features, enhancing user experience and accessibility.

* refactor(web): unify brand and design system flows

- Merged the brand extraction process into the design system creation workflow, allowing users to start from a brand directly within the design system wizard.
- Updated routing to redirect legacy brand links to the unified design systems tab.
- Enhanced the onboarding experience by removing the separate Brand Kit tab and integrating brand selection into the design system creation process.
- Improved UI components to reflect these changes, ensuring a seamless user experience across the application.

* feat(web): introduce brand enrichment banner and picker modal

- Added a new BrandEnrichmentBanner component to allow users to refine programmatically-extracted design systems with AI by selecting design-system skills.
- Implemented a BrandPickerModal for selecting brands from a searchable gallery, enhancing the design system creation flow.
- Updated ChatPane to conditionally display the enrichment banner for eligible brand projects, improving user engagement.
- Enhanced the design system flow to support the new brand enrichment features, ensuring a seamless experience for users.

* feat(web): enhance BrandPickerModal and DesignSystemAssetDropzone

- Updated the BrandPickerModal to allow scrolling of the entire picker area, improving user experience by creating a unified scrolling surface.
- Added new props to the BrandReferencePicker for action labels and scroll root reference, enhancing flexibility in brand selection.
- Introduced a new DesignKitView component for rendering design kits consistently across different surfaces.
- Enhanced the DesignSystemAssetDropzone to support a wider variety of file types with appropriate previews, improving asset management during design system creation.
- Updated styles for better visual clarity and responsiveness across components.

* feat(web): update Design Systems tab actions and enhance localization

- Changed the button label in the DesignSystemsTab from "Edit" to "Open" for better clarity in user actions.
- Added a new translation key for 'dsManager.openSystem' across multiple languages to support the updated button label.
- Enhanced the FileWorkspace component to ensure the Design Files tab aligns correctly with the Design System tab, improving UI consistency.
- Implemented a new design system editing feature that allows users to fetch and save design system content from DESIGN.md, enhancing the design workflow.

* fix(merge): repair post-merge regressions after origin/main integration

Follow-up fixes on top of the origin/main merge (886f925cd) addressing
regressions the conflict resolution introduced. main's web suite is the
oracle (100% green); resolution principle was main's engine/backend +
HEAD's UI, unioned.

daemon:
- library-sync.ts: correct design-systems import to ./design-systems/index.js
  (design-systems became a directory module on main).
- tests/server-bootstrap-regression: add LIBRARY_DIR to the PathDeps fixture
  (main-added test x HEAD-added LIBRARY_DIR field).

web:
- WorkspaceTabsBar: union — restore main's onboarding Search-popover close
  behaviour + guards, keep HEAD's library/brands nav entries.
- HomeView: restore main's composer sending-state (await onSubmit, widen its
  return type to Promise<boolean>|boolean|void, pass submitting to HomeHero).
- MemorySection.test: take main's version to match main's two-loop memory
  component.
- i18n: restore dropped settings.onboardingRoleMarketing key across types.ts
  and all locales.
- App/BrandsTab/EntryNavRail/router/home-intent: union fixes restoring main
  features dropped during conflict resolution (needs_input handling, etc.).

Validation: pnpm guard + full pnpm typecheck (all 23 packages) green.
Known-red: EntryShell onboarding step 3 intentionally retains HEAD's "build"
step rather than main's brand-extract step; 8 EntryShell.onboarding /
App.onboarding-amr-e2e tests stay red pending that onboarding decision.

* fix(merge): keep HEAD's unified brand flow (revert main's separate Brands tab)

Follow-up to 688544ff7. Per the chosen product direction (brand creation
unified into the design-system create wizard, not a standalone Brands tab),
revert the brand-flow routing/nav that the post-merge repair had restored
from main:

- router.ts: keep HEAD's brand routing (brands folded into design-systems),
  drop main's standalone /brands and /brands/:id view routing.
- EntryNavRail.tsx: drop main's standalone "Brands" nav button.
- runtime/home-intent.ts: drop main's brand "Use in new chat" confirmation
  notice plumbing (tied to the separate Brands flow).

Kept from the repair commit (additive, non-conflicting): App.tsx
loadedActiveProject correctness, composer Sending… state, WorkspaceTabsBar
onboarding popover behaviour, two-loop memory test, restored i18n keys,
brand needs_input STATUS handling, server.ts plugin-route infrastructure.

* feat(library-ui): implement conditional rendering based on LIBRARY_UI_VISIBLE

- Updated router.ts to conditionally render the library view based on the LIBRARY_UI_VISIBLE flag.
- Modified ComposerPlusMenu.tsx, DesignFilesPanel.tsx, and DesignSystemAssetDropzone.tsx to show the "Select from library" button only when LIBRARY_UI_VISIBLE is true.
- Adjusted EntryNavRail.tsx and EntryShell.tsx to include the library navigation button and section conditionally based on the LIBRARY_UI_VISIBLE state.
- Enhanced HomeHero.tsx to allow starting a blank project directly, improving user experience by providing more options for project creation.

This commit introduces a feature toggle for the library UI, allowing for better control over its visibility during development and testing.

* feat(home-hero): implement edge auto-scroll for horizontal overflow

- Introduced `useEdgeAutoScroll` hook to manage auto-scrolling behavior for horizontally overflowing components in HomeHero.
- Updated `PluginPromptPresets` and `RailGroup` components to utilize the new auto-scroll functionality, enhancing accessibility for users without trackpads.
- Added `EdgeScrollZones` component to provide interactive edge zones for scrolling.
- Enhanced CSS styles to support the new scrolling layout and ensure proper positioning of elements.

This commit improves user experience by making overflow content more accessible and easier to navigate.

* feat(design-systems): add project creation from design system and enhance UI components

- Implemented `handleCreateProjectFromDesignSystem` function in `AppInner` to facilitate project creation directly from a selected design system.
- Updated `DesignKitView` to wrap the iframe in a span for better layout control.
- Refactored CSS for `BrandPreviewCard` and `DesignSystemsTab` to improve styling and responsiveness.
- Introduced a new `TemplatePicker` component in `HomeHero` for selecting project-type templates, enhancing user experience.
- Updated various components to support asynchronous handlers for design system actions, improving overall functionality.

This commit enhances the design system integration and user interface, making project creation more intuitive and accessible.

* feat(brand-routes): enhance brand reservation API and add DESIGN.md support

- Updated the POST /api/brands endpoint to accept optional fields: description and designMd, allowing for more flexible brand reservations.
- Modified validation to require either a URL or designMd for brand extraction.
- Introduced a new design-md-input module to handle parsing and validation of DESIGN.md content.
- Enhanced startBrandExtraction function to support processing of DESIGN.md, improving integration with design systems.
- Added utility functions for managing DESIGN.md input and output, streamlining the brand creation process.

This commit improves the brand extraction workflow by integrating DESIGN.md support, making it easier for users to create and manage brands.

* feat(chat-pane, design-kit-view): enhance chat functionality and design preview features

- Added `handleNextStepPromptAction` to `ChatPane` for setting draft prompts, improving user interaction.
- Introduced `nextStepVariant` to differentiate design system projects in `ChatPane`.
- Updated `DesignKitView` to include a button for previewing design kit covers, enhancing user experience.
- Implemented a modal for displaying design kit previews, allowing users to view content in a dedicated space.

These changes improve the chat interface and design kit interactions, making the application more intuitive and user-friendly.

* feat(brand-extraction): enhance DESIGN.md support and testing

- Added a new test case to validate brand extraction from DESIGN.md input without requiring a website.
- Implemented functionality to register brands directly from DESIGN.md, improving the brand creation workflow.
- Updated the `ChatPane` and `NextStepActions` components to handle design system-specific actions for projects, enhancing user experience.
- Enhanced localization files with new carousel hints and project brief options across multiple languages.

These changes streamline the brand extraction process and improve the overall functionality of the design system integration.

* feat(wireframe-examples): add annotated and greybox wireframe examples

- Introduced new wireframe examples for annotated and greybox styles, enhancing design system capabilities.
- Added HTML and JSON files for both wireframe types, providing templates for low-fidelity design mockups.
- Implemented SKILL.md documentation for each wireframe example, detailing usage and design specifications.

These additions improve the design toolkit, offering users more options for creating wireframes in various styles.

* feat(brand-extraction): refine Chrome fallback and enhance error handling

- Updated the Chrome fallback logic in the prefetch pipeline to clarify its purpose and usage as a diagnostic tool.
- Introduced environment variable checks to enable or disable system Chrome usage, improving control over the extraction process.
- Enhanced error messages in the DesignSystemCreationFlow component to provide clearer guidance on required inputs for creating a design system.
- Added regression tests to ensure that prompts do not instruct the agent to invoke a non-existent `brand-extract` skill, preventing potential failures during brand extraction.

These changes improve the robustness of the brand extraction process and enhance user experience by providing clearer instructions and error handling.

* feat(brand-extraction): enhance DESIGN.md input handling and introduce brand ready prompt

- Updated the BrandFromDesignMdInput interface to explicitly define the description property as optional with undefined.
- Enhanced the brand extraction prompts to clarify the inline brand-extract workflow, preventing confusion during the extraction process.
- Added a new BrandReadyPrompt component to notify users when a design system is ready for preview, improving user experience.
- Introduced CSS styles for the BrandReadyPrompt to ensure a visually appealing and user-friendly interface.
- Updated localization files to support new strings related to the brand ready prompt across multiple languages.

These changes improve the clarity and usability of the brand extraction process, providing users with timely feedback and a more intuitive interface.

* feat(brand-extraction): improve design system focus handling and localization updates

- Refactored the handling of browser tabs in the brand extraction tests to ensure proper validation of tab states.
- Enhanced the AppInner component to refresh design systems alongside templates, ensuring users see the latest updates without page reloads.
- Introduced a pending focus state in the DesignSystemsTab to manage design system selection more effectively after brand extraction.
- Added a BrandReadyPrompt in the ProjectView to notify users when a design system is ready for preview, improving user engagement.
- Updated localization files for Chinese (Simplified and Traditional) to reflect changes in terminology related to design systems.

These changes enhance the user experience by providing timely feedback and ensuring that the design system selection process is seamless and intuitive.

* fix(styles): adjust letter-spacing and enhance plus-menu trigger styles

- Set letter-spacing to 0 in design-system-flow.css for improved text clarity.
- Added styles for plus-menu trigger in plus-menu.css, including background, border, and hover effects to enhance user interaction and visual consistency.

These changes refine the design aesthetics and improve the usability of the plus-menu component.

* feat(tests): add design-system focus handoff tests

- Introduced a new test suite for validating the design-system focus handoff functionality.
- Implemented tests to ensure that the focus ID is correctly set, read, and cleared from session storage, preventing user selection hijacking.
- Added checks for scenarios where no focus ID is pending, enhancing test coverage for the design system's behavior.

These tests ensure the reliability of the design-system focus handling, contributing to a more robust user experience.

* feat(export): restrict image format options to PNG and JPEG

- Updated the image format options in the export functionality to only allow PNG and JPEG, removing WebP to prevent silent downgrades.
- Enhanced error handling to provide clear feedback when an unsupported image format is specified.
- Adjusted related documentation and comments to reflect the changes in supported formats across the application.

These changes ensure consistency in image export behavior and improve user experience by providing immediate validation errors for unsupported formats.

* feat(origin-validation): implement zero-config OD Clipper bypass for library requests

- Added a new function `isZeroConfigClipperLibraryRequest` to validate requests from locally-installed browser extensions targeting the `/library/` path.
- Updated the origin validation middleware to utilize this function, allowing unpaired browser extensions to access the `/api/library/ingest` endpoint while blocking other cross-origin requests.
- Enhanced tests to cover the new bypass functionality, ensuring correct behavior for both valid and invalid origins.

These changes improve the integration of browser extensions with the local daemon, enhancing user experience while maintaining security.

* feat(design-systems): add download functionality for design systems

- Implemented a new command `od design-systems download <id>` to allow users to download design systems as a .zip file, including all system files and a generated SKILLS.md usage guide.
- Updated the CLI help documentation to include usage instructions for the new download command.
- Enhanced the design systems API to support the download feature, ensuring only user design systems are accessible while handling errors for non-existent presets.
- Added localization strings for the new download functionality across multiple languages.

These changes enhance the usability of design systems by providing a straightforward method for users to obtain and share their design assets.

* feat(design-systems): enhance design system management and localization

- Introduced new UI components and styles for managing design systems, including buttons for downloading, refreshing, and resetting edits.
- Updated the DesignKitView to support direct actions for DESIGN.md editing, improving user interaction with design systems.
- Enhanced the DesignSystemDetail component to include download functionality and improved state management for design system edits.
- Added localization strings for new features, ensuring consistent user experience across multiple languages.
- Improved error handling and user feedback for design system operations, including download failures.

These changes streamline the design system management process, making it more intuitive and user-friendly while ensuring robust localization support.

* feat(tests): add comprehensive tests for design system archive functionality

- Introduced a new test suite for validating the `buildUserDesignSystemArchive` and `buildDesignSystemSkillsMarkdown` functions.
- Implemented tests to ensure correct packing of design system files, including the generation of a `SKILLS.md` guide and exclusion of internal metadata.
- Added checks for handling non-user IDs and scenarios where a design system already includes its own `SKILLS.md`.
- Enhanced the overall test coverage for design system functionalities, ensuring reliability and correctness in the design system archive process.

These changes improve the robustness of the design system features by ensuring thorough testing of critical functionalities.

* feat(figma-import): enhance CLI output and add Figma import endpoint

- Updated the CLI to conditionally log detailed import information based on the `--json` flag, improving usability for users who prefer JSON output.
- Introduced a new API endpoint for importing Figma files, handling file uploads and validating project existence, with appropriate error responses for missing files or invalid URLs.
- Added a dedicated route for the Figma import functionality, ensuring seamless integration with existing project workflows.

These changes improve the Figma import experience by providing clearer output options and robust error handling, enhancing overall user interaction with the CLI and API.

* feat(design-files): enhance DesignFilesPanel with new actions and styles

- Added new action buttons for opening a browser and creating a design system in the DesignFilesPanel, improving user interaction in the empty state.
- Updated styles for action buttons to enhance visual distinction and usability.
- Enhanced tests to verify the functionality of new actions in the DesignFilesPanel, ensuring they trigger correctly.

These changes improve the user experience by providing additional functionality and clearer visual cues in the design files interface.

* fix(ci): restore new project modal flow

* fix(ci): align design kit and onboarding checks

* fix(ci): sync bake preview workflow action

* fix(ci): include plugin preview helper scripts

* fix(review): harden brand source and preview flows

* fix(ci): stabilize web workspace tests

* fix(review): address latest blocking feedback

* chore(ci): retrigger validation after label update

* chore: re-trigger CI on updated main — needs-validation gate moved to merge_group (#4714)

* refactor(lightbox): implement portal for overlays to resolve z-index issues

- Updated the lightbox component to use React's createPortal for rendering overlays directly to the <body>, ensuring proper z-index stacking.
- Removed session mode toggle from HomeHero and adjusted related styles and tests accordingly.
- Cleaned up CSS by removing unused styles related to session mode toggle.
- Updated tests to reflect changes in the HomeHero component and its interaction with the design router.

* style(home-hero): remove focus halo from template search input

- Updated CSS to eliminate the global input focus outline and box-shadow for the template search field in the HomeHero component.
- Added a test to verify that the template picker search field maintains a clean appearance when focused.

* feat(design-system): add create design CTA and enhance design kit functionality

- Introduced a new `DesignSystemCreateCta` component to facilitate creating new designs from an active design system, enhancing user experience in the chat interface.
- Updated `ChatPane` to include the new CTA, allowing users to create designs directly from the chat.
- Enhanced `DesignKitView` with sticky header functionality for better accessibility while scrolling.
- Added new CSS styles for the `DesignSystemCreateCta` component to ensure a visually appealing and consistent design.
- Updated internationalization files to include new strings for the design system creation feature.

* feat(upload): enhance file upload handling and error recovery

- Introduced `sanitizePath` to preserve directory structures during file uploads, preventing issues with subdirectory paths.
- Updated `DesignKitView` and related components to utilize the new `sanitizePath` function for improved file name resolution.
- Added `KitErrorBoundary` component to gracefully handle rendering errors in the design kit, providing a user-friendly fallback.
- Implemented internationalization updates for new error messages and action confirmations related to uploads and error handling.
- Enhanced CSS styles for better visual feedback during error states and improved user experience.

* feat(design-kit): add keyboard shortcuts hint and enhance key handling

- Introduced a new keyboard shortcuts hint in the DesignKitView, providing users with quick access to essential actions (E edit, C copy, U upload, R refresh, ⌫ delete logo).
- Implemented a keydown event handler to manage keyboard shortcuts contextually within the design kit, improving user interaction and accessibility.
- Updated CSS for the shortcuts hint to ensure it remains low-contrast until hovered, enhancing the UI experience.
- Added internationalization support for new shortcut labels and hints across multiple languages.
- Adjusted DesignSystemsTab to prefer user logos for their systems, improving visual consistency.

* feat(design-system): introduce DesignSystemExtractionPanel and enhance design system interactions

- Added the `DesignSystemExtractionPanel` component to facilitate user interactions during design system extraction, providing a synthesized conversation view and next steps.
- Updated `ChatPane` to render the new extraction panel when a design system is active, enhancing user guidance.
- Introduced a new utility function `designSystemExtractionSource` to derive human-readable labels for design system sources.
- Enhanced internationalization support with new strings for extraction-related actions and prompts across multiple languages.
- Updated various components and tests to reflect changes in terminology and functionality, improving overall user experience.

* feat(project): add project deletion functionality and enhance design system interactions

- Introduced `onDeleteProject` prop in `ProjectView` to handle project deletion, improving project management capabilities.
- Updated `AppInner` to include the new delete project handler, enhancing user experience in project interactions.
- Enhanced `DesignKitView` and `DesignSystemsTab` with loading states and improved visual feedback during design system resolution.
- Removed deprecated `DesignSystemCreateCta` component and associated styles, streamlining the codebase.
- Updated internationalization files to reflect changes in project management terminology and actions.

* feat(design-kit): enhance internationalization and user feedback in DesignKitView

- Updated various labels and error messages in the DesignKitView to utilize internationalization functions, improving accessibility and user experience.
- Enhanced color input validation messages and added confirmation prompts for design system deletions in DesignSystemsTab and FileWorkspace.
- Introduced new props for handling design system project deletions, streamlining project management.
- Updated internationalization files to reflect new strings and translations for improved user guidance across multiple languages.

* refactor(design-kit): remove keyboard shortcuts hint and streamline header menu

- Eliminated the keyboard shortcuts hint from the DesignKitView, simplifying the header menu.
- Updated the sticky-header overflow menu to exclude upload, full-system preview, and shortcut help actions, focusing on essential project operations.
- Adjusted related tests to reflect the removal of the shortcuts hint and ensure accurate menu item visibility.

* feat(brand-routes): add extract-from-html endpoint for brand extraction

- Introduced a new POST endpoint `/api/brands/:id/extract-from-html` to re-run brand extraction using HTML rendered from the in-app browser after clearing anti-bot walls.
- Implemented error handling for missing HTML and brand not found scenarios.
- Enhanced the `extractBrandFromHtml` function to process the provided HTML and optional CSS, integrating it into the existing brand extraction workflow.
- Updated `prefetch` functionality to support extraction from pre-rendered HTML, improving the overall brand data retrieval process.

* chore(nix): refresh pnpm deps hash

* feat(brand-cli): add extract-from-html command for brand extraction

- Introduced a new CLI command `od brand extract-from-html` to facilitate brand extraction from pre-captured rendered HTML, allowing users to bypass anti-bot walls.
- Enhanced the command to accept optional CSS and base URL parameters, improving flexibility in extraction scenarios.
- Implemented error handling for missing HTML input and invalid brand IDs, ensuring robust user feedback.
- Updated the `BRAND_USAGE` documentation to reflect the new command and its usage details.
- Adjusted server configuration to accommodate larger payloads for the new extraction endpoint.

* feat(design-system): enhance design system extraction and browser tools

- Added a new script to collect CSS styles from rendered pages, improving brand extraction capabilities by capturing computed styles from cross-origin stylesheets.
- Removed the `DesignSystemExtractionPanel` component and its associated styles, streamlining the codebase.
- Updated `ProjectView` and `FileWorkspace` components to enhance design system interactions and improve user experience.
- Introduced new internationalization strings for design system phases and actions, ensuring better user guidance across multiple languages.

* feat(brand-assist): implement browser assist for brand extraction

- Added support for a client-side confirmation mechanism for the brand-browser-assist od-card, allowing users to extract brand information from the unblocked browser DOM.
- Enhanced the `ProjectView`, `ChatPane`, and `AssistantMessage` components to handle the new assist functionality, improving user interaction during brand extraction.
- Introduced new internationalization strings for browser assist prompts and messages, ensuring clarity and guidance across multiple languages.
- Updated the `useBrandReadyPrompt` hook to manage the state of the browser assist, providing a seamless user experience when dealing with anti-bot walls.

* feat(brand-prompt): enhance BrandReadyPrompt with refinement options

- Updated the BrandReadyPrompt component to include options for AI optimization and manual editing, allowing users to refine extracted brand systems.
- Added a new refinement nudge to inform users that automatic extraction may miss details, improving user guidance.
- Adjusted styles for the prompt and dismiss button for better alignment and visual consistency.
- Introduced new internationalization strings for the refinement features, ensuring clarity across multiple languages.
- Removed deprecated PPTX export functionality from the FileViewer component, streamlining the export options.

* refactor(export): remove PPTX export functionality and streamline export options

- Eliminated PPTX export support across various components, including CLI, desktop, and web, to simplify export formats.
- Updated documentation and help messages to reflect the removal of PPTX, ensuring clarity for users.
- Adjusted export-related types and constants to focus on PDF and image formats only, enhancing code maintainability.
- Improved user experience by refining export options and related UI elements.

* refactor(export): remove PPTX references and update export functionality

- Removed all instances of PPTX export functionality from the codebase, including related dependencies and comments.
- Updated export options to focus solely on PDF and image formats, enhancing clarity and maintainability.
- Adjusted UI components and tests to reflect the removal of PPTX, ensuring a streamlined user experience.
- Improved internationalization strings and documentation to align with the new export capabilities.

* chore(nix): refresh pnpm deps hash

* fix(onboarding): preserve selected runtime

* fix(brand): localize generated kit copy

* fix(onboarding): align first-run flow with main

* fix(nav): use palette icon for design systems

* fix(analytics): use design system onboarding step

* fix(ui): remove design system guide toggle

* fix(ui): position design system ready prompt

* fix(ui): space plugin task notice

* fix(web): restore home ask mode and design kit preview

* test(e2e): align onboarding visual capture

* test(e2e): align amr onboarding checks

* fix(brand): remove blocked reference brands

* feat(onboarding): show profile choices as chips

* fix(home): prefer design system cover art in recents

* test(e2e): select onboarding profile chips

* feat(brand-extraction): implement programmatic extraction transcript and UI enhancements for design systems

* feat(brand-extraction): enhance programmatic extraction with transcript agent support and UI improvements

* feat(brand-extraction): add transcript agent resolution and improve message handling in brand extraction

* fix(design-systems): stabilize loading state coverage

* test(e2e): align design system detail visual

* fix(brand-extraction): backfill programmatic transcripts

* fix(web): refresh ready brand design systems

* fix(brands): stabilize extraction handoff and seed colors

* fix(brands): return extraction transcript immediately

* fix(web): open new project modal from entry rail

* fix(editing): expose content edits for plain targets

* feat(file-viewer): implement manual edit draft dirty state tracking and reset logic

* feat(design-system): enhance project creation flow with conversation ID handling

* feat(brands): implement light theme handling for color extraction and seed generation

* feat(brands): add finalizeBrandProject function for brand project completion

* feat(file-workspace): add designSystemBrandId prop and update DesignSystemProjectPanel to use it

* Fix manual editing for brand kits

* fix(design-system): wait for project refreshes

* fix(web): open new project modal from rail

* fix(web): restore home ask mode toggle

* fix(web): sync brand color edits with seeds

* fix(web): stabilize design system workspace tests

* test(tools-pack): relax Windows resource cache timeout

* chore(pr): retrigger review after validation

* fix(web): surface design kit action progress

* fix(web): clarify brand next-step actions

* fix(web): cancel programmatic brand extraction

* fix(web): add design systems tab action feedback

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: xne998808-ai <xne998808@gmail.com>
Co-authored-by: PerishCode <perishcode@gmail.com>
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
Co-authored-by: lefarcen <935902669@qq.com>
Co-authored-by: Siri-Ray <2667192167@qq.com>
2026-06-25 03:56:14 +00:00
295e541085 fix(e2e): stop the packaged Windows onboarding smoke racing the onboarding redirect (#4710)
A fresh install boots at od://app/ and the SPA immediately redirects to the dedicated
onboarding route (od://app/onboarding, since the #4513 cloud sign-in redesign). The healthy
inspect can land just before or just after that redirect, so the reported URL/href is either
one — but the smoke pinned the exact bare root with .toBe('od://app/'), which flaked ~3 of 4
nightly Windows builds when the redirect won the race (AssertionError: expected
'od://app/onboarding' to be 'od://app/').

Match the prefix leniently, exactly as mac.spec.ts and this test's own onboarding-landing
assertion already do. Test-only; no product change.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:18:28 +00:00
254fd7ddf0 fix(ci): only auto-merge a backport that is a pristine bot cherry-pick (#4705)
* fix(ci): only auto-merge a backport that is a pristine bot cherry-pick

The auto-approve/merge premise is that a backport is the untouched cherry-pick of code
already reviewed on main — so it can skip a fresh review. That breaks the moment a human
pushes a commit (resolving a conflict, fixing a failing test): those commits were never
reviewed, yet the existing gates don't catch them — the PR author stays the bot, a draft
gets un-drafted, and head==CI-sha only guards post-green races, not bot-vs-human authorship.

Gate auto-approve + auto-merge on pristine == 'true': every commit on the branch was
committed by github-actions[bot] (what korthout's cherry-pick produces; the human author is
preserved but the committer is the bot). Any human commit -> committer != github-actions[bot]
-> pristine=false -> skip, and the PR falls back to human review (REVIEW_REQUIRED; release-gate
blocks the release until it lands). Fail closed: any lookup miss / empty list -> not pristine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): paginate the pristine-commit check so a >100-commit backport can't pass on its first page

per_page=100 without --paginate only validated the first 100 commits: a human commit at
position 101+ would be missed and the PR would auto-merge unreviewed code (fail-open). Use
--paginate to fold in every commit and fail if any committer is not github-actions[bot];
-F/-x match the literal bot login (the brackets are not a regex class). Fail closed on an
empty list / lookup miss.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): use portable grep -c count for the pristine check, not grep -qv

`! ... | grep -Fqvx` mis-evaluated to pristine=true on a human-touched branch under BSD grep
(grep -q's early-exit status is unreliable across BSD/GNU) — the opposite of safe. Count the
non-bot committer lines with grep -Fvxc, whose count semantics are portable, and require zero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): fail closed when paginated commit lookup partially fails

gh api --paginate can emit several pages and then fail on a later one; `|| true` kept that
partial output, so an all-bot first page could read pristine=true without ever inspecting the
commits on the failed page. Capture gh's exit status with `if !` and blank the list on any
failure, so pristine requires a complete, successful enumeration of every commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(ci): assert the pristine check's actual shape (paginate + grep committer count)

The topology assertion still referenced the old in-jq comparison
'(.committer.login // "") == "github-actions[bot]"', which no longer exists after the check
moved to extracting '.[].committer.login // "none"' and counting non-bot committers with
grep -Fvxc across --paginate'd pages. Assert the shape that exists now.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:34:40 +00:00
cbf7e881fa fix(ci): match the App author login gh actually returns (app/open-design-release-bot) (#4703)
The auto-approve/merge gates checked author == 'open-design-release-bot[bot]', but
`gh pr view --json author --jq .author.login` returns 'app/open-design-release-bot' for an
App-authored PR. The gate never matched, so every clean backport's approve + merge steps were
skipped and the PR sat unmerged (e.g. #4697, #4698 — CI green, no approval). Match the actual
value in all three gates + the topology test.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 13:13:09 +00:00
lefarcenandGitHub a2c294b747 fix(e2e): stub /api/design-systems in /projects UI P0 tests to remove merge-queue flake (#4695)
The /projects view in EntryShell gates its render on
projectsLoading || skillsLoading || designSystemsLoading, and
designSystemsLoading only clears once GET /api/design-systems resolves
(App.tsx dsLoading). Seven tests navigate to /projects without stubbing
/api/design-systems, so their first assertion waits on the real daemon's
response — fast locally but a >10s CenteredLoader under CI load. This is
what dequeued PR #4548 from the merge queue (UI P0 project-workspace,
project-management-flows.test.ts:203 timed out twice on .designs-empty-state).

Add a stubDesignSystemsEmpty helper and call it before each goto('/projects'),
mirroring the existing /api/design-systems stubs used by the design-system
tests in the same file. Pure test-network change; no product behavior touched.
2026-06-23 12:35:49 +00:00
fa852014df feat(ci): auto-approve the release bot's own clean backport to satisfy release/v* review rule (#4682)
release/v* requires one approving review, so backport-automerge could not merge the bot's own
backport PRs (an app can't approve its own PR). Approve as github-actions[bot] — a separate
identity — under the SAME gates as the merge (author == release bot, same-repo, non-draft, head
== the CI SHA, CI green). The diff was already reviewed + approved on main; the backport is a
mechanical, conflict-free cherry-pick, so this auto-approval is scoped strictly to the release
bot's verified backports, then merged with the App token. Topology test updated.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 08:41:22 +00:00
92f219d20b feat(ci): auto-merge clean backports on green CI (+ Feishu ping on failure) (#4671)
* feat(ci): auto-merge clean backports on green CI + Feishu ping on failure

release/v* is PR-protected but has no merge queue and no required status check, so neither
GitHub auto-merge nor enqueuePullRequest applies. backport-automerge.yml reacts to ci.yml
completing for a backport-* branch:
- success + clean (non-draft) PR -> squash-merge with the App token (the push to release/*
  re-triggers notify-release-feishu / the nightly)
- failure -> Feishu ping for manual follow-up
- conflict (draft) -> left for a human; release-gate blocks the release until it lands

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): harden backport auto-merge — verify head identity + bind to CI SHA

Per review (PerishCode):
- Only auto-merge PRs authored by the release bot from the same repo (author ==
  open-design-release-bot[bot] && !isCrossRepository), so a stranger can't get a branch
  named backport-* auto-merged with the App token.
- Bind the merge to the SHA CI actually passed on: gate on head_oid == workflow_run.head_sha
  and pass --match-head-commit, so a commit pushed after CI went green is never merged
  untested (release/v* has no required status checks to catch it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): restrict backport auto-merge to PR CI runs + gate the Feishu path too

Per review (PerishCode):
- Only run for workflow_run.event == 'pull_request' on a backport-* branch, so a push or
  manual dispatch can't reach the App-token / Feishu steps.
- Apply the same author == bot && !isCrossRepository gates to the Feishu failure step, so a
  fork / non-bot PR named backport-* can't spam the release group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(ci): add topology coverage for the backport-automerge trusted workflow

Per review (PerishCode): packaged-smoke-workflow.test.ts registers backport-automerge.yml as a
ci workflow_run consumer and asserts its trust gates — workflow_run.event == pull_request, the
App-token mint, author == bot && !isCrossRepository && non-draft && release/v* base, the
head_oid == workflow_run.head_sha + --match-head-commit binding, and the same identity gates on
the Feishu failure path — so the security posture is covered by tests, not just YAML review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): resolve the backport PR by the CI run's SHA, not just the branch name

Per review (PerishCode): bind the resolved PR to the completed run — select the open PR whose
headRefOid == workflow_run.head_sha (and head branch + release/v* base), so a different/newer PR
on the same branch is never acted on. Topology test asserts the SHA-bound resolution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ci): resolve backport PR from workflow_run.pull_requests, not branch name

Per review (PerishCode): use the run's authoritative pull_requests association as the source of
truth — select the entry whose base is release/* at exactly workflow_run.head_sha — instead of a
gh pr list --head branch-name lookup. Empty for forks (never auto-merged). Topology test updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 07:17:07 +00:00
lefarcenandGitHub e707e017ce test(e2e): match onboarding href prefix in win smoke like mac (#4670)
The win [P0] onboarding smoke pinned `expect(initial.href).toBe('od://app/')`,
but since the #4513 cloud sign-in redesign onboarding lives on a dedicated
route, so the packaged href is `od://app/onboarding`. The mac smoke already
matches the prefix leniently (`/^(od:\/\/app\/|http:\/\/127\.0\.0\.1:\d+\/)/`);
align win with it. This assertion was dead code until the user-data reset fix
(#4665) let the app actually reach onboarding instead of booting to Home, which
is why it only surfaced now on the release nightly.
2026-06-23 06:31:42 +00:00
0bf1b6d6b8 [codex] converge release workflows and stable dry-runs (#4390)
* fix(tools-pack): use junctions for Windows standalone peer deps

* fix(desktop): expose IPC during startup

* fix(tools-pack): preserve Windows inspect diagnostics

* fix(tools-pack): report Windows inspect status errors

* fix(packaged): use Electron net fetch for app protocol

* fix(packaged): load Windows renderer from web sidecar

* fix(desktop): show Windows packaged window during startup

* fix(packaged): disable Windows GPU startup

* fix(tools-pack): keep Windows core smoke observable

* fix(packaged): remove Windows startup probes

* fix(tools-pack): trace Windows desktop IPC status

* fix(tools-pack): add Windows IPC diagnose loop

* fix(release): default beta-s Windows updater feed

* chore: clean merged test eof

* refactor(release): unify prerelease channel model

* chore(release): close prerelease doc escape hatches

* refactor(release): converge release channel workflows

* fix(release): install toolchain in metadata jobs

* fix(release): build release package before contracts

* chore(release): bump development version to 0.10.1

* fix(e2e): seed windows packaged smoke runtime config

* fix(release): install toolchain for metadata publish

* fix(release): materialize betas metadata checkout

* chore(release): bump development version to 0.10.2

* fix(release): allow betas metadata cold start from s3

* fix(e2e): support betas packaged update scenarios

* fix(release): pass betas channel into packaged smoke

* fix(release): set betas channel during self-hosted builds

* fix(release): verify counted channel reservations

* fix(release): use pnpm cmd for betas windows publish

* fix(release): add betas manifest artifact fallback

* fix(release): skip beta-s public metadata fetch

* fix(release): read beta-s manifests from storage

* fix(release): cache beta windows tools-pack builds

* fix(release): inline beta mac tools-pack builds

* fix(pack): deep sign unsigned mac bundles

* docs(pack): document payload-first beta updater validation

* fix(release): align preview tools-pack cache flow

* fix(release): align prerelease tools-pack cache flow

* fix(release): pass github token to prerelease metadata

* fix(release): setup pnpm before feishu notify

* fix(release): add stable dry-run prepublish flow

* fix(release): accept completed prerelease metadata gate

* fix(release): require stable release branches

* fix(release): converge r2 access checks

* fix(updater): use release channel parser for defaults

* fix(updater): harden windows payload relaunch

* fix(release): converge updater smoke fixture contract

* test(e2e): require silent updater fixture output

* fix(release): align stable windows smoke build path

* fix(ci): include release workspace in validation

* fix(ci): repair release validation lanes

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(ci): restore zero-install Feishu notification

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-23 06:13:21 +00:00
lefarcenandGitHub ef006808e7 test(e2e): only swallow ENOENT in win onboarding smoke reset (#4668)
Follow-up to #4665 (nettee review on the backport #4667). The namespace wipe
guarded its readdir with `.catch(() => [])`, which swallowed ANY error — a
real readdir failure (permissions, I/O) would turn the reset into a silent
no-op and let stale state through, defeating the fresh-first-run guarantee the
helper exists to make. Scope the catch to ENOENT only (missing root = already
fresh, nothing to wipe); rethrow anything else so the test fails loudly.
2026-06-23 05:59:14 +00:00
lefarcenandGitHub daa529720d test(e2e): clear Electron user-data on win onboarding smoke reset (#4665)
The [P0] packaged Windows onboarding smoke times out waiting for the fresh
cloud sign-in landing because the app boots straight to Home: a leftover
`onboardingCompleted: true` survives the test's data reset.

Root cause is a data-isolation gap specific to Windows. The packaged web
frontend persists its config — including `onboardingCompleted` — to
localStorage, which Electron stores under the SEPARATE `user-data/` partition,
not the daemon's `data/` dir (the boot log splits `daemonDataRoot` vs
`electronUserDataRoot`). When `<data>/app-config.json` is absent the daemon
omits `onboardingCompleted`, so `mergeDaemonConfig` keeps the localStorage
value; the `true` written by the [P2] smoke that runs first in this file then
boots the app to Home. `resetPackagedRuntimeDataRoot` only wiped `data/`, so
the localStorage flag persisted. The macOS smoke never hit this because it
removes the entire namespace root (app installs to /Applications); on Windows
the install lives under the namespace root, so the reset must preserve only
`install/` while clearing everything else.

Fix: reset every per-namespace runtime directory except `install/`, so
`user-data/` (and any future persisted state) is cleared too, matching the
mac smoke's true-first-run guarantee.
2026-06-23 05:10:43 +00:00
86824a30d8 feat(web): unify task-failure error card across all failure states (#4656)
* feat(web): unify task-failure error card across all failure states

Collapse the three divergent failure UIs (gray .msg.error card, red
.status-pill.is-error pill, accent .amr-card) into one neutral run-error
card with a consistent four-part structure: ① error-type title, ② detail
copy, ③ 1–2 fix CTAs anchored bottom-right, ④ collapsible error-source
(code-line styled, one line collapsed, expand/copy on the source row).

- amr-guidance: add titleKey mapping (code → title i18n); split "sign-in
  required" into AMR (inline AmrLoginPill, single CTA, no promotion) vs
  non-AMR ({agent} copy, retry + use-official-agent) per the PRD.
- ChatPane: rebuild the error card JSX into the unified shell, preserving
  every recovery handler (authorize/recharge/resume/retry/BYOK); interpolate
  {agent} via t(key, {agent}) + agentDisplayName.
- AmrLoginPill: drop the redundant device-code chip (the activation URL
  already carries the code); reword the activation hint.
- i18n: add run-error title/source/sign-in keys across all 19 locales.
- tests: update amr-guidance + AmrLoginPill specs for the new behavior.

* test(e2e): align run error assertions with unified card

---------

Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>
Co-authored-by: Mason <jinmeihong0201@gmail.com>
2026-06-23 04:08:24 +00:00
d3ebed564c [codex] Update Open Design X account links (#4611)
* fix: update Open Design X account links

* test(web): assert settings X link target

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-06-22 09:04:49 +00:00
d19c53d274 Agent-driven brand extraction + two-loop memory overhaul (#4260)
* Implement brand management routes and CLI support

- Added `brand-routes.ts` to handle HTTP endpoints for brand operations: listing, extracting, retrieving details, deleting, and serving logos.
- Introduced `brands-cli-help.ts` for CLI commands related to brand management, including usage instructions for listing, creating, retrieving, and deleting brands.
- Updated `cli.ts` to integrate brand commands into the existing CLI structure, allowing headless management of brands via the command line.
- Created supporting files for brand metadata handling, including `design-md.ts` for rendering brand information in markdown format and `index.ts` for the brand engine API.
- Implemented `prefetch.ts` to fetch and process brand material from specified URLs, ensuring a streamlined extraction process.
- Enhanced server setup in `server.ts` to register brand routes and manage brand-related data effectively.

This commit establishes a comprehensive framework for managing brands within the application, facilitating both HTTP and CLI interactions.

* Enhance memory management and onboarding experience

- Introduced canonical profile labels to ensure consistent handling of user input in profile forms, preventing duplicate entries.
- Updated the `parseProfileBody` and `captureProfileFromForm` functions to utilize the new canonical label matching.
- Added a memory callout section in the onboarding view to highlight the benefits of memory usage, including personalized responses and reduced setup questions.
- Implemented new UI elements in the onboarding view to improve user engagement with memory features.
- Expanded i18n support for new onboarding messages related to memory benefits across multiple languages.

* Refactor onboarding flow and enhance design system integration

- Updated the onboarding process to include a new brand extraction step, replacing the previous newsletter step.
- Adjusted the tracking logic to reflect the new onboarding steps, ensuring accurate analytics for user progress.
- Improved the UI for the onboarding view, including new input fields for email collection during the brand extraction phase.
- Refined the EntryShell component to remove outdated comments and clarify the onboarding renderer's purpose.
- Enhanced CSS styles for the onboarding steps to improve layout and user experience.
- Updated internationalization strings across multiple languages to reflect changes in the onboarding flow and brand extraction messaging.

* Add brand management features and enhance font handling

- Introduced new modules for managing brand assets, including `chrome.ts` for headless Chrome operations and `fonts.ts` for self-hosting web fonts.
- Implemented `prefetch.ts` to streamline the brand material extraction process, allowing for efficient harvesting of colors, fonts, and logos.
- Enhanced the brand system with new schema definitions in `schema.ts` to support brand color and font management.
- Developed the `engine` module to integrate brand building and rendering processes, including token derivation and artifact generation.
- Improved the overall structure and organization of brand-related files for better maintainability and scalability.

* Enhance brand extraction and project management features

- Updated `brand-routes.ts` to include new dependencies for project management, allowing for the registration of brand-related projects.
- Modified the `extractBrand` function to support project ID and system files, improving the brand extraction process.
- Enhanced the CLI commands in `cli.ts` to handle project IDs during brand creation, enabling better tracking of brand projects.
- Updated the server setup in `server.ts` to register new project-related routes.
- Improved the UI components to display project information associated with brands, including buttons for opening projects in the `BrandDetailView` and `BrandsTab`.
- Added new metadata fields in the contracts to support project tracking and management for brands.

This commit establishes a more robust framework for managing brand projects, enhancing both backend and frontend functionalities.

* Enhance onboarding profile management and memory persistence

- Added new canonical profile labels for 'Organization size', 'Use cases', and 'Discovery source' to improve user input consistency.
- Introduced `OnboardingProfileState` type to manage onboarding profile data more effectively.
- Implemented functions to build and persist the onboarding profile body to memory, ensuring user selections are saved accurately.
- Updated the `OnboardingView` component to handle profile persistence during navigation and submission steps.
- Enhanced tests to verify that user selections are correctly persisted to the memory profile.

This commit improves the onboarding experience by ensuring that user inputs are consistently captured and stored, enhancing overall user engagement with the application.

* Reflow brand extraction into an agent-driven, live flow

Replace the deterministic SSE prefetch/preview/system pipeline with an
agent-driven extraction: POST /api/brands now reserves the brand and stands
up a backing project with the target site open in an in-app browser tab plus
a seeded prompt, so the agent measures, synthesizes brand.json incrementally,
and the user can clear anti-bot walls by hand. New /preview and /finalize
routes let the agent render the kit page live and register the resulting
user:<id> design system, so extracted brand facts persist as a structured,
reusable brand kit instead of a one-shot deterministic guess.

Adds the brand-extract skill (SKILL.md + brand-kit.html template), kit-render
engine, brand-extraction-engine tests, brand project covers in the Designs
tab, onboarding extract handoff, and the matching od brand extract/preview/
finalize CLI subcommands and contract updates.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Sediment finalized brands into structured memory

Reflow a finalized brand into the memory store (brandToMemoryEntries +
reflowBrandToMemory) so future chats can ground vague requests in the
brand's palette, type, voice and rules. finalizeBrand now wires through
the runtime dataDir and best-effort persists the brand, MemoryChangeEvent
gains a 'brand' source, and the brand kit render hardens its inline JSON
escaping. Adds brand.previewEmpty / brand.viewDetails across all locales.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Implement logo fallback and imagery support in brand extraction

- Introduced a deterministic logo fallback mechanism to ensure that brand extraction processes can retrieve and save site logos, even when the agent fails to do so.
- Enhanced the `startBrandExtraction` and `finalizeBrand` functions to utilize the new logo fallback, allowing for better handling of logo assets.
- Added support for imagery samples in brand validation, enabling the inclusion of representative images in the brand kit.
- Updated the brand kit rendering to include self-hosted fonts and imagery, improving the overall presentation of brand assets.

This commit strengthens the brand extraction workflow by ensuring that logos and imagery are reliably captured and displayed, enhancing the user experience in brand management.

* Enhance memory management with rule proposal and verification features

- Introduced new functionality for distilling annotations into rule proposals, allowing users to suggest rules based on in-canvas annotations through the `od memory rule suggest` command.
- Implemented a verification system that programmatically enforces compliance with active rules during artifact generation, ensuring that all active rules are covered in the self-verify scorecard.
- Added endpoints for managing verification outcomes, including listing, removing, and clearing verification records, enhancing the transparency of the verification process.
- Updated the memory management system to support the retrieval of active rule entries, ensuring that only linked rules are considered during verification.
- Enhanced tests for both rule proposal generation and verification processes to ensure reliability and correctness.

This commit strengthens the memory management capabilities by integrating rule proposals and verification, improving the overall user experience in managing design rules and ensuring compliance.

* Distill review annotations into memory and enforce self-verify scorecard

Add distillAnnotationsToMemory to mine inline preview comments/highlights/
marks into durable feedback + rule memory via a dedicated distiller prompt,
threaded through the existing extract pipeline with an 'annotation' change
source. Tighten the self-verify prompt (daemon + contracts) to state the
daemon programmatically checks the scorecard, so a missing or uncovered
scorecard on an artifact turn is an enforcement failure. Cover the rule
suggest and verification-history routes with tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Apply brand design system through web config on "Use in new chat"

Thread onApplyDesignSystem from the entry shell into BrandsTab so the brand's
registered design system is applied via the web config channel instead of a
bare daemon PATCH that left the Home composer stale. Add a transient
home-intent latch + event so the Brands tab can request the Prototype chip on
the already-mounted HomeView, which consumes it once the plugin catalog loads.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Wire annotation distillation into background memory extraction

Add a background distill pass that mines inline review annotations
(comments / highlights / drawn marks) from a turn into durable memory
alongside the general LLM extraction, surface an `annotation` memory
toast source in the web UI, and cover the flow with a unit test.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix brand design system not applying to composer on "Use in new chat"

Selecting a brand's "Use in new chat" applies the brand's design system as
the default and fires the Prototype chip intent in the same synchronous click
handler. HomeView consumed that intent inside the event listener, so `pickChip`
ran before React committed the config change and seeded the composer's
design-system field from the stale (empty) default — the composer showed
"No design system" instead of the brand until a reload.

Split the intent handling: the listener now only bumps a tick, and a separate
effect consumes the chip after the re-render lands, so the seeded design system
reflects the freshly-applied brand. Add the previously-untracked home-intent
latch test coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(web): rework Brands into Brand Kit and add Home create entry

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(brands): harvest real cover/hero images for the Images module

The brand kit's Images gallery only populated when the extraction agent
remembered to save imagery — so a forgetful or bot-blocked agent (and the
pre-imagery "Open Design" brand) left it empty. Add a deterministic,
server-side imagery fallback (imagery-fallback.ts), mirroring the logo
fallback: it parses og:image/twitter:image, large <img> (highest-res
srcset/<picture>), <link rel=preload as=image>, and CSS background-image
hero blocks, fetches candidates with browser-shaped headers, decodes
PNG/GIF/JPEG/WebP dimensions to keep only big representative images
(dropping icons/sprites/logos/tracking pixels), dedupes by content hash,
and saves up to 8 of the largest into imagery/ with labeled samples.

finalizeBrand runs it as a timeout-bounded, failure-tolerant safety net
(injectable so tests stay offline) when the agent captured too few
samples, first adopting any on-disk images. The extraction prompt and
brand-extract SKILL now explicitly direct the agent to harvest the site's
large/cover/hero images, filtered by rendered size.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(qa): implement deck layout validation and safety checks

Add a new QA module for validating the layout of generated brand decks to ensure robustness against clipping and truncation issues. The `analyseDeckLayout` function checks for critical layout invariants, including the presence of `.slide` sections, correct container types, and necessary runtime layers. Introduce `assertDeckLayoutSafe` to enforce these checks during brand system rebuilds, preventing the deployment of decks that fail validation. Additionally, create comprehensive tests to verify the functionality of the new layout validation features.

* fix(brands): apply deck shrink-to-fit synchronously so slides never clip

The no-clip runtime scheduled its fit pass through requestAnimationFrame,
whose callbacks are throttled while the deck is offscreen or occluded. A
slide could therefore stay unscaled — and clip its content — until first
paint. Fit synchronously on resize/load/fonts-ready with a trailing
setTimeout settle pass for late reflow, removing the rAF dependency.

Verified at the previously-broken 1024x620 viewport: container-type:size,
zero truncations, runtime auto-applies scale (Problem 0.71, ASK 0.87,
Product 0.97, Competition 0.97) and frame clip count drops to 0.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(web): let New Brand modal embed scrollable brand reference picker

Add a fillHeight mode to BrandReferencePicker so the heading, quick-pick
row and controls stay pinned while only the gallery scrolls inside a
bounded-height parent. Wire it into NewBrandModal with a stable, spacious
dialog and refresh the related newBrand/brandPicker copy across all 18
locales.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(brands): enhance brand extraction with deterministic seed harvesting

Introduce a new `seed-fallback` module to provide a server-side deterministic palette and typography seed during brand extraction. This ensures that the brand kit's initial display includes a harvested logo, an approximate color palette, and font families, improving the user experience by reducing the all-skeleton appearance during the first paint. Update the `startBrandExtraction` function to utilize this new module, allowing for a more seamless and visually appealing brand extraction process.

Additionally, enhance the `BrandReferencePicker` component to reflect loading states and errors during brand extraction, ensuring users receive immediate feedback on their actions. Update related tests to verify the idempotency of the `finalizeBrand` function, ensuring that re-finalizing a brand correctly reuses the existing design system without duplication.

* feat(brand-extract): enhance BrandReferencePicker and localization updates

Updated the BrandReferencePicker component to reflect loading states and errors during brand extraction, improving user feedback. Added a new localization key for the brand extraction process and updated existing translations in English, Simplified Chinese, and Traditional Chinese to enhance clarity and user experience. Additionally, introduced new styles for better interaction with brand assets in the brand kit template.

* feat(brands): wire in-page lightbox/masonry/asset preview + refine seed

Brand-kit preview improvements for the live extraction kit:
- brand-kit.html: add in-page overlay system (sandboxed iframe has no
  top-nav) — clickable image lightbox with prev/next, a "view all"
  masonry modal, and a full-page asset preview modal that loads
  system/artifacts/<kind>.html in an iframe. Defer auto-reload while an
  overlay is open so it never yanks the modal out mid-interaction.
- seed-fallback.ts: prefer vivid mid-luminance hues for the seeded
  accent/accent-secondary, and drop icon/symbol faces (Remix Icon etc.)
  from the typography seed so specimens never render glyph soup.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(brands): wire in-page lightbox/masonry/asset preview + refine seed

Brand-kit preview improvements for the live extraction kit:
- brand-kit.html: add in-page overlay system (sandboxed iframe has no
  top-nav) — clickable image lightbox with prev/next, a "view all"
  masonry modal, and a full-page asset preview modal that loads
  system/artifacts/<kind>.html in an iframe. Defer auto-reload while an
  overlay is open so it never yanks the modal out mid-interaction.
- seed-fallback.ts: prefer vivid mid-luminance hues for the seeded
  accent/accent-secondary, and drop icon/symbol faces (Remix Icon etc.)
  from the typography seed so specimens never render glyph soup.

Co-authored-by: Cursor <cursoragent@cursor.com>

* i18n(web): add brandPicker.opening across remaining locales + picker test

Completes the brand-reference picker i18n key that was committed only for
en/zh-CN/zh-TW, so every locale satisfies the typed Dict, and lands the
BrandReferencePicker extraction-feedback test left untracked by the
concurrent worker.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(EntryShell): enhance AMR cloud card visibility post-detection

Updated the EntryShell component to ensure the AMR cloud card remains visible after detection settles, even when the AMR runtime is unavailable. This change prevents the card from disappearing and allows it to degrade gracefully to fallback content and sign-in flow. Additionally, added tests to verify the new behavior, ensuring a better user experience during onboarding.

* feat(brands): programmatic-first design system extraction + rename

Make brand extraction two-phase so a usable design system is ready the
moment the user enters a URL — the instant "aha" — instead of waiting on
the AI agent:

1. PROGRAMMATIC-FIRST (synchronous): startBrandExtraction now harvests the
   site deterministically (logo, palette, typography, one-line description,
   cover imagery, source URL) via prefetchBrand, synthesizes a valid design
   system with brandFromMaterial (no LLM), and finalizes + registers it
   before returning. finalizeBrand is refactored into a reusable
   finalizeBrandCore shared by both the programmatic path and the agent path.
2. ASYNC AI ENRICHMENT: the seeded agent prompt is reframed to enrich the
   already-usable design system and re-finalize in place (same user:<id>),
   updating every artifact/template.

Bounded + best-effort: a blocked/unreachable origin skips phase 1 and stays
`extracting` for the agent to drive. Gated on userDesignSystemsRoot so the
legacy agent-only path stays intact for tests.

Also rename the user-facing "Brand Kit" surface to "Design System" across
en + zh-CN strings, project names, and the enrichment prompt.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(brands): restore design-systems nav entry + reconcile BrandsTab on re-activation

Address review feedback on PR #4260:

1. EntryNavRail dropped the only control that reached view==='design-systems'
   when Brands replaced it in the rail, leaving the still-rendered/routed
   design-system manager deep-link only (the entry-nav-design-systems e2e
   specs assert this). Restore a reachable rail entry (blocks icon, existing
   navDesignSystems key) alongside Brands.

2. BrandsTab only fetched once on mount, but EntryShell keeps sub-views
   mounted and toggles visibility, so a brand finishing extraction in its
   backing project never reconciled until a full reload. Refresh whenever the
   Brands view becomes active again, and poll while any brand is extracting
   (torn down once settled / when hidden).

Red spec: tests/components/BrandsTab.refresh.test.tsx (fails pre-fix:
fetchBrands called once, not twice).

* fix(web): surface missing backing projects

* fix(web): re-enable brand actions after use

* fix(daemon): serve brand logos from data roots

* fix(brands): reconcile failed extractions

* fix(brands): non-blocking extraction start + lifecycle-accurate status (#4470)

Acceptance fixes for the Brand Kit surface (PR #4260, even-ferry),
addressing five reported issues:

- Perf: `startBrandExtraction` no longer blocks the POST /api/brands
  response on the synchronous programmatic harvest+finalize. It now waits
  only a short budget (~1.2s) so fast origins still land on a ready,
  applyable design system (the instant "aha"), while slow/blocked origins
  return immediately on the "Extracting…" skeleton and finalize in the
  background. Measured: example.com POST dropped from a ~19s block to
  ~1.2s, then finalized to `ready` in the background.

- Brand Kit stays stuck on "Extracting…" after a manual stop: the backing
  run's canceled/failed status now reconciles the brand to `failed` with a
  reason (this extends the existing reconcile; adds a regression spec).

- Brand Kit ignores a backing run that is awaiting user input
  (anti-bot wall / question form): new reversible `needs_input` brand
  status, derived on read (never persisted) so it flips back once the user
  answers. Surfaced as a "Needs input" badge + hint in the card and a
  distinct status dot in the list; the list keeps polling while paused.

- Home → Recent projects showed brand-extraction projects as
  "Not started": /api/projects now falls back to the brand lifecycle for
  brand projects whose run-derived status is `not_started`
  (extracting→running, needs_input→awaiting_input, ready→succeeded,
  failed→failed), so a live extraction is never shown as not started.

- "Use in new chat" produced no visible result: it now carries a
  confirmation notice through the Home intent channel so Home shows a
  "Using <brand>" toast, making the navigate+apply verifiable (the
  composer already preselects the brand's design system).

Tests: daemon brand-routes (canceled→failed, needs_input transient) and
brand-extraction-engine (fast return + background finalize) specs; web
BrandPreviewCard specs (needs_input badge/hint, Use-in-new-chat notice).
#4 validated end-to-end against a real daemon. guard + typecheck green.

* [codex] Fix brand extraction incomplete states (#4486)

* fix brand extraction incomplete states

* fix web memory tests for advanced modal

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix onboarding P0 brand-step navigation

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix daemon brand extraction test timeout

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix brand extraction fallback prompt state

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>

* [codex] Fix brand extraction terminal status reconciliation (#4532)

* Fix brand extraction terminal status reconciliation

* Keep prefetched brand pages previewable

* fix(brands): preserve ready extraction status

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(brands): persist terminal extraction signal

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(brands): clear stale terminal retry markers

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>

* Fix brand kit modal intent handling (#4528)

Co-authored-by: Siri-Ray <2667192167@qq.com>

* fix(web): let design system contents scroll in narrow review layout (#4526)

Co-authored-by: Siri-Ray <2667192167@qq.com>

* [codex] Fix Brand Kit preview lifecycle guards (#4527)

* Fix brand kit preview lifecycle guards

* fix(brands): keep detail selected on delete failure

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

* fix(brands): handle delete HTTP failures

Generated-By: looper 0.9.10+codex.autoclean (runner=fixer, agent=codex)

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>

* ci: retrigger pull_request checks

* test(e2e): stabilize entry settings shard

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: PerishCode <perishcode@gmail.com>
Co-authored-by: lefarcen <935902669@qq.com>
Co-authored-by: PerishFire <39043006+PerishCode@users.noreply.github.com>
Co-authored-by: Siri-Ray <2667192167@qq.com>
Co-authored-by: Looper <looper@noreply.github.com>
2026-06-22 08:34:05 +00:00