Commit Graph
306 Commits
Author SHA1 Message Date
4ee0cf8efd feat(landing): human-in-the-loop tutorial sync + contribute CTA (#4038)
* feat(landing): human-in-the-loop tutorial sync + contribute CTA

Two tutorials-page improvements:

1. Move the daily YouTube sync to a human review step in Feishu. The cron no
   longer generates entries or opens PRs on its own; notify-candidates.ts posts
   a numbered candidate digest (after dedupe + the LLM relevance gate) to a
   Feishu webhook, a maintainer replies which to publish, and
   generate-selected.ts turns the approved ids into entries for a PR. Splits the
   old fetch script into youtube.ts (API client) + notify/generate entries.

2. Add a 'Contribute a tutorial' CTA to the tutorials index that opens GitHub's
   create-file UI prefilled with a frontmatter template, so anyone can propose a
   tutorial via pull request. New i18n keys (en + zh + zh-tw; others fall back
   to en).

* feat(landing): friendlier tutorial contribution via issue form, surfaced higher

Review feedback on the contribute entry point:
- Submitting via a prefilled markdown file was intimidating. Add a GitHub issue
  form (tutorial-submission.yml) — paste a video link, pick a category, done —
  as the primary path; keep the prefilled-PR link as a secondary option for
  contributors who prefer a pull request.
- Move the CTA from the very bottom to directly under the masthead and restyle
  it as a compact, coral-accented bar so it is seen without scrolling past the
  whole list.

Also address PR review:
- notify-candidates: treat Feishu's legacy non-zero StatusCode (returned on an
  HTTP 200) as a failure, not just the newer code field.
- generate-selected: report selected ids that were not generated (unparseable
  args, videos not found on YouTube, write failures) and exit non-zero, so an
  approved selection can't be silently dropped.

---------

Co-authored-by: Joey <276262049+xne998808-ai@users.noreply.github.com>
2026-06-10 06:13:21 +00:00
58e5de2453 ci(release): allow manual nightly+Feishu dispatch off any ref (#4031)
Co-authored-by: audit <a@b.c>
2026-06-10 03:06:27 +00:00
879f34e2af feat(landing): backfill community youtube tutorials + daily sync workflow (#4013)
* feat(landing): backfill community youtube tutorials and add daily sync workflow

Adds 26 community YouTube tutorials about Open Design to the landing page
tutorials catalogue, and a scheduled workflow that keeps it current.

The catalogue had stalled at 12 entries (newest 2026-05-17) while community
coverage kept growing. This backfills 26 vetted entries (English, Chinese,
Portuguese, French, Russian, etc.) and adds tooling so it self-maintains:

- scripts/youtube-tutorials/lib.ts: shared core — an LLM relevance gate that
  rejects lookalike products (OpenCode, OpenClaude, a separate Open Codesign
  repo, generic AI roundups) plus LLM copy generation matching the existing
  editorial voice, written in each video's own language.
- scripts/youtube-tutorials/fetch-youtube-tutorials.ts: daily cron entry,
  queries the YouTube Data API v3.
- scripts/youtube-tutorials/backfill-tutorials.ts: one-off importer used here.
- .github/workflows/tutorials-youtube-sync.yml: daily run that opens a PR for
  maintainer review via open-design-bot; never pushes to main.

* fix(landing): remove dead GitHub source link from tutorial detail pages

The tutorial detail footer linked to the raw .md on GitHub via a 'view source'
link, which leads nowhere useful for readers. Remove the link, its sourceUrl
construction, and the now-unused .tut-source styles. The shared i18n viewSource
key stays — it is still used by the blog detail page.

* fix(landing): fail the youtube sync cron loudly on external errors

Per review: the cron logged search and write failures but exited 0, so an API
outage across all queries looked identical to 'nothing new', and a write/LLM
failure could yield a partial PR or no PR on a green run. Track failures and set
a non-zero exit code when all searches fail or any kept video cannot be written,
mirroring the backfill script's exit 2.

---------

Co-authored-by: Joey <276262049+xne998808-ai@users.noreply.github.com>
2026-06-10 02:21:12 +00:00
31cf0e0f0a fix(plugins-home): deck slide-tour bakes, CJK font fix, framing + reveal height (#4020)
* fix(plugins-home): anchor baked preset previews to the hero top

The example-prompt preset tiles render wider than the gallery's 1.31:1 baked
clip, so object-fit:cover was cropping the hero headline out of the vertical
middle. Anchor the preset video/poster to the top so the hero stays in frame.
Scoped to the preset cards; the Community grid (which matches the clip aspect)
is untouched.

* fix(plugins-home): bake decks as slide tours, fix CJK font tofu, unclip reveal

Three gallery-preview fixes found reviewing the baked Community gallery:

- Decks (fixed-viewport PPT/slideshow pages driven by arrow/wheel, not scroll)
  were vertically panned like tall landing pages — capturing ~20s of a dead
  WebGL background at the wrong 1.31 aspect (the page's compact responsive
  variant, hero headline gone). The bake now detects them (page no taller than
  the viewport), re-renders at 16:9, and walks their slides (arrow keys, falling
  back to wheel then a "next" control, detected by a structural slide signal)
  for a slide-tour the gallery loops on hover. BAKE_VERSION -> 2 re-bakes all.
- CJK-heavy templates baked tofu boxes: they pull Noto Serif/Sans SC from Google
  Fonts with display=swap and the CI runner had no CJK fallback. The bake job now
  installs fonts-noto-cjk + emoji, and the bake double-awaits fonts.ready around a
  force-load so a late-registered display face isn't captured as its fallback.
- The Home templates reveal clipped its last rows: a fixed max-height:6000px
  ceiling + overflow:hidden, but the gallery is ~7300px and grows. Switched to the
  repo's canonical grid-template-rows 0fr->1fr auto-height pattern (inner wrapper
  owns the overflow) so it expands to the gallery's natural height.

* fix(bake): probe-detect deck nav + capture at the 1.31 tile aspect

Refines the deck path after live review:

- Detection was scrollHeight-based, so a deck whose DOM stacks slides
  vertically got misread as a scroll page and vertically panned. Now PROBE the
  navigation: press the arrow key, then nudge the wheel, and use whichever
  actually moves a slide (deckSignal ignores the WebGL background); only a page
  that neither navigates nor fits the viewport keeps the linear pan.
- Captured decks at 16:9, which object-fit:cover then side-cropped in the 1.31
  tile (hero edges cut). Decks collapse to a compact variant at the normal 1440
  width, NOT at the 1.31 aspect — so capture at 1.31 (1760x1344, wide enough to
  clear the breakpoint): full layout AND a clip that fills the tile, no crop.

---------

Co-authored-by: audit <a@b.c>
2026-06-09 19:04:46 +00:00
c2d16727cd feat(plugins-home): content-hashed preview filenames + immutable CDN caching (#4007)
Baked clips are served from R2 behind Cloudflare's CDN (cf-cache-status: HIT).
Previously a re-bake overwrote the same key (example-x.mp4), so the edge kept
serving the stale clip until its TTL expired. Name each clip
example-x.<contentHash>.mp4 instead: a content change ships a NEW URL the
manifest points at, the old edge entry is simply no longer referenced, and the
upload sets Cache-Control: public, max-age=31536000, immutable so the CDN can
cache each clip forever.

Co-authored-by: audit <a@b.c>
2026-06-09 14:12:05 +00:00
d0f350e825 feat(plugins-home): pre-baked hover-pan preview clips for the gallery (#3994)
* feat(plugins-home): pre-baked hover-pan preview clips for the gallery

The Community gallery renders every html plugin as a live, scaled
example.html iframe that animates + auto-pans on hover. That is
GPU-expensive at scale (each tile is its own out-of-process document
re-compositing a tall page) and renders inconsistently for tricky pages
(WebGL noise, video backgrounds, lazy content).

Pre-render each preview to a tiny H.264 clip + first-frame poster:

- scripts/bake-plugin-previews.mjs: headless-Chrome screencast of a
  [hold@top in-place animation][linear pan top->bottom] capture, waiting
  on fonts + <img> + CSS background-images + <video> backgrounds first,
  then ffmpeg -> CFR H.264 mp4 + poster.jpg + manifest.json. Velocity is
  pre-computed from page height so the pan always finishes within ~10s.
  Runtime deps (puppeteer-core / Chrome / ffmpeg) stay out of package.json
  and are provided by the CI environment.
- daemon: serves <out> at /api/plugin-previews and attaches the clip to a
  plugin record under od.bakedPreview (a SEPARATE field — the detail modal
  still reads od.preview and opens the live, interactive page).
- web: inferPluginPreview(record, { preferBaked: true }) lets gallery tiles
  opt into the clip; MediaSurface loops the in-place [0, holdMs] span while
  idle and plays the pan on hover, one always-mounted <video> (no black
  flash), looped frame-accurately via requestVideoFrameCallback, with no
  native controls. Plugins without a bake keep the live-iframe fallback.

CI upload to R2 + the post-merge/nightly bake workflow (with content-hash
skip) and the daemon on-demand path land in follow-ups.

* feat(plugins-home): content-hash skip so unchanged plugins reuse their baked clip

The bake now hashes each plugin's preview HTML + a BAKE_VERSION and stores it
in the manifest. Re-running skips any plugin whose hash is unchanged (no render,
no re-encode, and the CI step re-uploads nothing) — editing the page or bumping
BAKE_VERSION invalidates it. Verified: a second pass over already-baked plugins
reuses all of them in ~1s instead of re-rendering.

* feat(plugins-home): point baked-preview URLs at R2 in production

bakedPreviewBlock now builds its poster/video URLs from
OD_PLUGIN_PREVIEWS_BASE_URL (the R2 public origin) when set, falling back to
the daemon's own /api/plugin-previews static route for local dev. The CI bake
uploads the clips to R2 and the deployed daemon points at them there.

* feat(plugins-home): CI workflow to bake + publish plugin previews

Adds .github/workflows/bake-plugin-previews.yml: post-merge (paths:
plugins/_official) + nightly + manual. Each run starts the daemon, bakes ALL
plugins (the content-hash skip makes that cheap — only changed pages re-render,
PREVIEW_REMOTE trusts the manifest hash since CI clips live on R2 not on disk),
`aws s3 cp`s the new clips to R2 (no --delete, so untouched clips stay), and
commits the refreshed manifest back to main.

- The daemon now reads the checked-in manifest from data/plugin-previews/ by
  default (binaries stay on R2; OD_PLUGIN_PREVIEWS_DIR still overrides locally),
  seeded here with an empty manifest so every plugin starts on the live-iframe
  fallback until the first bake lands.

Verification needs a real CI run (R2 secrets + a daemon in CI); the bake script,
hash skip, daemon injection, and web display are all already verified locally.

* ci(plugin-previews): open a reviewed PR for the manifest instead of pushing to main

Protected main can't take a direct push, and the manifest is version-pinned
(ships with the build), so the bake now opens a PR with the refreshed
data/plugin-previews/manifest.json and requests review from @lefarcen rather
than committing straight to main. Only fires when a plugin actually changed.

* ci(plugin-previews): fix invalid YAML — single-line PR body (@ in body broke the literal block)

* ci(plugin-previews): TEMP branch trigger + debug guards (limit 3, skip publish off-main)

* ci(plugin-previews): install puppeteer-core via pnpm (npm chokes on workspace:*)

* ci(plugin-previews): correct R2 secrets (repository-assets bucket) + TEMP branch publish test

* ci(plugin-previews): revert temp branch-debug toggles (workflow verified in CI)

* fix(plugins-home): gate bakedPreview on a fetchable source + fix workflow shellcheck

Review feedback (nettee):
- bakedPreviewBlock now only attaches a baked preview when a remote origin
  (OD_PLUGIN_PREVIEWS_BASE_URL) is set OR the clip files exist on disk. A
  deployment reading the checked-in manifest without the base URL set would
  otherwise emit /api/plugin-previews URLs that 404 (binaries live on R2),
  breaking tiles instead of falling back to the live iframe.
- workflow: `for _` instead of unused `for i` (SC2034) and split the CHROME
  declare/export (SC2155) so the actionlint gate passes.

* fix(plugins-home): log manifest load failures instead of swallowing them

Review feedback (nettee, non-blocking): loadManifest() caught every read/parse
error and returned {}, so a malformed manifest would silently disable all baked
previews with no trace. Warn so it's diagnosable.

* feat(plugins-home): use baked previews on the example-prompt preset tiles too

The HomeHero '示例提示词' preset tiles render the same plugin previews via
PreviewSurface; pass preferBaked so they get the cheap poster + hover-pan clip
instead of a live iframe, matching the gallery.

* ci(plugin-previews): grant pull-requests: write so the manifest PR step can open its PR

Review feedback (nettee): the permissions block only set contents: write, so
pull-requests defaulted to none and gh pr create would 403 on the first run that
changes the manifest.

---------

Co-authored-by: audit <a@b.c>
2026-06-09 13:14:54 +00:00
28f5516697 feat(prompt-templates): add desk hologram AR realdesk Seedance 2.0 video template (#3967)
* feat(prompt-templates): add desk hologram AR realdesk Seedance 2.0 video template

Ship the two-phase keyframe + i2v workflow behind blue-warrior-realdesk-t2v-15s
as a first-party video prompt template with preview MP4/poster and plugin bundle.

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

* fix(prompt-templates): host desk-hologram previews on repo-assets CDN

Drop the 8.9 MB tracked MP4 to satisfy the 1 MiB blob guard and point
previewImageUrl/previewVideoUrl at repo-assets.open-design.ai, matching
the Three Kingdoms Seedance templates. Bump bundledPreinstallCount to 402.

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

* ci: add workflow to publish prompt-template previews to repo-assets R2

Maintainer workflow_dispatch helper for template PRs that drop large MP4s
from git and point preview URLs at repo-assets.open-design.ai.

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

* ci: auto-publish desk-hologram template previews on workflow push

Run the repository-assets upload when this workflow lands on the
template branch so PR #3967 preview URLs go live without waiting for main.

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

* ci: keep repository-template asset publish as workflow_dispatch only

Assets for PR #3967 are live on repo-assets; drop the one-shot branch push
trigger now that the upload succeeded.

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

* fix(plugins): add specVersion to desk-hologram video template manifest

Align open-design.json with the official video-template plugin schema.

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

---------

Co-authored-by: Tuola Ge <gexingli@refly.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 12:16:49 +00:00
PerishFireandGitHub f9433a7a07 release: harden packaged launcher validation (#3995)
* Unify release metadata publishing

* Allow empty release asset suffix

* Harden beta-s release publishing

* Clean beta Windows release namespace

* Clean Windows launcher namespace

* Retry transient mac notarization uploads

* Control mac notarization upload mode

* Notarize release mac DMGs after build

* Harden release mac DMG notarization retries

* Make beta-s mac updates payload-first

* Bake mac updater metadata URL

* Add unsafe DMG install helper

* Fix beta-s feed and About version

* Improve release validation observability

* Build launcher proto before packaging

* Notarize release-stable mac artifacts

* Enable mac notarization in tools-pack

* test: update packaged release workflow assertions
2026-06-09 11:50:01 +00:00
AmyandGitHub 90817ce72c Rebalance Playwright critical gate (#3904)
* Rebalance Playwright critical gate

* Fix staged attachment e2e selectors

* Run critical gate before visual PR checks

* Expand strict PR visual coverage

* Widen visual harness config fixtures

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

* Expand strict visual suite to 35 cases

* Stabilize critical app artifact selectors

* Fix onboarding visual model assertion

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

* Stabilize settings dialog e2e entry

* Reduce UI extended E2E failure fanout

* Handle nested settings menus in e2e helper

* Sync AMR logout status to settings card

* Centralize agents route mocks in E2E

* Fix critical smoke settings grep

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

* Consolidate E2E agents route fixtures

* Stabilize critical project instructions test

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

* Let nested agents mocks handle routed requests

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

* Stabilize project instructions critical gate

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

* Stabilize visual home plugin surfaces

* Upload critical gate artifacts in visual PR job

* Fix visual PR gate reports and fixtures

* Address PR 3904 review paths

* Localize project instructions control
2026-06-09 10:07:37 +00:00
lefarcenandGitHub fbf1f3c392 ci(feishu): route daily build notification to the shared release Feishu bot (#3597) 2026-06-09 01:48:13 +00:00
1ead1b3c2f docs(landing-page): Claude Design / Figma alternative posts + 0.9.0 release blog (#3884)
* docs(landing-page): add Claude Design / Figma alternative posts and 0.9.0 release blog

Three blog posts for the landing-page marketing site, all bilingual
(English body + i18n.zh.bodyHtml):

- "The open-source alternative to Claude Design" — SEO reverse post,
  now with study-plate figures and the Chinese translation
- "The open-source alternative to Figma" — new SEO reverse post
- "Open Design 0.9.0: design for everyone" — release blog for the
  install-and-create release, matching the 0.8.0 post format

Adds seven 1024x1024 study-plate images under public/blog (plate-16..22),
a figure/img/figcaption style block in the post template, and list-card
image mappings for the Figma and 0.9.0 posts.

* fix(landing-page): render i18n.zh blog translations instead of falling back to English

The blog index and post templates resolved `i18n[locale][field]` to a string
first, then passed that bare string to explicitLocalizedString. That helper
expects a locale→string map and returns undefined for a plain string on any
non-default locale, so every localized blog title, summary, and body silently
fell back to the English source. Use the already-resolved field directly and
keep the English fallback, which fixes zh (and every other locale) rendering
for all posts that ship an `i18n` block.

* i18n(landing-page): add 17-locale translations for the three new blog posts

Adds full i18n.<locale> frontmatter (title/summary/bodyHtml) in 17 languages
(zh, zh-tw, ja, ko, de, fr, ru, es, pt-br, it, vi, pl, id, nl, ar, tr, uk) for:
- open-source-alternative-to-claude-design
- figma-alternative-open-design
- open-design-0-9-0-design-for-everyone

The Claude Design post is now a clean two-way Claude Design vs Open Design
comparison; the earlier open-codesign competitor reference is fully removed
across the English source and all 17 translations.

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

* perf(landing-page): compress oversized blog plate PNGs under the 1 MiB guard

The six figma-alternative study plates shipped at ~2 MiB each, tripping the
repo blob guard (1 MiB per changed file) and failing the Validate workspace
gate. Requantize them to a 256-colour palette: every plate now lands at
~480-610 KiB with PSNR 40-42 dB (visually indistinguishable for these
editorial illustrations). Filenames are unchanged, so no blog references move.

* fix(landing-page): localize internal links in translated blog bodyHtml

Translated post bodies are authored with root-relative links (href="/blog/...")
that point at the default English routes. Because the route injects bodyHtml
verbatim through <Fragment set:html>, a reader on /zh/blog/... clicking such a
link was bounced back to English, undercutting the localized pages. Rewrite
every internal href through the existing localizedHref helper before set:html;
src (locale-agnostic image assets) is left untouched, and absolute/external/
anchor URLs pass through unchanged.

* fix(ci): skip blog SEO noindex check on staging noindex builds

The landing-page CI builds with OD_LANDING_NOINDEX=1 so the PR-preview mirror
stays out of the search index, then runs the blog SEO linter against that same
out/. Every rendered page therefore carries noindex, which the linter flagged
as an indexability blocker — failing Validate landing page for any PR that adds
or edits a blog post. (The check landed in CI after the last blog PR merged, so
the conflict sat latent until now.) Pass the same flag through to the lint step
and skip the noindex assertion when it is set; production builds leave the flag
unset, where the check stays meaningful.

---------

Co-authored-by: Joey <276262049+xne998808-ai@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:06:44 +00:00
AmyandGitHub ae58382040 test(e2e): stabilize main P0 follow-up alerts (#3844)
* test(e2e): stabilize main P0 follow-up alerts

* ci: cover no-browser Playwright failure alerts

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

* ci: distinguish fallback CI alert fingerprints

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

* test(e2e): tolerate AMR onboarding model defaults
2026-06-08 06:10:04 +00:00
7231c891f8 Allow release-stable preflight dry runs (#3857)
* Allow release stable preflight dry runs

* Fix stable dry-run nightly branch validation

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

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-08 04:46:02 +00:00
PerishFireandGitHub 9612abbdb3 ci: add standalone Nix validation lane (#3851)
* ci: add standalone Nix validation workflow

* ci: clarify standalone Nix hash guidance
2026-06-08 04:16:09 +00:00
7a1ad1a438 feat(packaged): add launcher payload updates (#3812)
* feat(packaged): add launcher payload runtime

* ci: focus launcher payload windows smoke

* ci: publish beta mac x64 launcher payload

* test(e2e): harden payload updater smoke

* fix(pack): inject launch config for installed windows smoke

* fix(daemon): allow packaged payload resource roots

* fix(updater): relaunch after payload update

* test(e2e): refresh windows updater UI status

* test(e2e): ensure windows smoke reaches main shell

* test(e2e): skip onboarding before windows updater smoke

* fix(updater): detach windows payload relaunch helper

* test(e2e): run payload smoke without updater dry run

* fix(desktop): relaunch windows payload updates on quit

* fix(updater): preserve installed payload relaunch target

* fix(updater): require launcher target for payload updates

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

* test(e2e): align mac x64 artifact mode assertion

* test(e2e): canonicalize launcher relaunch root

* fix(updater): validate launcher payload config before activation

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

* fix(updater): require live launcher target for payload updates

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

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-07 07:38:57 +00:00
d4c6bbd9e1 Add Spark self-hosted CI runner workflows (#3750)
* Probe Spark runner CI path

* Add Spark runner setup validation

* Harden Spark setup install probe

* Add Spark runner policy checks

* Add Spark runner unit checks

* Add Spark runner typecheck path

* Add Spark runner daemon checks

* Isolate Codex runtime tests from host PATH

* Isolate headless plugin prompt capture

* Fix headless prompt capture script syntax

* Add Spark runner web checks

* Add Spark runner build checks

* Add Spark runner browser checks

* Compose Spark runner CI gate

* Harden Spark runner gate diagnostics

* Shard Spark runner daemon checks

* Add daemon parallel CI experiment

* Install dependencies for daemon parallel CI

* Use Vitest parallelism for daemon runner checks

* Implement manual hosted backup CI gate

* Prepare pnpm for hosted CI backup

* Use dynamic matrix for hosted CI modes

* Emit searchable CI timing lines

* Fix hosted CI Node version

* Optimize CI runner scheduling

* Stabilize runner CI all topology

* Rebalance runner CI all lanes

* Harden ci-gate run identity checks

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

* Fix CI runner cold-start pnpm bootstrap

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

* Require both ci-gate runs for both-success

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

* Fix CI summary pnpm metadata timing

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

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-05 15:18:43 +00:00
0582749df5 Refactor beta release workflows (#3741)
* Refactor beta release workflows

* Fix beta workflow runner bootstrap

* Fix self-hosted beta release bootstrap

* Use pnpm cmd in self-hosted beta Windows bootstrap

* Pass mac notarization profile to beta builds

* Allow direct Apple notarization credentials

* Fix beta Windows asset handoff

* Skip Windows report zip in beta publish

* Fix beta metadata workflow cleanup

* Stabilize self-hosted beta metadata checkout

* Retry self-hosted beta manifest downloads

* Use release storage for self-hosted manifests

* Isolate self-hosted beta metadata scripts

* Add Windows pack phase diagnostics

* Disable beta self-hosted Windows security checks

* Remove beta Windows security preflight

* Add release-beta-s version reservation

* Fix beta daily workflow caller inputs

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

* Guard beta latest publish and build-only runs

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

* Tighten beta latest publish guards

* Fix beta publish artifact suffix handling

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

* Fix beta metadata suffix and Windows publish target handling

* Fix self-hosted beta release ref and publish guards

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

* Fix beta release commit identity wiring

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

* Fix hosted beta checkout pinning

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

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-05 13:01:54 +00:00
AmyandGitHub 9498e6b9e6 [codex] stabilize main P0 CI alerts (#3740)
* test: stabilize main P0 CI alerts

* fix: address main CI alert review feedback

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

* test: cover manual edit bridge selection

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

* fix: restore project instructions UI coverage

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

* fix: address project instructions and restoration feedback

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

* test: preserve file route active-tab assertions

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)
2026-06-05 11:53:49 +00:00
5bcd7d2c26 fix(release-beta): pass a valid mac sign mode (on -> sign-only) (#3735)
release-beta.yml passed MAC_SIGN_MODE: "on" to build-mac.sh, which only
accepts no | sign-only | notarize and exits with "unsupported MAC_SIGN_MODE:
on". That broke every mac beta built directly from main (the green betas came
from the release-beta-isomorphism self-hosted lane, not main).

Use sign-only: signs the beta with the Apple Developer ID certificate (already
wired via APPLE_SIGNING_CERTIFICATE_*) without notarization. Notarization needs
APPLE_NOTARY_KEYCHAIN_PROFILE / the direct-credential notarize path that lands
with the release-beta-isomorphism mac-lane rework; until then this unblocks
signed mac betas from main.

Co-authored-by: audit <a@b.c>
2026-06-05 09:23:39 +00:00
AmyandGitHub b384c64716 test(e2e): align main P0 UI coverage (#3720)
* test(e2e): align main P0 UI coverage

* test(web): align BYOK model search assertion

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

* fix: address P0 coverage review feedback

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

* fix: restore picker keyboard and recent-project coverage

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

* fix(web): repair design-system picker type checks

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

* ci: classify main CI Feishu alerts

* fix: restore active-tab P0 coverage

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

* ci: add E2E coverage reminder

* ci: run E2E coverage reminder at 18:00

* ci: avoid unsafe E2E reminder pagination exits

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)
2026-06-05 08:47:16 +00:00
AmyandGitHub b9d0f7e966 ci: add Feishu main alert smoke test (#3704) 2026-06-05 04:23:29 +00:00
PerishFireandGitHub 0ce9a19bce Add CI gate workflow placeholders (#3701) 2026-06-05 03:29:25 +00:00
8f6cdaaba1 Harden release-beta-s self-hosted beta lane (#3641)
* Fix Windows smoke launcher payload layout

* Harden release-beta-s runner probe

* Use cmd shell for release-beta-s probe

* Add win beta build script for self-hosted lane

* Keep release-beta-s checkout lightweight

* Use explicit local beta version for release-beta-s

* Install dependencies in beta build script

* Repair Electron dist before Windows beta build

* Add release-beta-s timings and diagnostics

* Polish release-beta-s observability guardrails

* Trace tools-pack cache materialization

* Reuse materialized workspace build cache

* Trace Windows packaging segments

* Trace Windows archive process details

* Reuse installer unpacked materialization

* Preserve reusable Windows unpacked projection

* Add fast smoke telemetry for self-hosted beta

* Cache Windows archive outputs for beta lane

* Tighten fast Windows smoke lane

* Scope Windows beta artifact reporting by target

* Clean stale Windows target artifacts

* Wire Windows Authenticode signing

* Expose self-hosted beta validation modes

* Add core Windows smoke profile

* Decouple core smoke from updater fixture version

* Trace Windows smoke lifecycle steps

* Skip unpacked materialization on NSIS cache hit

* Add Nexu S3 beta publisher

* Harden self-hosted Windows beta lane

* Use Windows PowerShell for publish probe

* Run publish probe via repo script

* Require explicit self-hosted S3 public origin

* Run real installer acceptance for external feeds

* Skip ready-prompt checks for external feeds

* Relax optional self-hosted update inputs

* Tolerate BOM in self-hosted publish probe

* Converge self-hosted beta metadata and signing flow

* Run self-hosted beta metadata prep on the runner lane

* Use Windows PowerShell for self-hosted beta metadata checks

* Bypass execution policy in self-hosted beta metadata checks

* Split self-hosted beta metadata publish

* Suppress mc output in beta publish helpers

* Split Windows NSIS payload cache layers

* Require fresh tool dist metadata

* Preserve Windows overlay payload paths

* Default beta-s gate to core

* Optimize win beta packaging reuse

* simplify windows full smoke lifecycle

* preserve onboarding state after windows upgrade smoke

* fix windows tools-pack test portability

* recover stale tools-pack locks

* Allow auto signing fallback for beta builds

* Add self-hosted beta mac arm64 lane

* Quote self-hosted signing choices

* Run self-hosted beta metadata on mac lane

* Use shallow metadata checkout on self-hosted beta

* Use sparse beta metadata checkout

* Make mac release profile bash-compatible

* Allow file sparse metadata checkout

* Isolate self-hosted metadata checkout

* Clear sparse skip-worktree before mac build

* Isolate self-hosted mac build checkout

* Run self-hosted beta publish without aws cli

* Use unsigned beta prefix for mixed platform publish

* Sparse checkout self-hosted beta publish

* Publish mac beta platform before metadata

* Pass mac publish release profile

* Probe mac signing keychain import

* Expand mac signing preflight variants

* Use sudo keychain helper for self-hosted mac signing

* Route self-hosted mac codesign through sudo wrapper

* Normalize mac signing identity name

* Harden self-hosted mac notarization

* Optimize self-hosted mac release cache

* Fix mac signing helper cleanup

* Retry transient mac notarization uploads

* Keep mac notarization fail-fast

* Reduce mac symlink diagnostics noise

* Normalize self-hosted beta platform inputs

* chore: bump beta base version to 0.9.1

* chore: tune release tools-pack cache retention

* test: add temporary cache validation marker

* chore: key windows nsis base payload by content

* Revert "test: add temporary cache validation marker"

This reverts commit e8487bfd3e.

* chore: probe windows nsis payload cache before materializing

* chore: align self-hosted beta smoke modes

* chore: skip mac smoke report upload when disabled

* chore: default self-hosted beta to publish both platforms

* chore: make beta signing modes explicit

* Add release report artifacts and metatool metadata

* Fix Windows release report zip publishing

* Remove artifact handoff from release beta self-hosted lane

* Fix release beta merge follow-ups

* Fix release beta install regressions

* Restore web build dependencies

* Route Windows beta downloads through mirrors

* Use PowerShell 7 for Windows beta lane

* Fix release beta preflight gates

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

* Fix Windows beta smoke update reuse

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

* Fix release beta review follow-ups

* Refresh Nix pnpm dependency hashes

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

* Fix Windows signtool fallback resolution

* Reject stale beta platform manifests

* Validate beta platform manifest run identity

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

* Validate beta manifest commit identity

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

* Allow beta metadata publish reruns

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

* Build tools-pack fully in beta Windows bootstrap

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

* Emit r2 metadata for Windows beta publish

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

* Stabilize beta publish browser test

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

* Stabilize Windows beta metadata smoke test

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

* Restore multi-platform Vela CLI package

* Refresh Nix pnpm dependency hashes

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

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-05 03:00:55 +00:00
lefarcenandGitHub 668d556097 ci(release): throttle the per-push nightly build instead of debouncing it (#3682)
`notify-release-feishu` builds a full nightly (via release-stable, channel=nightly)
and posts a Feishu card on every push to `release/**`. Its concurrency used
`cancel-in-progress: true`, so a newer push cancelled the in-flight build. When a
release branch is busy — pushes spaced closer than a full cross-platform build
takes — every build was cancelled by the next push and no nightly was ever cut.
(Observed on release/v0.10.0: a run of pushes all cancelled, the last successful
nightly was hours old.)

Switch to `cancel-in-progress: false` (throttle): the running build finishes, and
pushes that arrive during it coalesce — GitHub keeps the in-progress run plus only
the newest pending run, cancelling older pending ones. A busy release branch now
always cuts a nightly and converges on the latest commit, with at most one build
running plus one queued. Trade-off: a newer push no longer interrupts a running
build for an older commit; it waits one build cycle. For nightly that's the right
call.
2026-06-04 15:04:53 +00:00
AmyandGitHub 2afe7c7713 ci(e2e): tune main UI regression workflow alerts (#3601)
* ci(e2e): tune main UI regression workflow alerts

* ci(e2e): shard nightly UI regression runs

* ci(e2e): split P0 UI regression by domain

* ci(e2e): include entry configuration P0 shard

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)
2026-06-04 11:00:22 +00:00
67077fd36f chore(docs): move translated docs into docs/i18n/ (#3621)
* chore(docs): move translated docs into docs/i18n/

Collect the translated README/QUICKSTART/CONTRIBUTING/MAINTAINERS files
(including the Korean set) into docs/i18n/, leaving only the English sources
in the repo root so the GitHub project home page file list stays clean.
Rewrite internal links for the new layout (../../ for repo-root resources,
sibling filenames between translations), update both switcher conventions,
the i18n-check mixed-layout support, the contributors-wall workflow globs,
TRANSLATIONS.md guidance, and drop now-dead root translation paths from the
fork-PR docs allowlist.

* fix(docs): correct root-relative links in Korean contribution guide

Prefix repo-root targets (scripts/sync-design-systems.ts, TRANSLATIONS.md,
package.json, .github/pull_request_template.md) with ../../ so they resolve
from the new docs/i18n/ depth; sibling translated docs stay bare.

Generated-By: looper 0.8.1 (runner=fixer, agent=claude-code)

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
2026-06-04 06:55:16 +00:00
AmyandGitHub 8fefebbbaf test(e2e): add priority tiers and main UI alerts (#3574)
* test(e2e): add priority tiers and stabilize p0 coverage

* test(e2e): align restoration artifact reopen path

* test(e2e): stabilize p1 workspace flows

* ci(e2e): run extended UI on main and notify failures

* fix(e2e): repair priority preflight checks

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

* fix(e2e): restore AMR login pill coverage

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

* fix(e2e): add full UI test script

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

* fix(e2e): remove disallowed ui script

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

* fix(e2e): allow full UI script in guard

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)
2026-06-03 12:08:06 +00:00
lefarcen fa59871428 Merge origin/main into release/v0.9.0 — back-merge to reconcile v0.9.0 with main
Syncs release/v0.9.0 with main so PR #3282 (release/v0.9.0 → main) merges
cleanly. 17 conflicts resolved by commit-time + per-area ownership:

- Comment/edit/preview: keep main (newer). FileViewer combines main's #3438
  (pin manual-edit inspector, no full-height panel) + #3347 deck/queue work
  WITH release's #3401 hover-card anti-flicker grafted on; #2879 Button kept.
- Toolbar: footer layout (reviewer-validated, ca03a7e0f) overrides main #3447 —
  CLI/agent selector in the composer footer, settings gear in the header, no
  settings item in the AvatarMenu dropdown.
- AMR: keep release's client-side model polling (mergeAmrModelsIntoAgents) and
  all release AMR fixes (#3450 sign-out, #3504 console, #3487 acp tool-only
  guard, amrCloudVisible onboarding, #3511 analytics provider id).
- Pet composer button stays removed (release decision); only /pet slash props
  retained.
- Pickers: main's providerModelsCache approach.

Validation: web typecheck + guard + full web suite (2638) + daemon suite green.
2026-06-03 17:21:00 +08:00
Marc ChanGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
b6105a8cab refactor(web): extract shared UI primitives (#2879)
* refactor(web): extract shared UI primitives

* fix(web): include components in packaged builds

* fix(nix): refresh daemon pnpm deps hash

* fix(components): trim unused primitive exports

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)

* refactor(components): split primitives into modules

* fix(web): restore finalize cancel link

Generated-By: looper 0.9.0 (runner=fixer, agent=opencode)

* docs: prefer shared web components

* refactor(web): migrate easy primitives

* fix(web): preserve migrated control styling

* fix(nix): refresh daemon pnpm deps hash

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)

* fix(web): keep Continue in CLI action styling

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)

* fix(web): keep finalize action styling

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)

* chore(nix): refresh pnpm deps hash

* fix(web): preserve MCP row label styling

Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)

* fix(web): resolve ChatComposer merge conflict

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(e2e): stop leaked tools-dev runtime before retry

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* ci: include packages/components in change scopes

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(pack): include components in linux internal packages

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(pack): ship components package in packed installs

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(pack): align win app test with internal packages

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(components): use global visually hidden class

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(components): add development export for source builds

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(components): restore selected custom select state

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(tools-pack): rebuild components for packaged web builds

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* test(tools-pack): cover components workspace artifacts

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

* fix(ci): include components in tools-pack scope

Generated-By: looper 0.9.2 (runner=fixer, agent=opencode)

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-06-03 03:22:29 +00:00
PerishFireandGitHub 98767fb302 chore: move safe large assets to R2 (#3503)
* chore: tighten blob guard to 2MiB

* chore: move safe 1MiB assets to R2
2026-06-02 11:10:07 +00:00
PerishFireandGitHub c3c356961b chore: move large repository assets to r2 (#3492) 2026-06-02 10:19:12 +00:00
lefarcenandGitHub 12a00dbff0 ci(release): split Feishu streams — daily=beta, release-push=nightly (#3474) (#3478)
Follow-up to #3462. Two corrections:
- The per-push release/** stream must build the nightly channel (release-stable),
  not beta. The daily 09:00 stream stays on the lighter beta channel.
- The nightly card was missing the macOS Intel download button; release-stable
  builds mac arm64 + mac Intel (signed) + win unconditionally, so switching that
  stream to it adds the Intel button for free.

Changes:
- release-stable.yml: add workflow_call (channel/nightly_version/ref inputs +
  outputs incl. per-platform download URLs and previous_commit); add ref to every
  checkout; pin GITHUB_SHA to the resolved build HEAD in metadata/publish; capture
  the previous nightly commit before publish for the changelog baseline.
- release-beta.yml: expose mac_arm64_url + win_url as workflow_call outputs (the
  rest of its workflow_call interface from #3462 is unchanged; daily still uses it).
- notify-release-feishu.yml: call release-stable channel=nightly; CHANNEL_LABEL=Nightly.
- notify-daily-feishu.yml: keep calling release-beta; CHANNEL_LABEL=Beta.
- notify.ts: channel-agnostic title/changelog via CHANNEL_LABEL; download buttons
  from explicit per-platform URL outputs (mac_arm64/mac_intel/win/linux) instead of
  re-fetching metadata.json — guarantees the Intel button on nightly.

Ref plumbing mirrors release-beta: scheduled callers run on main but build a
release branch, so checkouts take inputs.ref and metadata/publish pin GITHUB_SHA
so metadata.json (and the next run's changelog baseline) records the real commit.

(cherry picked from commit 2305a35768)
2026-06-02 16:09:07 +08:00
lefarcenandGitHub 2305a35768 ci(release): split Feishu streams — daily=beta, release-push=nightly (#3474)
Follow-up to #3462. Two corrections:
- The per-push release/** stream must build the nightly channel (release-stable),
  not beta. The daily 09:00 stream stays on the lighter beta channel.
- The nightly card was missing the macOS Intel download button; release-stable
  builds mac arm64 + mac Intel (signed) + win unconditionally, so switching that
  stream to it adds the Intel button for free.

Changes:
- release-stable.yml: add workflow_call (channel/nightly_version/ref inputs +
  outputs incl. per-platform download URLs and previous_commit); add ref to every
  checkout; pin GITHUB_SHA to the resolved build HEAD in metadata/publish; capture
  the previous nightly commit before publish for the changelog baseline.
- release-beta.yml: expose mac_arm64_url + win_url as workflow_call outputs (the
  rest of its workflow_call interface from #3462 is unchanged; daily still uses it).
- notify-release-feishu.yml: call release-stable channel=nightly; CHANNEL_LABEL=Nightly.
- notify-daily-feishu.yml: keep calling release-beta; CHANNEL_LABEL=Beta.
- notify.ts: channel-agnostic title/changelog via CHANNEL_LABEL; download buttons
  from explicit per-platform URL outputs (mac_arm64/mac_intel/win/linux) instead of
  re-fetching metadata.json — guarantees the Intel button on nightly.

Ref plumbing mirrors release-beta: scheduled callers run on main but build a
release branch, so checkouts take inputs.ref and metadata/publish pin GITHUB_SHA
so metadata.json (and the next run's changelog baseline) records the real commit.
2026-06-02 07:17:21 +00:00
netteeandGitHub 1695a58166 chore: stop daily metrics PRs (#3463) 2026-06-02 06:06:14 +00:00
lefarcenandGitHub b6d1a3b785 ci(release): build beta + notify Feishu on release push and daily (#3462) (#3469)
Reuse the release-beta pipeline as a workflow_call and add two thin caller
workflows that build a full beta ("nightly") package and post a Feishu card
with download links and the changelog since the previous beta:

- notify-release-feishu: triggers on every push to release/**, debounced
  (concurrency cancel-in-progress), notifies FEISHU_RELEASE_WEBHOOK.
- notify-daily-feishu: cron 01:00 UTC (09:00 Asia/Shanghai) + manual; resolves
  the active release branch (highest-semver release/vX.Y.Z) and builds it via
  the new ref input, notifies FEISHU_DAILY_WEBHOOK.

release-beta gains a workflow_call entry (enable_* booleans + optional ref +
outputs) so callers reuse one build source. Because scheduled callers run on
main but must build a release branch, every checkout takes inputs.ref and the
metadata/publish jobs pin GITHUB_SHA to the resolved build HEAD so the published
metadata.json records the real built commit (and the next run's changelog
baseline stays correct).

The changelog needs zero extra state: the baseline is the previous beta's commit
read from beta/latest/metadata.json before this run overwrites it; download links
come from this build's version metadata.json. notify.ts renders a Feishu
interactive card and supports optional signed bots (FEISHU_*_SIGN_SECRET).

(cherry picked from commit 6ffa75aa5b)
2026-06-02 13:26:22 +08:00
lefarcenandGitHub 6ffa75aa5b ci(release): build beta + notify Feishu on release push and daily (#3462)
Reuse the release-beta pipeline as a workflow_call and add two thin caller
workflows that build a full beta ("nightly") package and post a Feishu card
with download links and the changelog since the previous beta:

- notify-release-feishu: triggers on every push to release/**, debounced
  (concurrency cancel-in-progress), notifies FEISHU_RELEASE_WEBHOOK.
- notify-daily-feishu: cron 01:00 UTC (09:00 Asia/Shanghai) + manual; resolves
  the active release branch (highest-semver release/vX.Y.Z) and builds it via
  the new ref input, notifies FEISHU_DAILY_WEBHOOK.

release-beta gains a workflow_call entry (enable_* booleans + optional ref +
outputs) so callers reuse one build source. Because scheduled callers run on
main but must build a release branch, every checkout takes inputs.ref and the
metadata/publish jobs pin GITHUB_SHA to the resolved build HEAD so the published
metadata.json records the real built commit (and the next run's changelog
baseline stays correct).

The changelog needs zero extra state: the baseline is the previous beta's commit
read from beta/latest/metadata.json before this run overwrites it; download links
come from this build's version metadata.json. notify.ts renders a Feishu
interactive card and supports optional signed bots (FEISHU_*_SIGN_SECRET).
2026-06-02 04:41:50 +00:00
lefarcenGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
5916cfa12d chore(pack): ship AMR on Windows/Intel-mac via vela-cli 0.0.6 + require-vela-cli guard (#3310)
* chore(pack): bump @powerformer/vela-cli to 0.0.6 for multi-platform AMR

vela-cli 0.0.6 publishes per-platform native packages for all five targets
(darwin-arm64/x64, linux-arm64/x64, win32-x64); 0.0.4 only shipped
darwin-arm64. tools-pack bundles the CLI by resolving @powerformer/vela-cli
for the packaging host, so this lets the Windows and Intel-mac packaged apps
bundle Vela/AMR instead of silently shipping without it.

* chore(nix): refresh pnpm deps hash

* ci(release): require bundled Vela CLI for mac/win packaging

tools-pack bundles the Vela CLI optionally and silently skips it when the
platform binary cannot be resolved — which is exactly how Windows and
Intel-mac shipped without AMR unnoticed. Now that vela-cli 0.0.6 publishes
all platform packages, pass --require-vela-cli to the mac (arm64 + intel) and
win build commands so packaging fails loudly if the bundled CLI is missing
instead of producing an AMR-less app. Linux (containerized) is left as-is for
a separate follow-up.

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-29 20:58:13 +08:00
lefarcenandGitHub 2ea4fcf981 fix(pack): stop release builds depending on node_modules Electron dist (#3289)
Nightly/stable mac arm64 and win x64 packaging failed on release/v0.9.0:

- mac arm64: the "Verify mac Electron framework symlinks" step exited 1
  because node_modules' electron dist had no
  `Electron Framework.framework`.
- win x64: electron-builder failed renaming `electron.exe` ->
  `Open Design.exe` with ENOENT because `electron.exe` was missing from
  the same dist.

Root cause: pnpm does not reliably materialize Electron's prebuilt dist
(a symlinked `.framework` on mac, the unpacked binaries on win) under
`node_modules/.pnpm/electron@*/.../dist` on the CI runners. Both failing
runs were cache-cold (the win job logged "Cache not found"), and the
beta workflow's non-fatal inspect step shows the identical missing
framework on a green run — so it is not a GHA-cache or download-speed
problem, and it is not reproducible locally.

The mac builder never passed `electronDist` and has stayed green because
electron-builder downloads its own Electron (~1s for 116 MB on CI). The
win and linux builders pinned `electronDist` at the fragile node_modules
dist, and stable/preview still ran the old fatal verify step (beta was
already relaxed to a non-fatal inspect).

This change aligns every platform with the mac approach:

- win/linux: drop `electronDist` so electron-builder downloads its own
  complete Electron for the target platform.
- release-stable.yml / release-preview.yml: replace the fatal "Verify
  mac Electron framework symlinks" step with the non-fatal "Inspect"
  step already used by release-beta.yml (electron-builder is the source
  of truth for whether packaging works).

Validated: tools-pack typecheck, guard, and the win/linux/win-app/
workspace-build pack test suites all pass. Real win/linux packaging is
validated by the release workflow itself.
2026-05-29 16:44:55 +08:00
CaprikaGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>lefarcen
76c7d31c53 chore: bump vela cli to 0.0.4 (#3239)
* chore: bump vela cli to 0.0.4-test.0

* chore: refresh lockfile for vela cli 0.0.4-test.0

* chore(nix): refresh pnpm deps hash

* fix: materialize electron before mac release checks

* fix: rebuild electron when mac framework links are invalid

* revert: drop release workflow experiments

* chore(nix): refresh pnpm deps hash

* fix: stop blocking beta mac release on electron symlink preflight

* fix: stop using custom electron dist for beta mac packaging

* fix: guard oversized chat images and opencode overflow

* chore: bump vela cli to 0.0.4

* chore(nix): refresh pnpm deps hash

* fix(daemon): surface prompt-image stat failures instead of dropping them

resolveSafePromptImagePaths only swallowed unresolvable path input; once a
path was confirmed inside UPLOAD_DIR and existed, a statSync failure
(EACCES/EPERM, a file vanishing mid-run) silently dropped the image and let
the run continue without that prompt context. Since this helper is now also
the 1 MB enforcement point, that turned an infra/validation failure into a
'successful' run with missing required context.

Collect those into a new failedImages bucket and fail the run with
INTERNAL_ERROR at the call site, mirroring the oversized-image guard. Add a
unit test covering statSync throwing.

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
Co-authored-by: lefarcen <935902669@qq.com>
2026-05-29 06:41:17 +00:00
9a4816b101 feat(plugins): site-wide plugin detail pages, share-to-site links, landing deploy trigger (#2999)
* feat(plugins): site-wide plugin detail pages, share-to-site links, landing deploy trigger

Why: a merged plugin PR didn't redeploy the landing site (plugins/** was missing from the deploy paths), and the desktop Share menu copied a local/404 link instead of the public marketplace URL. The landing plugin routing left by the detail-page rework also 404'd: the locale listing's cards used a multi-segment href while detail pages were single-segment, and only 388 bundled _official plugins had pages.

What changed:
- Deploy: landing-page deploy/ci trigger on plugins/**, and skip the slow previews step on an exact cache hit (cache key aligned across both workflows so a PR-built cache is reused by main).
- Share URL: packages/contracts/plugin-url.ts owns the single-segment plugin URL scheme; the web Share menu and the landing site both derive links from it. Web links now point at https://open-design.ai/plugins/<slug>/.
- Full detail coverage: detail pages now cover all 403 local plugins (_official incl. atoms + community), each rendered from its local manifest. Fixes the locale-listing 404s and the community manifest-name/catalog-id (- vs /) mismatch.
- Self-host: daemon exposes OD_SITE_ORIGIN via /api/app-config; web falls back to the canonical origin until the daemon answers.

Validation: pnpm guard, pnpm typecheck (all packages), contracts + web tests green, and a full build E2E confirming all 403 catalog ids and locale-listing cards resolve to built detail pages (0 missing).

* chore: retrigger CI

* ci(landing): carry plugins/** trigger + previews cache-hit into #2994 split workflows

Merged origin/main, which split landing deploy into staging + manual production (#2994). git auto-migrated my landing-page-deploy.yml changes into landing-page-staging.yml via rename detection (plugins/** path, fallback-preview-card.ts cache key, cache-hit skip all carried). The new manual landing-page-production.yml didn't have them, so add the previews cache-key alignment + cache-hit skip there too (plugins/** path is N/A — production is workflow_dispatch only).

* fix(ci): wrangler-action uses pnpm so it tolerates landing's workspace dep

This PR added @open-design/contracts (workspace:*) to apps/landing-page/package.json so the landing site can share the plugin-url slug rules. But the landing deploy/preview steps run cloudflare/wrangler-action with packageManager: npm in workingDirectory apps/landing-page, and 'npm i wrangler' chokes on the workspace: protocol (EUNSUPPORTEDPROTOCOL), failing 'Validate landing page'. Switch all three landing wrangler-action steps (staging / ci preview / production) to packageManager: pnpm, which is workspace-aware.

* test(e2e): bundled plugins now offer the README badge

After this branch, buildPluginShareUrl returns a public open-design.ai link for bundled plugins (not just official-marketplace ones), so the home-starter share menu now shows 'Copy README badge'. Update the assertion from toHaveCount(0) to toBeVisible().

* fix(landing): drop @open-design/contracts dep, use a landing-local slug helper

Per review on #2999: the marketing site must not import @open-design/contracts (AGENTS.md boundary — it's the web/daemon product-runtime contract layer). Move the slug/path helpers into landing-local app/_lib/plugin-slug.ts; the web client keeps contracts' plugin-url. The two derive the same scheme and are verified in lockstep by the e2e route check (403 share URLs -> 403 detail pages, 0 missing). landing no longer has a workspace dep, so revert the wrangler-action packageManager back to npm.

* fix(landing): include plugins/_official in previews cache key

Per review on #2999: generate-previews.ts builds bundled-plugin preview jobs from plugins/_official/**/open-design.json and renders fallback cards from manifest fields (title/description/mode/scenario/tags). With plugins/** now triggering the workflow but the cache key not hashing plugin inputs, a plugin-only PR/merge could exact-hit an old cache and skip the preview regen, shipping with a stale or missing /previews/plugins/<manifest-id>.png. Add plugins/_official/** to the cache key in all three landing workflows (ci, staging, production). community is not currently covered by generate-previews so its glob is omitted.

* fix(plugins): include community marketplace installs in share gate

hasPublicPage now covers sourceMarketplaceId === 'community' so the
README badge and public detail link surface for community installs.
Community manifest names carry a community- prefix that diverges from
the landing-page route slug, so URL derivation uses sourceMarketplaceEntryName
(community/<folder>) instead — pluginDetailSlug takes the last segment,
matching the /plugins/<folder>/ route the landing page emits.
Adds component tests for buildPluginShareUrl, badge copy, and the
Open-in-marketplace link for a community/registry-starter record.

Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)

---------

Co-authored-by: mrcfps <mrc@powerformer.com>
2026-05-28 09:07:12 +00:00
lefarcenGitHuba1chztAmyMasonCaprikaopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
df8a0faff6 feat(runtimes): register AMR (vela) as an ACP stdio agent (#2355)
* feat(runtimes): register AMR (vela) as an ACP stdio agent

AMR is the vela CLI's ACP runtime mode. `vela agent run --runtime opencode`
speaks ACP JSON-RPC over stdio (see vela's
`specs/current/runtime/manual-agent-run-openrouter.md`); per
`docs/new-agent-runtime-acp.md` we expose it through the same `streamFormat:
'acp-json-rpc'` transport that already powers Hermes, Devin, Kimi, etc.

The new `defs/amr.ts` is the entire wiring — `buildArgs` returns
`['agent', 'run', '--runtime', 'opencode']`, `fetchModels` reuses
`detectAcpModels`, and the fallback list seeds the OpenRouter ids vela's
e2e baseline uses. `executables.ts`/`app-config.ts`/`metadata.ts` get the
matching `VELA_BIN`/`VELA_LINK_URL`/`VELA_RUNTIME_KEY`/`VELA_OPENCODE_BIN`
allowlist + install/docs URLs, so users can configure the per-agent env in
Settings without leaking into other adapters.

Coverage: `tests/fixtures/fake-vela.mjs` is a minimal ACP stub that returns
the documented `initialize` / `session/new` / `session/set_model` /
`session/prompt` shapes; `tests/amr-acp-integration.test.ts` spawns it via
`child_process.spawn` and drives a full turn through `attachAcpSession` and
`detectAcpModels`, so the ACP transport contract for AMR is end-to-end
verified locally even before a real `vela` binary is installed.

Validated:
- pnpm guard
- pnpm typecheck (all workspace projects)
- pnpm --filter @open-design/daemon test (2881/2881)

Deferred: real OpenRouter-backed turn through a built `vela` binary —
the runtime def needs no changes for that path, only `VELA_RUNTIME_KEY`
and `VELA_LINK_URL` in env (or Settings).

* fix(runtimes/amr): pin a concrete default model and bare openai ids

End-to-end validation against a freshly-built `vela` (nexu-io/vela@main)
+ OpenRouter surfaced two contract details the first AMR runtime def
got wrong:

1. vela rejects `session/prompt` with `session/set_model must be called
   before session/prompt`. attachAcpSession in apps/daemon/src/acp.ts
   skips set_model whenever the picked model is the synthetic 'default'
   id, so AMR's fallback list must NOT include DEFAULT_MODEL_OPTION. The
   def now ships a concrete `gpt-5.4-mini` as both `fetchModels`'
   default option and `fallbackModels[0]`, which makes attachAcpSession
   always send a real `session/set_model` for AMR turns.

2. `vela --runtime opencode` auto-prepends `openai/` to whatever modelId
   it forwards to opencode's openai provider. With OpenRouter-style ids
   like `openai/gpt-5.4-mini`, opencode receives the double-prefixed
   `openai/openai/gpt-5.4-mini` and replies `ProviderModelNotFoundError`.
   The new fallback list ships the bare ids opencode's openai registry
   actually knows about (gpt-5.4, gpt-5.4-mini, gpt-5.4-fast, etc.).

Stub + tests:
- tests/fixtures/fake-vela.mjs now enforces the set_model gate the same
  way real vela does, so a regression that silently goes back to
  model: 'default' would surface as a fatal error in tests instead of a
  hidden production failure.
- tests/amr-acp-integration.test.ts pins both contracts: no 'default' /
  no 'openai/' prefix in fallbackModels, and a negative case that
  asserts session/prompt fails when no model is set.

Adds `apps/daemon/scripts/verify-amr-real-vela.mjs` — a small dev-time
runner that drives `attachAcpSession` against a real `vela` binary and
prints the daemon's chat events, so future protocol drift can be checked
against an actual OpenRouter call.

Verified locally: `vela agent run --runtime opencode` + OpenRouter
returns the prompted string ("AMR-E2E-PASS") through the full daemon
pipeline; daemon test suite stays 2883/2883.

* fix(runtimes/amr): substitute concrete model when chat run sends 'default'

A plugin-driven AMR run from the UI surfaced a real-world hole in the
prior commit:

  json-rpc id 3: session/set_model must be called before session/prompt

The Default-design-router plugin (and any caller that doesn't pin a
real model) sends `model: 'default'` straight through, which the AMR
runtime def cannot accept — vela rejects `session/prompt` without
`session/set_model` and attachAcpSession skips set_model whenever
model === 'default'. Just leaving DEFAULT_MODEL_OPTION out of the
adapter's `fallbackModels` is not enough: the chat-run handler in
server.ts still forwarded 'default' verbatim.

This adds `resolveModelForAgent(def, resolved, env?)` as the
single source of truth for the substitution:

  1. If the caller picked a real id, pass it through.
  2. Else, if `def.defaultModelEnvVar` is set and the daemon process
     env has a non-empty value for it, return that (operator escape
     hatch — see below).
  3. Else, if the def's `fallbackModels` does NOT contain a 'default'
     id, return `fallbackModels[0].id`.
  4. Else, return the original value (the historic shape — defs that
     list 'default' themselves are untouched).

AMR sets `defaultModelEnvVar: 'VELA_DEFAULT_MODEL'`, so when
opencode's openai-provider registry deprecates `gpt-5.4-mini`
upstream, an operator can swap the fallback id without a code change
by exporting `VELA_DEFAULT_MODEL=gpt-5.5` before launching tools-dev
/ od. Worth noting the env var must live in the daemon's `process.env`
(Settings-UI per-agent env values only reach the spawned child, not
the daemon's resolver) — the new field's docblock spells this out.

Coverage:
- `tests/runtimes/resolve-model.test.ts` — 8 unit tests covering all
  four resolver branches plus the env-override happy path / fallback /
  ignore-when-user-picked-a-real-id case.
- `pnpm --filter @open-design/daemon typecheck` clean.

* chore(runtimes/amr): move AMR to the top of the base agent list

So `AMR (vela)` shows up first in the agent picker / status views,
ahead of claude / codex. Pure ordering change; no behavior delta.

* feat(amr): Sign-in / Sign-out button on the AMR Settings card

The first half of the AMR work assumed the operator would set
VELA_RUNTIME_KEY / VELA_LINK_URL on the daemon process and never
surfaced login state to users. This adds the missing UX so a fresh
install can drive the full path from Settings:

  - GET  /api/integrations/vela/status   reads ~/.vela/config.json
    for the active profile and returns { loggedIn, profile, user }
    (without leaking the runtime/control keys themselves).
  - POST /api/integrations/vela/login    spawns `vela login` once
    (409 if one is already in flight). The vela CLI opens the user's
    browser to the device-authorization page itself — Open Design
    only needs to kick the subprocess off.
  - POST /api/integrations/vela/logout   removes ~/.vela/config.json
    so the next status read returns logged-out.

`AmrAgentCard` is a dedicated agent-card component for AMR because
the existing `<button>` row can't host an interactive sub-control
(nested interactive elements). It polls /status after a login click
until the daemon reports loggedIn=true (or 5 minutes elapse), and
exposes a Sign-out action on hover. Other adapters (claude, codex,
hermes, …) keep their existing `<button>` card.

i18n: 8 new keys (settings.amrLogin / Logout / LoggingIn / etc.)
added to en + zh-CN. Other locales spread `en` and inherit the
English copy until translations land.

Coverage:
- `tests/integrations/vela.test.ts` pins the config.json reader
  against a tmp HOME — including the negative case where a profile
  has user info but no runtimeKey (still logged-out), and the
  secret-leak guard ("rt-secret-*" must not appear in the projection
  payload).
- `tests/components/AmrAgentCard.test.tsx` covers all four UI
  states (logged-out, logging-in, logged-in, logging-out) plus the
  click-propagation invariant the divergent card was built to keep.

`pnpm --filter @open-design/daemon test` 2901 / 2901 passing.
`pnpm --filter @open-design/web test` 1719 / 1719 passing.
`pnpm typecheck` + `pnpm guard` clean.

Dev script side-effects: `apps/daemon/scripts/verify-amr-real-vela.mjs`
no longer requires both VELA_RUNTIME_KEY and VELA_LINK_URL — if
VELA_PROFILE is set, the vela CLI is allowed to resolve credentials
from `~/.vela/config.json`. Added the two AMR `.mjs` fixtures to
`scripts/guard.ts` allowlist with the executable-fixture / dev-runner
rationale.

* fix(connection-test): substitute model for AMR before attachAcpSession

The chat-run path in server.ts already routes the requested model through
`resolveModelForAgent` so AMR / vela (whose CLI demands an explicit
`session/set_model` before `session/prompt`) gets the def's first
concrete fallback id when the chat run ships `model: 'default'`.
`connectionTest.ts` was wiring `attachAcpSession({ ..., model: model ?? null })`
directly, which made the Test Connection button on the AMR Settings
card deadlock with the same `session/set_model must be called before
session/prompt` error the chat-run path already handles — surfaced as a
permanent "Testing connection…" spinner in the UI.

Reuse the same helper here so Test Connection mirrors chat-run behavior.

* test(amr): three-layer end-to-end coverage for the AMR login + turn flow

The PR up to this point shipped runtime + UI code with unit-level Vitest
coverage. This commit adds the cross-layer regression net the live demo
relied on:

1. apps/daemon/tests/integrations/vela.routes.test.ts (HTTP, Vitest)
   Spins up the real daemon Express app via `startServer({port:0,...})`,
   persists `agentCliEnv.amr.VELA_BIN = <fake>` into app-config.json,
   and exercises every /api/integrations/vela/* endpoint against the
   extended fake-vela stub:
     - status reads ~/.vela/config.json under various states
     - login spawns the fake, waits for config.json to appear, returns
       pid + startedAt + profile
     - 409 already-running guard with the stub's delay knob
     - logout removes the file (idempotent)
     - secrets (runtimeKey / controlKey) never leak in the projection
     - login → status round-trip flips loggedIn=false → true

2. e2e/tests/amr/turn.test.ts (tools-dev orchestrated, Vitest)
   Boots a namespaced daemon + web pair through `createSmokeSuite`,
   inlines a self-contained fake `vela` binary that handles BOTH
   `vela login` (writes ~/.vela/config.json) and
   `vela agent run --runtime opencode` (ACP stdio with the
   `session/set_model must precede session/prompt` gate the real binary
   enforces), then drives a complete /api/runs lifecycle for
   `agentId: 'amr', model: 'default'` and asserts the assistant message
   captures the fake's streamed text. This is the test that would have
   surfaced today's plugin-default-model regression (the `set_model
   before prompt` error) at PR time instead of demo time.

3. e2e/ui/amr-login-pill.test.ts (Playwright)
   Mocks /api/agents + /api/integrations/vela/{status,login,logout}
   to drive the Settings AMR card through the full Sign in → Signed in
   → Sign out cycle. Pins the AmrLoginPill polling contract and the
   aria-label semantics (the pill's accessible name is "Sign out" once
   logged in, regardless of which label the hover-state text shows).

fake-vela.mjs extensions:
   - Handles `vela login` argv by writing
     ~/.vela/config.json for the active VELA_PROFILE and exiting 0 —
     mirrors real vela's on-disk side-effect without the device-auth
     loop.
   - FAKE_VELA_LOGIN_DELAY_MS knob so route tests can observe the
     in-flight state of the spawn lifecycle.
   - FAKE_VELA_LOGIN_USER_EMAIL / _USER_PLAN to assert the surfaced
     user fields end-to-end.

Validated:
   - `pnpm guard` + `pnpm typecheck` (all workspace projects)
   - `pnpm --filter @open-design/daemon test`: 2998 / 2998 passing,
     including the new 8-test integration suite.
   - `cd e2e && pnpm test tests/amr`: 1 / 1 passing.
   - `cd e2e && pnpm exec playwright test ui/amr-login-pill.test.ts`:
     1 / 1 passing (6.7s).

* feat(amr): package native cli and refine login ui

* feat(amr): wire vela cli beta packaging

* docs(amr): document vela ci packaging review

* docs(amr): refine vela ci integration review

* fix(ci): refresh nix pnpm dependency hashes

* fix(pack): clean up Vela CLI packaging

* fix(pack): bundle Vela CLI support files

* fix(amr): recover login attempts from stale auth state

* test: expand AMR and automations coverage

* fix(amr): address review follow-ups

* test(web): align tasks fixtures with contracts

* fix(daemon): type wildcard route params

* fix(ci): refresh PR merge validation

* fix(amr): clear env credentials on logout

* feat(settings): inline local CLI model configuration

* fix(amr): recognize daemon env credentials

* [codex] Fix Vela companion packaging (#2979)

* Fix Vela companion packaging

* Update Nix pnpm dependency hashes

* [codex] Surface AMR account failures (#2980)

* fix: surface AMR account failures

* fix: cover AMR recovery error guidance

* chore: bump beta base version to 0.8.1 (#2990)

* Fix AMR profile and packaged runtime review issues

* Detect packaged AMR OpenCode companion tree

* feat(web): polish AMR frontend flows

* Polish AMR onboarding card

* fix: read AMR login state from dot-amr config (#3048)

* test: tighten AMR credential and packaging coverage

* test: restore AMR executable test env helper

* [codex] Fix packaged mac Dock identity and AMR label (#3076)

* Fix packaged mac sidecar Dock identity

* Rename AMR assistant label

* Fix AMR live models and dot-amr login state (#3073)

* fix: read AMR login state from dot-amr config

* fix: load live AMR models before runs

* fix: point AMR onboarding link to production wallet

* fix: address AMR model review feedback

* fix: persist live AMR model fallback

* [codex] Fix AMR link catalog model ids (#3088)

* Fix packaged mac sidecar Dock identity

* Rename AMR assistant label

* Fix AMR link catalog model ids

* Fix AMR model normalization typecheck

* Use live AMR model for default runs

* fix: polish AMR runtime settings UI

* Accelerate AMR startup defaults (#3092)

* Surface AMR insufficient balance wallet URL (#3099)

* fix(web): polish onboarding controls (#3112)

* fix(web): show CLI scan loading state

* Avoid duplicate AMR wallet recharge links (#3117)

* Avoid duplicate AMR wallet recharge links

* Use Vela CLI 0.0.3 test package

* chore(nix): refresh pnpm deps hash

* Fix AMR wallet guidance display

---------

Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>

* chore(pack): pin Vela CLI 0.0.3-test.1 (#3127)

* chore(nix): refresh pnpm deps hash

* chore(pack): pin Vela CLI 0.0.3

* chore(nix): refresh pnpm deps hash

* fix(web): suppress AMR exit 130 fallback (#3136)

* feat(web): nudge users to hosted AMR on model/auth/quota failures (#3083)

* feat(web): nudge users to hosted AMR on model/auth/quota failures

When a non-AMR agent run fails with an auth / quota / upstream model
error, surface an inline nudge under the error pill linking to Open
Design's hosted AMR gateway (https://open-design.ai/amr). The nudge
fires `surface_view` (element=run_failed_toast) on impression and
`ui_click` (element=go_amr) on the link.

Also teach the daemon to classify CLI-agent auth/quota/upstream failures
(Claude Code, codex, ...) into specific API error codes
(AGENT_AUTH_REQUIRED / RATE_LIMITED / UPSTREAM_UNAVAILABLE) instead of
the generic AGENT_EXECUTION_FAILED, so both the error message and the
nudge key off accurate codes. AMR's own runs are excluded from the
nudge — they keep the dedicated sign-in / recharge affordances.

* feat(web): rework failed-run AMR guidance into per-case error UI

Replace the single inline nudge with a per-case failed-run experience
driven by the run's error code + agent:

- The error card is now neutral gray (was red) and always carries a
  retry button; it is driven by the persisted per-message error event so
  it survives a reload.
- Non-AMR agent hitting a model/auth/quota wall: a theme-color promotion
  card under the error card offers "switch to AMR & retry" — switches the
  run to AMR, opens Settings on the AMR card, and auto-retries once the
  account signs in (ProjectView polls vela login status, independent of
  the Settings pill lifecycle, with success / 5-min-timeout / unmount
  exits).
- AMR agent unauthorized: clearer copy + an "authorize & retry" button.
- AMR agent out of balance: clearer copy + a "top up" button to the AMR
  wallet, with manual retry.
- Settings AMR card: when opened from the nudge, it scrolls into view and
  pulses, and an authorize-button coachmark (a fake hand cursor that
  rises in and dismisses on hover) points at the sign-in control when not
  yet authorized.

analytics: surface_view (run_failed_toast) on the promotion card and
ui_click (go_amr) on its action are retained. i18n adds chat.amrCard.*
and chat.amrError.* (en / zh-CN / zh-TW translated; other locales fall
back to en) and drops the old chat.amrErrorGuidance keys.

* fix(daemon): require status context for numeric service-failure codes

Per review on #3083: the model-service classifier matched bare HTTP
status numbers (`500`, `502`, `429`, `401`), so ordinary CLI output like
`line 500`, `read 502 bytes`, or `exit code 401` could be misclassified
as a provider outage / auth wall and wrongly surface the AMR nudge. Now
a status number only counts when it carries explicit context (`HTTP 500`,
`status 503`, `code: 401`, `502 Bad Gateway`); textual provider phrases
(overloaded, bad gateway, service unavailable, rate limit, …) are
unchanged. Adds fixtures proving unrelated numeric output stays null.

* fix(web): keep error pill for failed runs ChatPane's card doesn't cover

Per review on #3083: the per-message gray error pill was suppressed for
every persisted error status event, but ChatPane only renders the
replacement top-level error card for `retryableAssistantMessage` (the
last failed assistant). So a failed turn that is no longer last (after a
follow-up) or an older failed run in history showed neither the pill nor
the card — its error detail vanished, undercutting reload/history
survival. ChatPane now passes `errorCardOwnerId` (the assistant id whose
error the card represents); AssistantMessage suppresses only that one
pill and keeps rendering StatusPill for all other error events.

* fix(daemon): don't treat a process exit code as an HTTP status

Follow-up to review on #3083: the status-context helper accepted a bare
`code` prefix, so `exit code 401` / `process exited with code 429` still
matched and got classified as AGENT_AUTH_REQUIRED / RATE_LIMITED (the
very `exit code 401` case the comment calls out as noise). `code` now
only counts when qualified (`status code` / `error code` / `response
code`) or punctuation-bound (`code: 401`); bare `exit code N` no longer
matches. Adds fixtures for exit-code lines returning null.

* chore(web): translate AMR card / error keys for 16 remaining locales

PR #3083 added 10 new `chat.amrCard.*` / `chat.amrError.*` keys but only
provided en/zh-CN/zh-TW translations; the other 16 locales fell back to
English. Translate the card title/body, three chips, primary CTA, and
the AMR self-error (auth / balance) messages and buttons for ar, de,
es-ES, fa, fr, hu, id, it, ja, ko, pl, pt-BR, ru, th, tr, uk.

* fix(amr): address review feedback on #2355

Targeted fixes for the unresolved review threads on #2355. Each fix
includes / updates a focused test.

- runtimes/executables.ts: `packagedVelaOpenCodeCompanionTree` now
  verifies the inner `opencode` executable exists + is runnable, not
  just the directory. This closes the false-positive availability path
  that let `detectAgents()` surface AMR as available even when the
  packaged companion was empty / partially copied (mrcfps, 4 threads).

- runtimes/executables.ts: `resolveAmrOpenCodeExecutable` now prefers
  the bundled `<OD_RESOURCE_ROOT>/bin/libexec/opencode/opencode` over a
  stale `opencode` on the user's PATH, so packaged AMR builds can't be
  hijacked by a global installation.

- web/EntryShell.tsx: when the Local CLI scan returns an available
  agent and the previously-selected agent is AMR, switch the selection
  to the first available local agent so the runtime and persisted
  agent agree before Continue.

- server.ts (model-probe branch): for AMR, check `readVelaLoginStatus`
  BEFORE rejecting on an empty live-model catalog — a signed-out user
  was getting `AMR_MODEL_UNAVAILABLE` ("choose a model") instead of
  the correct `AMR_AUTH_REQUIRED` (sign-in affordance).

- server.ts (default model fallback): if the user asked for the AMR
  agent default and the cached id is no longer in the FRESH catalog,
  fall back to `liveModels[0]` from the probe instead of rejecting the
  run as `AMR_MODEL_UNAVAILABLE`.

- integrations/vela.ts: route `vela login` through
  `createCommandInvocation` so an npm/Node-style `vela.cmd` / `.bat`
  shim on Windows gets the correct `cmd.exe /d /s /c …` wrapping with
  verbatim args (matches `execAgentFile` / chat-run spawning).

- tools/pack/src/linux.ts: in containerized Linux builds, bind-mount
  the host directory of `OPEN_DESIGN_VELA_CLI_BIN` and rewrite the env
  to the container-side path. The host path was being passed in as-is
  even though the default container only mounts /project, /tools-pack
  and cache/home — `copyOptionalVelaCliBinary` saw a missing path.

Deferred (out of scope for this PR):
- `od amr status/login/logout/cancel` CLI subcommands (AGENTS.md
  UI/CLI dual-track rule, server.ts:5763) — sizable surface; tracked
  for a separate focused PR.
- Strict `--require-vela-cli` for Windows + mac-x64 beta builds:
  prematurely blocked — `@powerformer/vela-cli` only publishes the
  `darwin-arm64` platform binary today; adding the flag elsewhere
  would fail the builds. Revisit once win/x64/linux binaries ship.

* fix(amr): hoist sendAmrAccountFailure above the AMR catalog preflight (TDZ)

The new signed-out AMR branch in the catalog preflight at server.ts:10875
calls `sendAmrAccountFailure(...)` to emit AMR_AUTH_REQUIRED, but the
const declaration sat ~100 lines below at the outer function scope. Because
`const` is TDZ-aware, that branch would have thrown `ReferenceError:
Cannot access 'sendAmrAccountFailure' before initialization` for the
exact users it tries to help — defeating the original intent.

Hoist the helper to just above the AMR preflight block so it's available
to every AMR code path in this function. Behavior elsewhere is unchanged.

Also rerun the daemon test suite: `launch.test.ts > resolveAgentLaunch
uses packaged built-in Vela for AMR` was creating the
`<resourceRoot>/bin/libexec/opencode/` companion *directory* only, but
this PR's earlier tightening of `packagedVelaOpenCodeCompanionTree`
also requires the inner `opencode` executable. Add it to that fixture
to match the new contract; the test was a sibling of the executables /
env-and-detection fixtures already updated in 13fc4f4.

Addresses #2355 review (mrcfps, 2026-05-28).

* feat(web): add hover cancel for AMR login (#3158)

* feat(web): add hover cancel for AMR login

* fix(web): don't bounce AmrLoginPill back to 'Signing in…' after local cancel

Both codex-connector (P2) and looper (CHANGES_REQUESTED) on this PR
flagged the same race in the new local-cancel path: `handleCancelLogin`
dispatches `notifyAmrLoginStatusChanged('login-canceled')` immediately
after `/login/cancel` returns, but the `AMR_LOGIN_STATUS_EVENT` listener
unconditionally re-enters `refresh()` and then restarts polling
whenever `/api/integrations/vela/status` still reports
`loginInFlight: true`.

That is a real race because the daemon's `cancelVelaLogin()` only sends
SIGTERM (escalating to SIGKILL after `LOGIN_CANCEL_KILL_GRACE_MS` =
2000 ms) and keeps the child in `activeLoginProcs` until it actually
exits — so the first `/status` read after a successful cancel can
legally still come back as in-flight. Under that window the pill flips
back to 'Signing in…' and can later surface the timeout/error path even
though the user already canceled, defeating the behavior promised in
the PR description.

Fix the listener instead of every dispatch site: in the
`login-canceled` branch, after the local reset (stopPolling +
setPending(null) + clear refs), optimistically mark every subscribed
pill instance as not-in-flight (`setStatus((c) => c ? { ...c,
loginInFlight: false } : c)`) and `return` — skip the
refresh-and-reconcile branch below entirely. The next explicit refresh
(component mount, user interaction, or a `status-changed` event) will
pick up the daemon's confirmed state once the child has actually
exited.

Add a focused regression test that holds `/api/integrations/vela/status`
at `loginInFlight: true` even after a successful `/login/cancel`,
asserting that the pill stays at the Canceled → Authorize sequence and
never bounces back to 'Signing in…'. This test fails on the pre-fix
listener and passes on the new behavior; existing
'cancels an in-flight AMR sign-in…' and 'reconciles late AMR browser
completion to Signed in after local cancel' tests continue to pass.

Addresses review feedback on #3158 (chatgpt-codex-connector, nettee).

---------

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

---------

Co-authored-by: a1chzt <chizblank@gmail.com>
Co-authored-by: Amy <1184569493@qq.com>
Co-authored-by: Mason <jinmeihong0201@gmail.com>
Co-authored-by: Caprika <56862773+alchemistklk@users.noreply.github.com>
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
2026-05-28 05:09:55 +00:00
d3a5e2901b Add release-beta-s self-hosted workflow placeholder (#3150)
* Add self-hosted beta release workflow placeholder

* ci: register self-hosted runner labels for release-beta-s lint

actionlint flagged the custom self-hosted labels nexu-win and release-beta
as unknown. Add them to .github/actionlint.yaml so the release-beta-s
workflow passes the lint gate.

Generated-By: looper 0.0.0-dev (runner=fixer, agent=claude-code)

---------

Co-authored-by: libertecode <libertecode@proton.me>
2026-05-28 03:44:52 +00:00
lefarcenandGitHub abe72af2a2 fix(ci): resolve blog-SEO base ref to a SHA in merge queue (#3137)
The "Lint changed blog SEO" and "Guard blog URL changes" steps in
landing-page-ci.yml fell back to the literal "HEAD^" when no base SHA was
available. On merge_group (and first-push) events there is no
pull_request.base.sha / before, so BASE became "HEAD^", and the
blog-indexing scripts' assertSafeGitRef (regex /^[A-Za-z0-9_./:-]+$/,
which forbids "^") threw `Unsafe git ref for base: HEAD^`, failing the
merge queue for every PR.

Resolve the fallback to a concrete commit with `git rev-parse HEAD^`
(checkout uses fetch-depth: 0, so it is always available), mirroring
blog-indexing-on-deploy.yml. The resulting 40-hex SHA passes the ref guard.
2026-05-27 17:07:09 +00:00
ce9fa687ca ci: trigger PR exploration via maintainer /explore comment (no approval) (#3139)
* ci: trigger PR exploration via maintainer "/explore" comment (no approval)

Add a low-friction way to run the sandbox exploration: a maintainer
comments "/explore" on a PR.

- on: issue_comment (kept workflow_dispatch). The job `if` allows the
  comment path only when it is on a PR and the commenter has write access
  (author_association OWNER/MEMBER/COLLABORATOR), so randoms cannot trigger
  it; untrusted PR code still runs only inside the Docker sandbox.
- Drop the agent-pr-explore environment approval gate: both triggers are
  already write-gated and there is no auto-trigger, so the extra manual
  approval is redundant. R2 creds are repo-level secrets (no env-scoped
  secrets), so they stay available without the environment.
- Feedback: 👀 reaction on the command + a placeholder comment carrying
  the report marker (so the run yields one evolving comment), 🚀 on
  success, and 👎 + a failure note (with the run link) on failure.

Does not auto-run on every PR, so unrelated PRs stay clean.

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

* ci: don't clobber a produced report with the /explore failure note

Review: the failure-feedback step ran after the always() report step, so
on the failure-with-report case (sandbox wrote a report then exited
non-zero) it overwrote the just-posted report with the generic "failed
before producing a report" note — losing the useful output.

Guard it: if the report file exists, leave the posted report in place and
skip the failure note/reaction. Only post the short failure note when no
report was produced.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 16:48:58 +00:00
Marc ChanandGitHub bc44a8add3 ci: relay contributor card events to worker (#3113) 2026-05-27 14:48:01 +00:00
bf61a39cb5 ci: clean agent report (write-to-file) + slim artifacts/uploads (#3116)
* ci: clean agent report (write-to-file) + slim artifacts/uploads

Four related cleanups to the agent PR exploration output:

1. Clean report. The PR comment / report.md was assembled by dumping the
   entire verbose expect.log (ACP init logs, "Git failed" warnings, the
   ~24KB echoed prompt, ANSI codes, progress checklist) under the trace
   header -- ~28KB of noise. Instead, instruct the agent to write its
   final Markdown report to a file via its file-write tool, and have the
   runner read that file directly. Verified: Codex writes a clean report
   to the given absolute path. Falls back to an inconclusive note if the
   agent did not finish.

2. Drop duplicate trace/video. The script copied
   playwright-smoke-trace.zip -> playwright-trace.zip (a ~28MB legacy
   duplicate) and the webm likewise, and uploaded both to R2. Keep only
   the canonical smoke-named artifacts.

3. Slim the GitHub artifact. The trace zips and videos are already on R2;
   exclude *.zip / *.webm from the uploaded artifact so it drops from
   ~56MB to <1MB (report + logs only).

4. Persist report on the runner. Copy the report / agent-report /
   expect.log / trace URL to a stable host dir
   ($HOME/.cache/agent-pr-explore/reports/pr-<n>) so dry runs
   (skip_comment) can be inspected without downloading the artifact.

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

* ci: address review — keep advisory reports + recursive artifact excludes

Review findings on the report/artifact cleanup:

1. Regression fix: the non-app-surface and deterministic-verifier branches
   write their pre-baked advisory report (Inconclusive / Pass / Fail) and
   never run the agent, so they don't produce agent-report.md. After
   switching write_agent_report_artifact to read only agent-report.md they
   fell through to the "agent did not write a final report" fallback,
   dropping the real advisory (and mis-reporting on .github-only PRs like
   this one). Fix: those branches now write their advisory directly to
   $agent_report_file — single source of truth for the report body.

2. Recursive artifact excludes: the source Playwright recording lives at
   artifacts/playwright-video/<uuid>.webm; non-recursive !*.webm / !*.zip
   didn't match the subdirectory. Use **/*.zip and **/*.webm so the slim
   actually holds.

3. Drop the now-dangling summary.legacyTrace field (the legacy trace copy
   is no longer produced), matching the legacyVideo removal.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 12:30:05 +00:00
f8c860a505 feat(landing-page): localize plugins library across 18 locales (#3010)
* feat(landing-page): localize plugins library across 18 locales

PR #2926 shipped the new `/plugins/` library hub + four kind sub-routes
+ detail pages, but the chrome was English-only — visitors landing on
`/zh/plugins/` saw the old marketplace registry placeholder rendered
by the catch-all instead, and detail pages rendered identical English
copy regardless of locale prefix. This PR brings the plugin surface
to feature parity with `/zh/skills/`, `/zh/templates/`, `/zh/systems/`,
`/zh/craft/`.

## What changes

- New `app/_lib/plugins-i18n.ts` — single source for all plugin chrome
  copy (hub, list pages, chip rails, share dialog, detail-page meta
  labels). English baseline + 17 locale overrides keyed on
  `LandingLocaleCode` (the same short-code shape `localeFromPath()`
  returns). Missing keys per locale fall back to English so a
  partially-translated locale still renders sensibly. Translations
  cover hub copy, four tile titles + blurbs, seven artifact-kind
  labels + descriptions, 23 scene-subcategory labels, 18 detail-page
  chrome strings, and a six-key share-dialog table with a
  per-locale `shareTemplate({title, url})` function (translated for
  every locale where `_lib/i18n.ts` already had one — same voice).
- `app/pages/plugins/{,templates/,templates/[kind]/,skills/,systems/,
  craft/,[slug]/}/index.astro` — every hardcoded English string now
  reads `getPluginsCopy(locale)` keys. Page logic and routing
  unchanged.
- New short-code wrappers under `app/pages/[locale]/plugins/` — six
  files (hub + three sub-routes + `[kind]/` and `[slug]/`) following
  the same pattern `[locale]/skills/index.astro` already uses: each
  re-exports the canonical page component and adds a per-locale
  `getStaticPaths()` so the build emits 17 locale prefixes per
  plugin route. Total plugin-route prerender count goes from ~390 to
  ~7 000, matching the existing skill/template scaling.
- Catch-all (`[locale]/[...path].astro`) — old `getPublicPlugins` /
  `getRegistryCounts` registry rendering removed (placeholder UI
  that was never wired to a real marketplace data source). Plugin
  routes now live exclusively under `[locale]/plugins/...` short-code
  wrappers, so the catch-all stops claiming `'plugins'` as a route
  root. The dead-code path also drops a `pluginCounts.all` reference
  the title row was reading.
- `.plugins-tile-grid` styles promoted from a scoped `<style>` in the
  default-locale hub to global `app/sub-pages.css` so the
  short-code wrapper renders the same hub markup without re-mounting
  per-page CSS — `display: contents`-style scoping pitfalls in
  Astro's per-component CSS scoping made this the cleanest fix.

## Surface area

- [ ] **UI** — new page / dialog / panel / menu item / setting / empty state in `apps/web` or `apps/desktop`
- [ ] **Keyboard shortcut** — new or changed
- [ ] **CLI / env var** — new `od` subcommand or flag, new `tools-dev` flag, or new `OD_*` env var
- [ ] **API / contract** — new `/api/*` endpoint, new SSE event, or changed shape in `packages/contracts`
- [ ] **Extension point** — new entry under `skills/`, `design-systems/`, `design-templates/`, or `craft/`, or change to the skills protocol
- [ ] **i18n keys** — new translation keys (full plugin chrome added across all 18 locales)
- [ ] **New top-level dependency** — adding any new entry to the **root** `package.json`
- [ ] **Default behavior change** — changes what existing users experience without opting in
- [x] **None** — landing-page-only restoration of i18n parity for the plugin surface

## Validation

- `pnpm --filter @open-design/landing-page typecheck` → 0 errors
- `pnpm --filter @open-design/landing-page build:static` → 16 127 pages
  built (+6 584 over current main: ~388 plugin detail pages × 17
  locale prefixes plus the hub + four sub-routes × 17 locales).
- `copy-example-html.ts` reports `266 entry files + 65 referenced
  files`, identical to before — no regression in the asset-mirroring
  pipeline.
- Local Playwright smoke (`/zh/plugins/...`):
  - `/zh/plugins/` renders `<title>插件库 · Open Design</title>`,
    label `插件库`, h1 `407 个可组合的构件。`, four tiles labelled
    `模板 / 技能 / 设计系统 / 工艺`.
  - `/zh/plugins/templates/video/` renders h1 `48 视频`, scene chips
    `全部 / 动效 / 短视频 / 营销 / 产品 / 数据讲解`.
  - `/zh/plugins/example-article-magazine/` share dialog renders
    `复制下面的文案、然后跳到你想分享的平台粘贴即可` etc., share
    template auto-interpolates plugin title + URL into Chinese voice.
  - All 18 locale prefixes (`/zh`, `/zh-tw`, `/ja`, `/ko`, `/de`,
    `/fr`, `/ru`, `/es`, `/pt-br`, `/it`, `/vi`, `/pl`, `/id`, `/nl`,
    `/ar`, `/tr`, `/uk`) → 200 across hub + four sub-routes + sample
    detail page.
  - English `/plugins/` unchanged (default-locale path bypasses the
    `[locale]/...` wrapper).

* feat(landing-page): finish plugins i18n chrome across 18 locales

The first localization pass shipped a partial fix: hub headings, lead
copy, two-level page chrome, detail-page metadata labels, the share
dialog, and the chip rail were still falling back to English on every
non-English locale because plugins-i18n.ts only filled a chrome slice
for `zh` and the file header even claimed "7 artifact-kind labels and
25 scene-subcategory labels are translated" for every locale that did
not yet have those blocks.

Three changes close the visible gap:

1. plugins-i18n.ts: fills the 27 still-missing chrome fields per locale
   for zh-tw / ja / ko / de / fr / ru / es / pt-br / it / vi / pl / id /
   nl / ar / tr / uk. Includes the 7-key category map, the 23-key
   subcategory map, hubHeading / hubLead, the 4 *Label / *Heading /
   *Lead triples for the templates / skills / systems / craft hub
   pages, the 4 tile blurbs, the 4 browse buttons, sceneLabel, allChip,
   the 12 detail-page metadata labels (mode / scenario / platform /
   surface / author / manifest id / tags / preview caption / find on
   GitHub / homepage / open in new tab) and bucket label map, the
   detail share dialog (title / copy link / jump-to), and the
   header-side nav.plugins entry. zh receives the same 11 detail-page
   and share-dialog labels it was also missing.

2. header.tsx + site-footer.astro: routes the hardcoded "Plugins /
   Templates / Skills / Systems / Craft" labels through `nav.*` from
   HeaderCopy, so every locale gets its own dropdown trigger and
   footer column. Adds `nav.plugins` to HeaderCopy and fills it in 18
   locales with the local form ("插件" / "プラグイン" / "Plugins" /
   "Plug-ins" / "Plaginy" / "الإضافات" / etc).

3. plugin-row.astro + content-i18n.ts: chip rail. The bundled-plugin
   branch now runs raw `mode` / `scenario` slugs through the shared
   localizeTaxonomyValue, and that helper now also consults the
   plugins-i18n subcategory map before giving up. localizeTaxonomyValue
   now returns undefined on a true miss instead of the unknownTag
   placeholder, so chips drop quietly instead of showing "Category" /
   "分類" / "Categoría" for taxonomy slugs we have not localized yet.
   Callers that genuinely want the placeholder (`localizeContentTag`,
   blog `category`, system noun) still keep the explicit fallback.

Out of scope and tracked separately: per-plugin title and description
in plugins/_official/* (author-supplied English metadata, ~401 plugins
without an i18n schema in the manifest yet — needs RFC + tooling
before the manifests can be expanded), and adding the long tail of
mode / scenario / category slugs (`code-migration`, `plugin-sharing`,
`tune-collab`, `live-artifacts`, `engineering`, ...) to TAXONOMY_TERMS
so chips render localized labels for every taxonomy value rather than
dropping silently.

* feat(landing-page): cover plugins chip rail long-tail taxonomy slugs

PR #3010's first round localized the high-frequency mode/scenario
chips (prototype, video, image, marketing, design, ...) but left the
~37 mode/scenario and 14 category slugs that show up in real `od.*`
metadata — code-migration, plugin-sharing, design-system, planning,
scenario, refine, discovery, handoff, token-map, tune-collab, orbit,
live-artifacts, engineering, healthcare, hr, sales, support,
default-router, downstream-export, figma-migration, media-generation,
plugin-authoring, validation, 3d-shaders, animation-motion,
audio-music, creative-direction, design-systems, diagrams, documents,
image-generation, marketing-creative, screenshots, slides,
video-generation, web-artifacts, ... — falling through to undefined
and dropping their chip silently on every non-English locale.

The data layer is the source of truth here, so this expansion lands
in `content-i18n.ts:TAXONOMY_TERMS` / `CATEGORY_LABELS` rather than
the plugins-i18n catalog: a single dictionary entry per slug fans out
to every chip-rail consumer (catalog rows, detail metadata, the
templates/[kind] facets) without each consumer touching its own copy.

Translations cover all 17 non-`en` locales. Brand and product nouns
(Figma, Open Design, BYOK, plugin) stay literal; technical taxonomy
slugs get short equivalents that read as chips rather than full
prose. The result on `/ja/plugins/skills/` matches `/plugins/skills/`
chip-for-chip (30 chips both sides) instead of dropping 27 of them
the way the previous iteration did.

* feat(landing-page): read manifest title_i18n / description_i18n on bundled plugins

PR #3010's prior rounds localized chrome and chip rails but the
catalog's most prominent text — each row's plugin name and blurb —
stayed English on every non-English locale. The plugin manifest
schema (`packages/contracts/src/plugins/manifest.ts`) has supported
`title_i18n` and `description_i18n` (Record<locale, string>) on every
manifest from spec v1; ~24 of the 401 first-party manifests already
carry one for `zh-CN`. The reader was just never wired to use them.

This change does the reader half: bundled-plugins.ts captures the
two i18n maps off each `open-design.json`, plugin-row.astro and the
detail page resolve them at render time via two new helpers
(`resolveBundledTitle`, `resolveBundledDescription`) that mirror the
short→long fallback chain documented in the manifest spec
(`htmlLang` like `zh-CN` → short `LandingLocaleCode` like `zh` →
primary tag → `en` → English baseline). The static-paths pass still
runs once for all locales — it has to, since each manifest produces
one URL — but the title/description shown on the rendered page now
reads the locale off `Astro.url.pathname` and picks the right entry
out of the maps.

Verified locally: `/zh/plugins/example-card-twitter/` now reads
"Twitter 分享卡 / 推特金句 / 数据卡, 适合配推文" from the manifest's
existing `zh-CN` block instead of the English baseline.

Plugin-data half follows in a separate commit. The 17 non-English
locales × 401 manifests need backfilling so the reader has something
to resolve to; that's data, not schema, and lands as a sequence of
manifest patches rather than tangled with this code change.

* feat(plugins): translate scenarios bucket title/description across 17 locales

Closes the first chunk of #3028. Eleven scenarios plugins (the
default-scenario bundle for each taskKind: code-migration,
figma-migration, media-generation, new-generation, tune-collab,
plugin-authoring; the default design router; the React / Vue /
Next.js downstream-export starters; and the Refine baseline) get
title_i18n + description_i18n filled for all 17 non-English locales
the landing page serves (zh-CN, zh-TW, ja, ko, de, fr, ru, es,
pt-BR, it, vi, pl, id, nl, ar, tr, uk).

The reader landed in 7ddfe36; this commit is data-only. taskKind
slugs that other docs reference by name (`code-migration`,
`figma-migration`, `tune-collab`, etc.) stay literal in the
descriptions so cross-references still resolve. Brand nouns —
Open Design, Next.js, React, Vue, Figma — also stay literal.

`/ja/plugins/od-code-migration/` now reads
"コードマイグレーション(デフォルトシナリオ)" instead of the English
baseline; `/zh/plugins/skills/` shows "代码迁移(默认场景)" in the
catalog row.

Remaining buckets (image-templates 45, video-templates 50,
examples 140, design-systems 142 = 377 plugins) follow in
subsequent commits in this PR.

* fix(landing-page): drop CJK template wrap when source name is still English

The Chinese / Japanese / Korean fallback templates for craft, skill,
template, system, plugin, and blog text splice the source `name` /
`title` into a CJK sentence frame: ``${name}工艺规则``,
``Open Design 指南:${topic}``, ``${name} は…のスキルです``. When the
underlying SKILL.md / craft markdown / blog frontmatter still ships
an English name (true for ~95% of the catalog today), that produces
mid-sentence script straddling on `/zh/...`, `/zh-tw/...`, `/ja/...`,
`/ko/...` like:

  H1   : "Editorial typography hierarchy工艺规则"
  Lead : "这条 Open Design 工艺规则定义 Editorial typography hierarchy
          的执行标准…"
  Plug : "video 插件 · 3D Animated Boy Building Lego"

That reads worse than the all-English fallback, because the visitor
parses the page in two scripts at once.

Adds a `nameNeedsEnglishFallback` guard that fires for the four CJK
locales whenever the spliced-in name has no CJK characters of its
own, and threads it through every `localizeXxxText` helper:
craft, template, system, plugin, skill, blog. When it fires the
helper returns the raw English content untouched, so the section
renders end-to-end in one language. Chrome (header, footer, breadcrumb,
buttons, share dialog) keeps its CJK rendering — only the
title-and-lead block falls back.

Side benefit: the same guard kicks in on the long tail of plugin
manifests still pending `title_i18n` / `description_i18n` backfill
(tracked in #3028), so `/zh/plugins/<bundled>/` no longer pairs a
"video 插件 · 3D Animated Boy Building Lego" title with a Chinese
breadcrumb. The page reads "3D Animated Boy Building Lego" + the
English manifest description, while header / footer / breadcrumbs
stay localized. Once a manifest ships its i18n maps, the chrome and
body re-converge automatically.

Non-CJK non-Latin scripts (ar, vi, ...) keep the previous behavior —
their templates already read tolerably with English names. If that
turns out to be wrong on a real audit, the same guard generalizes by
adding the matching Unicode range and locale set.

* feat(plugins): translate image-templates bucket title/description across 17 locales

44 of 45 image-templates plugins get title_i18n + description_i18n
filled for all 17 non-English locales (zh-CN, zh-TW, ja, ko, de, fr,
ru, es, pt-BR, it, vi, pl, id, nl, ar, tr, uk). Generated via Claude
Sonnet 4.5 over the OpenRouter gateway, ~$1.38 in API spend, 156s
wall-clock. Brand and cultural references stay literal (Open Design,
Lego, Hanfu, Showa, Pokémon, Black Myth: Wukong). Long AI generation
prompts collapse to a 1-2 sentence summary capturing what the plugin
does — the description doubles as catalog blurb on the landing site,
not as the actual generation prompt (which lives in example.html /
the manifest's preview entry).

Skipped: `profile-avatar-realistically-imperfect-ai-selfie` returned
malformed JSON on three retries; will rerun with a tighter prompt in
a follow-up commit. Catalog rows for that plugin keep falling back to
the raw English fields per #3010's reader change, so nothing breaks.

Tracking: closes the image-templates row in #3028.

* feat(plugins): translate video-templates bucket title/description across 17 locales

49 of 50 video-templates plugins get title_i18n + description_i18n
filled for the 17 non-English landing locales. Generated via Claude
Sonnet 4.5 over OpenRouter, ~$1.47 in API spend, 177s wall-clock.
HyperFrames templates, the Three Kingdoms cinematic series, the
Seedance/short-film prompts, and the K-pop / wuxia / anime variants
all get a 1-2 sentence catalog blurb in each locale; brand and
cultural tokens (Black Myth: Wukong, Hanfu, Showa, Pokémon, Three
Kingdoms / 三国志, Lego, Disney, K-pop, HyperFrames) stay literal.

Skipped: `live-action-anime-adaptation-water-vs-thunder-breathing-duel`
returned malformed JSON on three retries; will rerun in followup.
Falls back to the raw English fields per the reader landed in 7ddfe36.

Tracking: closes the video-templates row in #3028.

* feat(plugins): translate examples bucket (117/140) title/description across 17 locales

117 of 140 examples plugins get title_i18n + description_i18n filled
for the 17 non-English landing locales. Generated via Claude Sonnet
4.5 over OpenRouter, $3.94 in API spend, ~13 min wall-clock at
8-way concurrency. Existing zh-CN translations on 24 manifests are
preserved (the merge keeps author-supplied entries and only adds
missing locales).

23 of 140 returned malformed JSON on three retries — the output
likely hit the 4000 max_tokens ceiling on plugins whose description
balloons across 17 locales. Those manifests fall back to English on
non-`en` rendering per the reader landed in 7ddfe36, and will rerun
in a follow-up commit with a larger token budget and a stricter
output schema.

Tracking: closes 117/140 of the examples row in #3028; the remaining
23 stay open in that issue's failure list.

* feat(plugins): translate design-systems bucket (141/142) title/description across 17 locales

141 of 142 design-systems plugins get title_i18n + description_i18n
filled for the 17 non-English landing locales. Generated via Claude
Sonnet 4.5 over OpenRouter, $2.55 in API spend, 301s wall-clock at
8-way concurrency.

Translator script gained two improvements between examples and this
bucket:
- max_tokens bumped from 4000 to 8000 so 17-locale outputs stop
  truncating on the long-tail manifests with verbose descriptions
- a balanced-brace JSON extractor that pulls the outermost `{ ... }`
  from the response, tolerating trailing prose Claude occasionally
  appends after the JSON object.

Result: only 1 manifest (`totality-festival`) failed parse this
batch, down from ~16% on the examples bucket. The next commit
re-runs the prior buckets' failures with the improved script.

Tracking: closes 141/142 of the design-systems row in #3028.

* fix(plugins): backfill 4 plugins that retried green after JSON extractor improvement

dcf-valuation, social-media-dashboard, wireframe-sketch (examples
bucket) and live-action-anime-adaptation-water-vs-thunder-breathing-duel
(video-templates bucket) parse cleanly under the balanced-brace
extractor introduced for the design-systems batch. The remaining
22 failures from the prior runs hit a different parse mode (Claude
emitting unescaped double quotes inside string values when the source
description contains its own English quotes like 'make it professional');
those will need a tighter prompt and rerun.

* fix(plugins): translate the last 22 plugins with quote-handling prompt fix

The 22 stuck plugins all carried English / Chinese double-quoted
phrases inside their description (\"make it professional\",
\"What's inside\", \"电子杂志 × 电子墨水\") that Claude was emitting
back inside JSON string values without escaping, breaking the parse.

Added one rule to the translator prompt — never use a straight double
quote inside a translated string, prefer single quotes / curly quotes
/ CJK 『 』 / 《 》 — and the previously stuck batch sailed through
clean: 22/22 ok, 0 retries, $0.85.

This closes the long tail of #3028:
- scenarios   11/11   ✓
- image-templates 45/45 ✓
- video-templates 50/50 ✓
- examples    140/140  ✓
- design-systems 142/142 ✓
- atoms       N/A (filtered from public catalog)

All 388 catalog-visible plugins now ship title_i18n + description_i18n
for all 17 non-English locales the landing page serves.

* fix(plugins): clean up four review-flagged i18n data issues

- apps/landing-page/app/_lib/plugins-i18n.ts:759 — Polish bucket
  label `examples: 'Przyklad'` was missing the diacritic; every
  other Polish string in the same block uses proper diacritics.
  Restore to 'Przykład'. (Reviewer: looper #4364985878.)

- video-templates/cinematic-route-navigation-guide — German
  title_i18n.de was a byte-for-byte copy of en ("Cinematic Route
  Navigation Guide") while the German description was already
  translated. Replace with "Cinematischer Routen-Navigationsleitfaden"
  to match the German voice the description sets.

- video-templates/hollywood-haute-couture-fantasy-video-prompt —
  Dutch title_i18n.nl was identical to en for the same reason.
  Translate the trailing noun phrase: "Hollywood Haute Couture
  Fantasy Videoprompt" (mirrors the Dutch description's compound
  word style).

- video-templates/video-seedance-three-kingdoms-guanyu-slaying-yanliang —
  Korean Hangul `돌진` had leaked into the Turkish description (a
  translation-pipeline artifact where the model copied the verb
  from the Korean output without translating it). Replace
  "saflarına돌진 eder" with the idiomatic Turkish "saflarına dalar".

All four are data-only fixes against existing manifests; no schema
changes, no reader changes. typecheck stays at 0 errors.

* fix(landing-page): localize aria-labels, alt text and BreadcrumbList JSON-LD on plugin detail page

The PR's prior rounds left six accessibility / structured-data
surfaces on `/{locale}/plugins/<slug>/` either entirely English or
mixing English chrome with the localized plugin title. Reviewer
flagged each one across multiple loops; this commit clears them all:

1. `aria-label` on the open-in-new-tab popout no longer reuses the
   visible label `pcopy.detailOpenInNewTab` (which carries the
   decorative `↗`). Added `detailOpenInNewTabAria` — same wording,
   no glyph — and the `<a aria-label>` consumes that key. The
   visible link text still ends in `↗`.

2. `<nav class="breadcrumb" aria-label="Breadcrumb">` now reads
   `aria-label={pcopy.breadcrumbLabel}`. Eighteen locales filled
   ("面包屑导航", "パンくずリスト", "Brotkrumen-Navigation",
   "Fil d'Ariane", "مسار التنقل", "İçerik haritası", ...).

3. Share-dialog `<button aria-label="Close">` now reads
   `aria-label={pcopy.shareDialogClose}`. Eighteen locales filled
   ("关闭", "閉じる", "Cerrar", "Закрыть", "إغلاق", ...).

4. Three template-literal a11y strings (`${pluginTitle} preview`,
   `Open interactive preview for ${pluginTitle}`, `${pluginTitle}
   interactive preview`) become function calls
   (`pcopy.previewImageAlt(t)`, `previewSummaryAria(t)`,
   `previewIframeTitle(t)`) so the sentence frame around the
   plugin title rotates with the page locale. Two `<img alt>` call
   sites (the static preview at line 210 and the click-to-expand
   thumbnail at line 179) both consume `previewImageAlt`.

5. `BreadcrumbList` JSON-LD position-2 now reads
   `name: pcopy.hubLabel` instead of hardcoded English `"Plugins"`.
   The visible breadcrumb at line 105 already renders
   `pcopy.hubLabel`; this aligns the structured data with the
   rendered chrome on every locale.

The new function-typed keys deliberately interpolate `pluginTitle`
(which is itself locale-resolved via `resolveBundledTitle`) so the
mixed-language guard from commit 002d457 is preserved: a manifest
without a per-locale title still flows through to a coherent
single-language a11y string because `pluginTitle` falls back to
English along with the rest of the section.

apps/landing-page typecheck stays at 0 errors.

Closes reviewer threads:
- #pullrequestreview-4364985878 (Open in new tab aria)
- #pullrequestreview-4368926224 (Polish typo + plus mixed-language alt/aria)
- #4373... (BreadcrumbList JSON-LD)
- #4374... (aria-label="Close" + aria-label="Breadcrumb")

* fix(landing-page): redirect legacy fa/hu/th /plugins/ paths to canonical

When the new `/{locale}/plugins/...` short-code wrappers landed, the
legacy catch-all `pages/[locale]/[...path].astro` dropped `'plugins'`
from its `paths` list. That intentionally avoids serving stale
marketplace-registry placeholder routes for the modern landing
locales — but it also takes `/fa/plugins/`, `/hu/plugins/`, and
`/th/plugins/` from 200 to 404, because those three legacy locales
live only in the old `_lib/i18n.ts:LOCALES` set and are not part of
`LANDING_LOCALES` (the modern 18-locale list the new wrappers serve).

Three `301`s in `_redirects` send those legacy URLs to the canonical
English `/plugins/...` so SEO and inbound links keep working until
the legacy locale set is retired entirely.

Reviewer thread (#pullrequestreview-4364052045) flagged this as a
non-blocking regression across multiple loops; this commit closes it.

* ci(landing-page): add merge_group trigger so the queue can clear PRs

`landing-page-ci.yml` only fired on `pull_request` and `push:main`,
which meant the required `Validate landing page` and
`Strict PR visual tests` checks never dispatched against the
`merge_group` ref the merge queue creates. The queue then sat at
"awaiting checks" until it timed out and ejected the PR (the
deadlock observed during the 5/26 release window).

Adding a `merge_group: { types: [checks_requested] }` trigger to
the same workflow lets the queued ref reuse the existing job graph,
matching the pattern in `ci.yml` which already wires `merge_group`.

Also drops `plugins/**` into the same paths filter as `pull_request`
since the new bundled-plugins reader (commit 7ddfe364) consumes
those manifests' `title_i18n` / `description_i18n` maps and the
landing-page CI must rerun when manifest data changes.

---------

Co-authored-by: Joey-nexu <joeylee12629@gmail.com>
2026-05-27 09:30:59 +00:00
1ac3da130f ci: add skip_comment dry-run input to agent PR exploration (#3080)
Add a `skip_comment` workflow_dispatch input (default false). When set,
the "Comment exploration report" step is skipped, so a validation/dry
run can exercise the full pipeline and produce the report artifact
without posting a public comment on the target PR (useful when testing
against an external contributor's PR). The report is still uploaded as
an artifact for review.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 05:51:48 +00:00
80639d4da4 ci: make agent PR exploration trusted checkout lightweight (#3071)
The "Checkout trusted base scripts" step did a full actions/checkout of
this large repo on the self-hosted runner. On a recent run it stalled in
the initial `git fetch --depth=1 origin <sha>` for many minutes before
the agent script ever started, and the run had to be cancelled.

The trusted host side only needs the self-contained
`.github/scripts/agent-pr-explore-sandbox.sh`; PR code is checked out
inside Docker and PR context is gathered via the API. Replace the full
checkout with a single-file fetch via `gh api` (raw), pinned to the same
trusted base/dispatch commit, which avoids the git-protocol fetch of the
whole repo entirely.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 04:18:19 +00:00
lefarcenandGitHub 7b8bf0d9fb ci: map agent trace upload to existing R2 secrets (#3013)
* ci: map agent trace upload to existing R2 secrets

* ci: make agent report comments macos-compatible

* ci: ensure Playwright browsers for agent traces
2026-05-27 03:01:36 +00:00