* feat(landing-page): restructure /plugins hub — resources-first, natural copy
Lead with the design plugins themselves instead of a raw count. Rename the
hub to the agent design plugin library, rewrite the hero copy in plain
language (en/zh/zh-tw), promote a real editor's-picks grid above a
browse-by-type section, and slim the contribute strip to the bottom.
* feat(landing-page): category cover cards + centered hub hero
Give each category collection an on-brand cover illustration (Figma-selection
visual language), center the hub title + lead, and drop the editor's-picks
experiment so every card on the hub shares one style.
* feat(landing-page): content-rich plugins hub — stats, CTAs, explore grid
Turn the hub into a full library at a glance: centered hero with quick
stats and Download / GitHub-star CTAs, smaller 3-up category covers, and
a dense 'explore all resources' grid of real plugin cards with filter
chips. Reuses TemplateCard + the templates filter-chip styles.
* feat(landing-page): in-page category filter + refined hero CTAs
Explore section now filters the grid in place by real template category
(clean i18n chips: prototype/slides/image/video/…), not links away. Hero
stats drop the license line and ellipsize the agent list; the GitHub star
button uses the canonical btn-ghost style.
* fix(landing-page): kill 130px section padding gap, HTML-only explore
The global `section { padding: 130px 0 }` homepage rule was inflating the
hub's grid/explore sections by 260px each. Cancel it. Also filter the
explore grid to HTML templates (hide image/video/audio generators), and
link the agent stat to /agents/ ("…21 agents").
* feat(landing-page): soft brand-tinted hero wash + agent stat link
Add a faded radial wash behind the hub hero (acid-green + faint coral
dissolving into the page), echoing the /stories hero treatment. Link the
agent stat pill to /agents/.
* feat(landing-page): real /stories hero art plate + richer explore
Use the shared lab-stage-art.webp faded base image behind the hero (same
treatment as the /stories hero). Explore grid now shows only templates
with a real preview cover, expands filter chips to the common content
tags (web/deck/marketing/prototype/…), and shows up to 36 cards.
* fix(landing-page): hero art plate sizing + lighter overlay
Cap the hero art plate at the image's native 1920px so wide screens don't
upscale/soften it, align the crop to the reference (50% 16%), and lighten
the white overlay so the art reads as vividly as the /stories hero.
* fix(landing-page): drop preview autoplay on hub to stop tab freeze
The hub explore grid's video previews were auto-playing on scroll (many at
once), freezing the tab. Remove TemplateCardAutoplay so cards render as
static poster frames (data-src never promoted), and cap the grid at 24.
* fix(landing-page): full-bleed hero art, 4-up explore, drop low-quality card
Remove the 1920px cap so the hero art plate is full-bleed (no white side
gutters on wide screens) and extend it to cover the top. Explore grid goes
4-up (smaller cards). Exclude the low-quality example-data-report entry.
* fix(landing-page): extend hero art past nav padding to kill top white gap
.sub-main's 140px top padding left a ~12px white strip above the art plate.
Pull the ::before up to -168px so the art reaches the very top of the page.
* fix(landing-page): curate explore filter tags via allowlist
The raw tag set is noisy and full of near-dupes (deck/slides/ppt,
hyperframes/html-video, marketing/launch/campaign). Replace the blocklist
with a curated non-overlapping allowlist: web/deck/marketing/prototype/
desktop/brand/editorial/motion.
* fix(landing-page): equal-height explore cards
Give the explore grid equal-height rows (grid-auto-rows: 1fr), stretch each
card to fill its cell so the internal excerpt row absorbs the slack and the
CTA buttons align, and clamp the description to 3 lines.
* feat(landing-page): localize explore filter tag labels
Add a curated tagLabels map (web/deck/marketing/… → localized) for en / zh /
zh-TW so the explore filter chips are no longer hard-coded English; other
locales fall back to English via the deep-merge.
* feat(landing-page): localize hub hero + explore copy for all 9 shipped locales
ja/ko/de/fr/ru/es/pt-br/it/tr now have the new hero heading + lede, eyebrow,
stat pill, download CTA, explore title, All/See-all, and the filter tag
labels — no more English leaking on non-en/zh hub pages. astro check clean.
* chore(landing-page): complete zh-tw hub strings for consistency
Even though zh-tw is a retired locale (404, not shipped), fill in the
remaining new hub keys in the zh-tw override so the block is internally
consistent and no longer reads as partially localized.
---------
Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com>
OpenAI rejects response_format on /v1/images/edits when the model is a
gpt-image-* model with HTTP 400 'Unknown parameter: response_format'.
The generations branch already avoids setting response_format for
gpt-image-*, but the edit (image-to-image) branch set it unconditionally
whenever a reference image was supplied.
Set response_format only for non-gpt-image-* models in the edit branch
to match the generations behaviour.
* feat(plugins-home): order the template gallery by usage (OPEND-449)
Non-prototype facets (Slides, Image, Video, …) now lead by real usage — a
trailing 28-day blend of distinct-users + runs from `run_finished`. The
Prototype facet keeps its curated editorial order; the default mode-seeds
(Web Prototype, Simple Deck) and no-preview / manually-sunk tiles drop to the
bottom of their facet and the All view. Ordering lives in one shared comparator
(`comparePluginGalleryOrder`) used by the grid and the Home rail; the All view
keeps its visual-appeal order with sunk tiles pushed last.
A weekly in-repo workflow rebuilds the popularity snapshot from PostHog and
opens a self-PR; a committed snapshot ships the ordering immediately.
* test(plugins-home): make Live Artifact grouping assertions order-insensitive
The rail + Community-section tests hardcoded the curated Live Artifact preset
order. Facet order is now usage/sink-driven (OPEND-449), so these grouping/
routing tests assert membership via a sorted comparison instead.
---------
Co-authored-by: Scarlett Huang <scarletthuang@Scarletts-MacBook-Air.local>
AvatarMenu's compact trigger button (the provider selector in the
composer footer) rendered `AgentIcon id={currentAgent.id}` whenever
`currentAgent` was truthy — but `currentAgent` is derived only from
`config.agentId`, which keeps its last value after switching
`config.mode` to `'api'`. So after selecting `Use API · BYOK`, the
trigger kept showing the previously active local CLI provider's icon
instead of the BYOK glyph.
Gate the icon on `config.mode === 'daemon'`, matching the pattern this
same file (and InlineModelSwitcher's chip) already uses everywhere else
mode-dependent UI is rendered.
Fixes#5366
The .od-toast.tone-error used a color-mix background that was ~87% transparent,
causing the left chat pane to bleed through the error toast in the browser-assist
extraction flow. Replaced with the existing --red-bg / --red variables (already
used across the app for error surfaces) so the toast has a solid readable
background.
Both silent-stall cases pinned OD_CHAT_RUN_INACTIVITY_TIMEOUT_MS to 400ms,
which had to do two jobs with one budget: trip the no-output watchdog on the
first (silent) attempt, and NOT trip it on the retry before that fresh child
cleared node cold-start and emitted its first token. On a loaded host the
retry's cold-start could exceed 400ms, so it tripped its own watchdog and the
spec flaked (#5721; same watchdog path as #5292).
The first attempt stalls for ~60s, so any timeout trips it deterministically —
the value only needs to sit above any realistic subprocess cold-start. Raise it
to 3000ms via a shared, documented STALL_WATCHDOG_TIMEOUT_MS constant. For the
forced-shutdown case the escalation window (trip + grace) and the retry lifetime
(trip + backoff) are both anchored to the trip, so the overlap that makes it a
valid regression is independent of the timeout; only the retry's cold-start
margin changes.
Measured flake rate on this host: ~1/8 at 400ms, ~1/30 at 1500ms, and 0 across
40 runs of both stall cases + 15 runs of the full 5-test suite at 3000ms.
A production-side injectable clock would be the fully deterministic fix, but it
means threading a fake timer through the core run-lifecycle watchdog — too much
risk to take on for a test-reliability change; tracked in #5721.
Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
* fix(daemon): filter transient ACP status events at persistence time (#4618)
Transient ACP protocol-internal status labels (waiting_for_first_output,
tool_call, tool_call_update, session_update) carry no user-visible
detail. The web-side translateAgentEvent already normalizes these to
'running' for live display, but the daemon was persisting them as-is,
causing empty expandable rows in the assistant process panel on history
replay.
Suppress them at persistence time via TRANSIENT_ACP_PERSISTED_STATUS_LABELS
in daemonAgentPayloadToPersistedAgentEvent, matching the web-side
TRANSIENT_ACP_STATUS_LABELS set.
* fix(daemon): drop duplicated ACP helper block, add transient-status matrix test
- Remove duplicated helper block from server.ts (lines 1612‑1746)
- Delete stray .tmp.txt that adds an unrelated artifact to the repo
- Add matrix test to tool-loop-persistence.test.ts ensuring
waiting_for_first_output, tool_call, tool_call_update, session_update
return null while visible statuses still persist detail
- Permanently drop duplicated ACP helper functions from server.ts
- Use .forTest* utilities directly, no local re-exports needed
* fix(daemon): remove redeclared share-helpers constants (#5145)
PR description: follow-up to ce391b467 — remove the remaining
redeclared constants (githubRepoNameFromPluginName,
PLUGIN_SHARE_ACTION_LABELS, USER_PLUGIN_SOURCE_KINDS,
PLUGIN_CONTEXT_SKIP_DIRS) that are already imported from
'./plugins/share-helpers.js'. The @ts-nocheck directive allowed
these to exist silently, but they shadow the typed import
and create confusion during review.
* fix(daemon): remove remaining duplicate share-helpers block from server.ts
The previous commits left a ~270-line duplicate of plugin-share helper
implementations (normalizePluginShareAction, renderPluginSharePrompt,
copyPluginFolderForProjectContext, ensureGhReady,
reconcileAssistantMessageOnRunEnd, isPluginAuthoringRun,
hasGeneratedPluginArtifacts, assistantMessageEmittedQuestionForm,
deferredSkillPluginCandidateForRun,
detectSkillPluginCandidateOnRunSuccess,
upsertSkillPluginCandidateAssistantMessage) in server.ts, even though
all of them are already imported with proper types from
./plugins/share-helpers.js at the top of the file.
server.ts runs under // @ts-nocheck, so the duplicate bindings were
silent — but they shadow the typed imports and create two competing
sources of truth for the same logic. This removes the entire copied
block, keeping only shouldReportRunCompletedFromMessage and
telemetryPromptFromRunRequest (which are local to server.ts).
Addresses review feedback on PR #5145 (fix#4618).
* fix(deploy): honour target for Cloudflare Pages branch and URL selection (#4483)
- deployToCloudflarePages: add optional target ('preview'|'production', default 'production')
- Branch derivation: preview→'preview', production/omitted→'main'
- URL selection: preview uses per-deploy alias; production uses production root
- routes/deploy.ts: read target from request body (default 'production'), pass to deployToCloudflarePages, persist actual target (not hardcoded 'preview')
- db.ts normalizeDeployment: read target from DB row instead of hardcoding 'preview'
- contracts: widen target fields to 'preview'|'production'; add target to DeployProjectFileRequest
* test(deploy): type FormData branch capture as string | undefined (#4483)
The Phase-1 branch-capture objects were typed `{ branch?: string }` but
assigned `string | undefined`, which fails `tsc` under
`exactOptionalPropertyTypes`. Type the field as `branch: string | undefined`
so the daemon typecheck gate passes; assertions are unchanged.
* fix(deploy): reject invalid deploy target with HTTP 400 (#4576)
The deploy route coerced any non-'preview' target to 'production', so a
malformed value (typo, empty string, wrong case) silently published to the
production Cloudflare branch instead of a gated preview. Validate the supplied
target up front and return 400 for anything outside preview|production; an
omitted target still defaults to production (non-breaking).
Addresses review feedback on #4576 / #4483.
* feat(deploy): add `od deploy` CLI subcommand with --target (#4483)
Land the CLI surface for the preview/production deploy capability so it is
not API-only. `od deploy <projectId> --file <name> [--target preview|
production] [--provider vercel-self|cloudflare-pages] [--cf-zone-id]
[--cf-zone-name] [--cf-domain-prefix] [--json] [--daemon-url]` POSTs to the
same `/api/projects/:id/deploy` endpoint the web UI uses, with local
validation of `--target` before any request (omitted target = no key, server
defaults to production).
Addresses the surface-closure review feedback on #4576 by shipping the
embeddability (CLI) surface alongside the API contract. The human web-UI
preview/production selector remains tracked as a follow-up in #4577.
Tests: apps/daemon/tests/cli-deploy.test.ts (11 cases, written test-first).
* feat(deploy): add web UI target selector for Cloudflare Pages deploys
Closes the web-UI surface gap PerishCode flagged on #4576: deploy target
(preview/production) had HTTP + od CLI support but no way to choose it
from the deploy modal. Adds a target <select> to the Cloudflare Pages
deploy panel, defaulted to Preview, threaded through deployProjectFile's
new optional target argument and forwarded in the deploy POST body.
References #4576.
* fix(deploy): reject vercel-self target=production instead of silently deploying preview
POST /api/projects/:id/deploy resolved the requested target but only
threaded it into deployToCloudflarePages(); deployToVercel() never
received it and always behaved as preview. A caller explicitly
requesting target=production against the Vercel provider was silently
deployed as preview with no error (Codex P2 finding on PR #4576).
Real Vercel production-target support is out of scope for this PR, so
reject the explicit request with 400 BAD_REQUEST instead. The guard
gates on the raw requested value, not the resolved target, so an
omitted target (which defaults to 'production' for Cloudflare Pages
parity) keeps deploying Vercel as preview exactly as before.
* fix(web): default Cloudflare Pages deploy target to production
The deploy target selector defaulted to 'preview', both in its initial
useState and when syncing from the (currently placeholder,
always-'preview') GET /api/deploy/config response. A user who opened
the Cloudflare Pages deploy modal and clicked Deploy without touching
the selector silently deployed to preview instead of production,
contradicting the daemon's documented default for an omitted target
and pre-PR behavior.
Default both sites to 'production'; explicit user selection of either
target is untouched. FileViewer.deploy-target.test.tsx covers the
default and both explicit selections.
Fixes#4483
* fix(web): default community gallery to all types
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex)
* test(web): align community filter decoupling with all default
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex)
* fix(web): restore HTML project covers
Use shared project cover selection for recent and full project lists, render HTML covers from refreshable file URLs, and keep glyph fallback diagnostic when the HTML cover fails to load.
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex)
* fix(web): probe HTML project cover availability
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex)
* fix(byok): preserve provider model order
* test(e2e): expect current DeepSeek preset model
Generated-By: looper 0.10.4 (runner=fixer, agent=codex)
* fix(byok): preserve DeepSeek and Ollama defaults
Align DeepSeek e2e fixtures with the V4 preset defaults and keep the existing Ollama Cloud gpt-oss:120b default first in the canonical preference list.\n\nGenerated-By: looper 0.10.4 (runner=fixer, agent=codex)
* Fix Open Design example logo loading
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex)
* Stabilize visual home cold start checks
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex)
* fix(landing-page): emit localized plugins/templates/[kind] pages in production
The `[locale]/plugins/templates/[kind]` route imported `getStaticPaths`
from the sibling English `[kind]` page and re-invoked it. That cross-page
`getStaticPaths` re-export resolved to zero paths in the CI (Linux)
production build, so every `/<locale>/plugins/templates/<kind>/` URL
(deck, prototype, image, video, audio, live-artifact, hyperframes) 404'd
in production for all non-English locales — while the English routes and
the localized category index built fine. The language switcher on the
English category pages links straight into these 404s.
Derive the locale x category paths directly from PLUGIN_CATEGORIES (the
same source the English page's own getStaticPaths uses), mirroring the
self-contained `[locale]/plugins/[slug]` wrapper, so the route no longer
depends on the fragile cross-page getStaticPaths import.
* fix(landing-page): stop download function shadowing localized catalog pages
The attributed-download function `functions/[os]/[arch]/[token]/[asset].ts`
has a file-path route (`/[os]/[arch]/[token]/[asset]`) that structurally
matches ANY four-segment path. Pages Functions run before static-asset
serving, so it was intercepting the localized plugin catalog pages — e.g.
`/zh/plugins/templates/deck/` bound os=zh, arch=plugins, token=templates,
asset=deck — and returning `{"error":"download_not_found"}` (404) instead
of the real page. Every `/<locale>/plugins/templates/<kind>/` URL (the only
four-segment paths on the site) 404'd for all non-English locales, and the
language switcher on the English category pages links straight into them.
Real download URLs are minted only as `/{windows|macos|linux}/{arch}/{token}
/{asset}` (see `functions/api/attribution/mint.ts`), so gate the function on
the platform segment: anything whose first segment isn't a download platform
falls through to the static asset via `context.next()`. Download requests
are unaffected. Adds `next` to the local `PagesFunctionContext` type.
Paired with the localized `templates/[kind]` getStaticPaths fix in the same
branch, which makes those static pages exist in the first place.
---------
Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com>
The /careers/ page turned the apply address and every CTA into mailto:
links (hero button, the email itself, each role card, the closing button).
Clicking them yanked the visitor into their mail client, which felt abrupt.
Make the apply email a display-only element instead:
- hero + closing: render join@open-design.ai as a plain <span> (user-select:
all so one click selects the whole address to copy), no mailto
- role cards: <a mailto> → non-clickable <div>; drop the "Apply →" affordance
and the interactive hover lift
- enlarge the email (clamp(20px,2.6vw,26px), 700) so it reads as the CTA
- remove the now-dead copy fields (applyCta / applySubjectPrefix / roleApply)
from the interface and all 12 locales
astro check passes (0 errors/warnings); /careers/, /zh/careers/, /ja/careers/
render with zero mailto links.
Co-authored-by: joeylee12629-star <joeylee12629@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The blog index resolves card art from the `postImages` map, not from the
`/blog/<id>-cover.webp` convention the detail page uses. The two most
recent release posts had cover images but no map entry, so their cards
fell back to the generated line-art placeholder instead of their warm
editorial covers. Register both so the index shows the real artwork.
Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com>
prefetch/page.html was saved verbatim, so previewing a capture executed
the source site's live JavaScript from the local origin. Sites that
ship Google One Tap (Behance, Medium, LinkedIn, ...) made Chrome pop a
"127.0.0.1 — Sign in with Google" prompt whenever the capture or any
file built from it rendered in the studio; analytics beacons and
consent SDKs replayed the same way.
previewablePrefetchHtml now leads every script tag with a non-JS type
attribute (on duplicate attributes the HTML parser keeps the first, so
an original type="module" further right is inert). The markup stays
readable as extraction evidence; only execution is disabled.
Co-authored-by: wiggdevin <wiggdevin@gmail.com>
The deploySavedToast was rendered inline inside the component tree, so it
ended up underneath the deploy modal backdrop (z-index 1700). Portal it to
document.body like the adjacent deployActionToast, and raise the placement-top
toast z-index from 1200 to 1800 so toasts always render above active modals.
* feat(web): make Studio discovery visual and inline
Move clarification forms into the chat flow, add artifact-aware visual style choices, and keep follow-up actions hidden until discovery is complete.
* feat(web): keep clarification entirely in chat
Remove the legacy Questions workspace tab, preserve discovery analytics in the inline form, and keep Skip, Submit, and answered summaries in one conversation flow.
* docs: split Studio Chat input and response work
Frame visual discovery as the input-side initiative and record response aggregation as a separately reviewed follow-up so the current PR stays bounded.
* docs: reorganize Studio Chat experience plans
Separate the overall journey, visual discovery, and response architecture so each scope can be reviewed and delivered independently.
* docs: define first-artifact satisfaction prompt
Limit the feedback prompt to the first successful previewable artifact so it gathers a useful signal without interrupting every iteration.
* docs: keep Studio Chat plans independent
Preserve the existing discovery proposal as one document and keep response architecture as a separate review surface without an extra master layer.
* fix(web): preserve inline discovery behavior
Keep visual direction single-select while restoring custom input, optional auto-continue, analytics, and current end-to-end coverage for the inline flow.
* fix(web): preserve files when skipping discovery step
* fix(web): exclude skipped file answers
* fix(web): align inline discovery with slim prompt
Preserve the latest discovery contract while keeping inline forms, uploads, drafts, and schema-driven visual selections consistent after the rebase.
* feat(web): add style catalog preview images
* fix(web): clarify discovery form completion
* fix(web): preserve question form context
* fix(web): time out unanswered question forms
* test(web): cover inline question form next steps
* fix(web): roll back partial inline uploads
* ci: trigger validation after rebase
* fix(web): unlock blocked inline question submits
* fix(web): retain previewless visual answers
* fix(web): load visual style previews from R2
* fix(web): clean blocked inline uploads
* test(web): expect R2 style preview assets
* feat(web): show full visual style catalog
* fix(web): keep inline question answers consistent
---------
Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com>
attribution, restricted to agent-touched files resolved through
findTouchedProjectFile. Two residual gaps in that matching:
- A ROOT-level project file (candidate contains no '/') never
suffix-matches an absolute event path like /workspace/index.html, so a
mixed turn that rewrites index.html plus any resolvable file drops the
rewritten HTML from the touched set and nothing auto-opens.
- An out-of-root absolute path sharing a tail with a project file
(/tmp/site/index.html, or a '..' escape resolving outside the root)
CAN suffix-match and mis-attribute an external write.
findTouchedProjectFile now takes the project's resolvedDir: absolute
paths are lexically normalized ('.'/'..') and must sit under the root,
in which case they match by their project-relative form; out-of-root
paths (including '..' escapes) are rejected before any suffix matching.
Paths whose '..' climbs above their own anchor are always rejected.
Callers without a usable root keep the legacy behavior.
Red on main / green here:
apps/web/tests/components/ProjectView.touched-path-containment.test.ts
(6 of 10 cases fail on main: both root-file misses and out-of-root
false positives).
Claude-Session: https://claude.ai/code/session_017peXvs7Q3sA5dhZ6vsYYwN
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
A successful Design turn with zero produced files is only a missing
deliverable when the run actually attempted to mutate project files.
Image-analysis and report-only audit turns answer in prose without ever
calling a write tool; classify them as report_only and let them finish
as normal text results instead of appending ARTIFACT_NOT_FOUND.
- chatExtractionEnabled now defaults OFF (explicit opt-in only); it gates
the regex pack, chat-form profile capture, and LLM extractor together
while leaving memory injection untouched
- add a one-time boot migration (memory-cleanup.ts) that deletes regex-pack
artifacts by name+template fingerprint, prunes user_profile to the
canonical field set the settings editor writes, and forces the legacy
config flag off once; marker-gated so a conscious re-opt-in sticks
- persist writer provenance (frontmatter source:) on every new entry so
future audits can tell auto-extracted facts from hand-written ones
- LLM-extracted entries are deliberately kept: on-disk they are
indistinguishable from hand-written prose, and they are the least
harmful of the retired pipelines' output
Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>
* fix(daemon): scan user-authored text only and latch intent signals per conversation
The slim charter gates three stable-region prompt blocks on per-turn regex
signals scanned over (message, currentPrompt). For transcript-resending
clients `message` embeds assistant turns, so the turn-1 discovery form's own
option copy ("Slide deck / pitch", 幻灯/路演, iOS/Android/响应式) flipped
deck+platform at t2 on every form-driven conversation, and agent-switch
history trims flipped signals back OFF — each flip changes
stableInstructionFingerprint and re-sends the ~17K stable block
(stable-prompt-changed miss). Production: stablePromptCacheHit fell from
32.6% (0.14.1) to 20.4% (0.15.0).
- extractUserAuthoredSignalText: scan only `## user` section bodies of a
packed transcript (assistant sections and the context-warning block are
dropped); plain messages scan whole. `[form answers — *]` blocks narrow
to the value part of each answer line — the real task-type form's own
question label carries deck vocabulary.
- latchConversationIntentSignals + conversations.intent_signals_json
(guarded migration): effective = stored OR fresh, monotonic ON for the
life of the conversation.
- OD_INTENT_SIGNAL_MODE=legacy restores the pre-hotfix scan as an escape
hatch.
A genuine mid-conversation activation now costs exactly one
stable-prompt-changed miss; form/assistant-copy false flips cost zero.
* fix(daemon): run the intent-signal latch under BEGIN IMMEDIATE
Review hardening (#5709): take the write lock before the read so the
read+merge+write cannot lose a previously latched bit to another writer.
Within one daemon process the sequence was already non-interleavable
(better-sqlite3 is synchronous, no await between read and write), so this
pins the monotonic guarantee against future refactors and hypothetical
multi-connection writers. Adds a db-level contract suite for the latch:
monotonic merge (never replace), cross-connection merge on the same
database file, unparsable-JSON fallback, and missing-row passthrough.
* fix(daemon): accept the full form-answer header grammar in signal narrowing
Review fix (#5709): the narrowing header matcher only recognized the
canonical em-dash form, but the repo's shared grammar
(packages/contracts/src/artifacts/od-card.ts parseFormAnswers, the server
turn-2 override, and the CLI docs) also accepts hyphen / colon separators
and the bare [form answers] header, case-insensitively. A CLI-driven
hyphen-form payload skipped narrowing, so echoed question labels could
still flip intent signals. The matcher now mirrors the shared grammar and
the answer-line pattern aligns with the shared parser; parity across all
variants is pinned red→green in intent-signal-user-text.test.ts.
* fix(daemon): tolerate CRLF and quoted role headings in the signal extractor
Review fix (#5709, third pass): extractUserAuthoredSignalText switched
into transcript mode off any standalone role heading and only recognized
exact-LF marker lines, so two valid inputs extracted an empty body and
silenced every intent signal: a plain prompt that merely quotes a
`## assistant` heading, and a CRLF-packed transcript (multiline `$`
matches before `\r`, but the line-equality marker check does not).
Transcript mode now requires a real `## user` marker line, and marker
comparison strips a trailing CR. Both shapes are pinned red→green in
intent-signal-user-text.test.ts.
* fix(daemon): require a packed-transcript shape before role-scoped scanning
Review fix (#5709, fourth pass): a plain prompt that quotes a `## user`
heading mid-text still entered transcript mode, dropping everything
before the quote as pre-marker text and silencing explicit deck/media/
platform requests. Transcript mode now additionally requires the first
content line to be a role marker or the context-warning header — the
shape buildDaemonTranscript always emits — while transcripts that begin
with a trimmed `## assistant` section keep parsing as transcripts. Both
pinned red→green in intent-signal-user-text.test.ts.
---------
Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com>
* feat(landing): add 0.14.0 and 0.15.0 release blog posts
Cover the two most recent minor releases the blog was missing:
0.14.0 "Inspiration Time Machine" (plan mode, HTML version history,
design-system source context, model cost tiers) and 0.15.0 "Cost Less.
Ship Faster." (a leaner Design System Prompt — 49.5% faster to first
token, decks that drive like a workspace, website clone from a URL).
Each post follows the established release-note format: English body,
per-locale bodyHtml for all 10 shipped non-English locales, and cover
plus inline feature illustrations in the site's line-art house style.
* fix(landing): repair blog localization gaps found in an audit
A full-blog i18n audit surfaced pre-existing gaps in the localized
bodies (localizedBodyHtml fully replaces the Markdown body, so any gap
is invisible to English readers but real for everyone else):
- osaka-kyoto-meetup was English-only — add full 10-locale i18n.
- 0.9.0 translations dropped the inline figure the English body has —
restore it (localized figcaption) in all 10 locales.
- what-is-vibe-design, vibe-design-tools/-vs-/-with-stitch, and
open-source-alternative had their translated headings one level too
deep (## rendered as h3/h4) — normalize to match the English levels.
* style(landing): restyle 0.14.0/0.15.0 release art to warm editorial covers
The two new release posts shipped with the near-white line-art cover
style, which clashed with the warm editorial illustration covers used by
the rest of the recent blog cluster on the index. Regenerate both
covers and their inline feature figures in the warm sage-green editorial
style so the blog index reads as one consistent set, and update the
figure alt text to match the new artwork.
---------
Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com>
The top nav listed Product and Agent as sibling tabs. Product is really the
umbrella for what Open Design ships, so promote it to a multi-column mega
menu whose columns are top-level categories: a Products column (Open Design /
HTML Anything / HTML Video) and an Agent column (the coding agents, header
linking to the /agents/ hub). This declutters the bar and leaves room to add
more category columns (e.g. Feature) later without another top-level tab.
- Product trigger opens `.nav-dropdown-mega`: flex columns, each a category
with a small-caps head + list; the long Agent list caps its own height and
scrolls so the panel never runs off short viewports.
- Product now lights up for /agents/ pages too, since Agent lives inside it.
- Remove the standalone Agent top-level tab.
- Mobile drawer flattens the mega to stacked full-width categories and drops
the Agent scroll cap so the list flows naturally.
No new i18n keys — reuses the existing `product` / `agent` labels, so all 11
locales are covered. `astro check` passes with 0 errors.
Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com>