Status: Complete - Milestone: M25 (TypeDuck-Web dogfooding round 2) - Created: 2026-06-21 - Closed: 2026-06-21 - Type: archived issue ledger / execution record
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.Intake note: This plan starts the second manual dogfooding loop for the internal TypeDuck-Web playground. Add the user's upcoming feedback as
M25-DOGFOOD-*rows here before implementing fixes.
Goal: Capture, classify, and later execute the second real dogfooding feedback batch for the internal TypeDuck-Web playground without reopening completed M24 rows or weakening oracle-backed compatibility claims.
Architecture: M25 treats apps/yune-web/ as the browser dogfooding surface and keeps the browser app on Vite + React + Tailwind CSS + small local components. Browser and UI defects are fixed with Playwright evidence from the local /web/ app; engine-output or ranking defects require pinned TypeDuck v1.1.2 or upstream 1.17.0 fixtures before changing engine behavior. TypeDuck-Web source edits must be regenerated into the tracked patch before they count as landed.
Tech Stack: TypeDuck-Web React/TypeScript, Vite/Bun, Tailwind CSS, small local React components, Playwright, @yune-ime/typeduck-runtime, yune-rime-api C ABI, yune-core, TypeDuck v1.1.2 oracle fixtures, upstream librime 1.17.0 oracle fixtures.
M25 is the second dogfooding and hardening loop for the internal browser playground:
- In scope: manual play feedback from
http://localhost:5173/web/, first-run and reload behavior, schema switching, settings ergonomics, candidate panel layout, dictionary/detail panel readability, input scenarios, inspector/status usefulness, and local dogfood UI polish. - In scope with oracle evidence: any change to candidate text, candidate set, candidate order, segmentation, correction, prediction, reverse lookup, dictionary lookup payloads, or commit behavior.
- In scope for frontend stack maintenance: keep the dogfood demo on Vite + React + Tailwind CSS + small local components only. Do not add DaisyUI back and do not add another UI framework.
- Out of scope: editing the separately cloned or deployed
TypeDuck-HK/TypeDuck-Webproduct, treatingtypeduck.hk/webas the hard oracle, broad design-system work, widening the defaultRimeApi, or adding unsupported controls that only appear to work.
M24 is closed and archived at docs/plans/completed/m24-plan-typeduck-web-dogfooding.md. Do not edit M24 rows for new feedback.
Use M24 as the baseline for:
- local Tailwind-only component stack,
apps/yune-web/e2e/results/m24-dogfooding/as historical evidence only,- TypeDuck-Web patch discipline,
- the
jigaajiusihaaTypeDuckv1.1.2ordering fixture, - the
menu/page_sizecustomize key, - the Jyutping schema's Mandarin-pinyin affix path
`p...as historical M24 behavior;M25-DOGFOOD-08reopens that dogfood UX and requests bare`for theluna_pinyinreverse-lookup trigger insidejyut6ping3.
M25 evidence belongs under apps/yune-web/e2e/results/m25-dogfooding/<issue-id>/.
Classify every report before editing code:
| Classification | Use when | First evidence |
|---|---|---|
| Browser integration | Worker/runtime/assets/settings wiring fails or drifts from the intended browser contract. | Browser console logs, worker diagnostics, state JSON, screenshot. |
| UI polish | The rendered app is confusing, cramped, mislabeled, inaccessible, or inefficient, but engine output is correct. | Screenshot plus the exact interaction path. |
| Engine correctness | Candidate output, ranking, commit text, segmentation, correction, prediction, or reverse lookup seems wrong. | Pinned oracle fixture or a row marked blocked until fixture capture. |
| Unsupported / N/A | The report asks for behavior intentionally not exposed in the dogfood playground. | Short rationale and, if useful, a UI copy/docs change. |
| Future product integration | The report belongs to the real TypeDuck-Web product, not the internal Yune harness. | Product-track note; do not edit apps/yune-web/source/ unless the harness also needs it. |
| Needs triage | The symptom is not reproducible or does not yet identify the layer. | Screenshot/state capture and a narrow reproduction attempt. |
- Save browser evidence under
apps/yune-web/e2e/results/m25-dogfooding/<issue-id>/. - For every browser-visible fix, capture a screenshot or JSON/state snapshot from the real local
/web/app. - For every engine-output fix, add or extend a pinned oracle fixture before implementation.
- Do not use
https://www.typeduck.hk/web/as a hard oracle. It is a useful feel target only. - Keep completed M9/M13/M16/M20/M22/M24 gates green unless a row explicitly changes a supported contract with fresh evidence.
Use this section as the high-level review map. The running ledger below remains the source of truth for exact evidence paths, owning files, and close criteria.
| Execution group | Rows | Why this grouping matters |
|---|---|---|
| Performance and responsiveness blockers | M25-DOGFOOD-01, M25-DOGFOOD-03 |
These decide whether the app feels usable at all. Review first for measurement quality, startup phase attribution, and whether normal typing is separated from deploy/customize/loading work. |
| Candidate/settings correctness | M25-DOGFOOD-02, M25-DOGFOOD-08 |
These affect user-visible IME behavior. M25-DOGFOOD-02 must fix both the settings key path and rendered candidate cap; M25-DOGFOOD-08 intentionally gives bare ` to the luna_pinyin affix translator inside jyut6ping3, deletes the vestigial bare-grave reverse_lookup slot unless a real consumer is proven, and moves useful secondary lookups into a v-prefixed namespace such as `vl / `vc. |
| Top-control and settings layout | M25-DOGFOOD-04, M25-DOGFOOD-05, M25-DOGFOOD-06, M25-DOGFOOD-10 |
These should be implemented together only after the correctness blockers are understood, because they share App.tsx, Toolbar.tsx, SchemaSwitcher.tsx, and Preferences.tsx layout surfaces. |
| Control affordance polish | M25-DOGFOOD-07, M25-DOGFOOD-09 |
These are local component presentation changes. They can be batched if tests preserve the individual checkbox and radio close criteria. |
Claude review should focus on four questions:
- Is the startup plan still aimed at the validated P0 suspects: debug browser WASM and forced schema invalidation before lower-level micro-optimization?
- Does the page-size row protect both
menu/page_sizebrowser/runtime wiring and the candidate panel's rendered row cap? - Is the reverse-lookup row clear that bare
`is reserved for theluna_pinyinaffix translator insidejyut6ping3, that the current bare-gravereverse_lookuptag is vestigial unless proven otherwise, and that retained secondary lookups need explicit non-bare triggers plus visible UI copy? - Can the UI rows be batched by touched surfaces without losing per-row browser evidence and close criteria?
apps/yune-web/source/ is gitignored in the Yune repository. Local edits there are allowed for development, but a M25 row is not closed until the matching tracked artifacts are updated.
Before closing any row that changes TypeDuck-Web source:
-
Regenerate
apps/yune-web/patches/yune-web-runtime.patchfrom the patched upstream checkout. -
Reverse-check from
apps/yune-web/source/:git apply --reverse --check ..\patches\yune-web-runtime.patch
-
Forward-check the patch on a clean source checkout reset to
apps/yune-web/yune-web.lock.json. -
Stage only the tracked artifacts for the slice: the patch, Yune-owned integration files, Playwright tests/evidence, Rust/runtime files, docs, and lock metadata when the upstream source pin changes.
M25 intake began on 2026-06-21 with user-reported browser dogfooding regressions. Keep adding one row per distinct user-visible symptom using the next M25-DOGFOOD-XX id.
| ID | Status | Classification | User-visible issue | First repro / evidence | Owning surfaces to inspect first | Close criteria |
|---|---|---|---|---|---|---|
| M25-DOGFOOD-01 | Open | Browser integration / performance | Refreshing http://localhost:5173/web/ still leaves 載入中 Loading... visible for too long; the user sees no practical improvement compared with the pre-M24 app and considers this unacceptable for real users. |
Reproduced in the in-app browser on 2026-06-21: reload took 47.752s; startup marker reported totalMs=47331, with runtime:initialized consuming nearly the entire delay after assets loaded. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-01/reload-startup-repro.json. |
apps/yune-web/source/src/worker.ts, apps/yune-web/source/src/yune-integration/adapter.ts, apps/yune-web/source/src/yune-integration/assets.ts, packages/yune-typeduck-runtime/src/typeduck.ts, packages/yune-typeduck-runtime/src/module.ts, packages/yune-typeduck-runtime/src/filesystem.ts, crates/yune-rime-api/src/typeduck_web.rs, schema deploy/init paths under crates/yune-rime-api/src/. |
Close only after a measured startup optimization lands. Add a Playwright startup budget test that records phase timings, preserve startup:complete diagnostics, and capture before/after evidence under this issue id. Target: interactive shell visible quickly and warm reload IME readiness materially below the current ~47s baseline; any chosen budget must be written into the test and justified by local evidence. |
| M25-DOGFOOD-02 | Open | Browser integration / settings and candidate pagination | The page-size control is hard to find after M24 UI changes, its allowed range is wrong for the requested behavior, and changing it still does not cap the rendered candidate list. The user expects a visible slider/control allowing 3-10 candidates, where setting 9 shows exactly 9 visible candidates. | User screenshot showed hai rendering far more than 10 rows. Reproduced in the in-app browser on 2026-06-21: DOM had page-size slider min=4, max=10, value=6, but typing hai rendered 50 visible candidate rows. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-02/page-size-hai-repro.json. |
apps/yune-web/source/src/Preferences.tsx, apps/yune-web/source/src/Inputs.tsx, apps/yune-web/source/src/CandidatePanel.tsx, apps/yune-web/source/src/App.tsx, apps/yune-web/source/src/yune-integration/adapter.ts, crates/yune-rime-api/src/typeduck_web.rs, crates/yune-rime-api/src/context_api.rs, crates/yune-rime-api/tests/typeduck_web.rs, apps/yune-web/e2e/yune-typeduck.spec.ts. |
Close when the UI exposes an obvious 3-10 page-size control and the rendered candidate panel never exceeds the configured page size. Add/extend native typeduck_web coverage for menu/page_size at 3 and 9, add Playwright coverage that sets 3/9/10 and types hai, verify page navigation still works, regenerate and reverse/forward check apps/yune-web/patches/yune-web-runtime.patch, and capture browser JSON/screenshot evidence under this issue id. |
| M25-DOGFOOD-03 | Open | Browser integration / typing responsiveness | Typing in the textbox can still show the global 載入中 Loading... state and can stall for about a second when entering a character. This makes the dogfood IME feel blocked even after the page becomes visible. |
User-reported during manual dogfooding on 2026-06-21. Evidence placeholder with exact report: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-03/typing-latency-user-report.json. Needs measured browser repro with keydown-to-candidate timing. |
apps/yune-web/source/src/CandidatePanel.tsx, apps/yune-web/source/src/rime.ts, apps/yune-web/source/src/worker.ts, apps/yune-web/source/src/App.tsx, apps/yune-web/source/src/Toolbar.tsx, apps/yune-web/source/src/yune-integration/adapter.ts, packages/yune-typeduck-runtime/src/typeduck.ts, crates/yune-rime-api/src/typeduck_web.rs, crates/yune-core/src/engine.rs. |
Close only after per-key latency is measured and improved. Add Playwright instrumentation for keydown-to-candidate update latency on hai, nei, and a long phrase; split queue wait, worker roundtrip, Rust process_key, and React render time; remove global loading from normal per-key composition; ensure typing remains responsive while startup/deploy/customize is in flight; save before/after latency JSON under this issue id. |
| M25-DOGFOOD-04 | Open | UI polish / schema switcher layout | The schema list sits in its own vertical block below the three mode buttons, wasting vertical space. The user wants the schema list moved into the same top row as the three buttons. The luna_pinyin option is also currently shown as 普通話, but the user expects the schema name 朙月拼音. |
User-reported during manual dogfooding on 2026-06-21. Local verification found upstream 1.17.0 luna_pinyin.schema.yaml uses 朙月拼音, while the current TypeDuck-Web source and TypeDuck v1.1.2 captured schema use 普通話. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-04/schema-switcher-toolbar-and-luna-name-report.json. |
apps/yune-web/source/src/App.tsx, apps/yune-web/source/src/Toolbar.tsx, apps/yune-web/source/src/SchemaSwitcher.tsx, apps/yune-web/source/src/consts.ts, apps/yune-web/source/src/Inputs.tsx, apps/yune-web/e2e/yune-typeduck.spec.ts. |
Close when the schema selector is part of the top control row with the three mode buttons on desktop, wraps within the same compact control band on narrow screens, and luna_pinyin is labeled 朙月拼音 as the primary user-facing schema name unless the row is explicitly revised with TypeDuck-v1.1.2-specific rationale. Add Playwright evidence covering desktop and mobile layout, active schema switching, and the visible Luna label; regenerate and reverse/forward check apps/yune-web/patches/yune-web-runtime.patch if source changed. |
| M25-DOGFOOD-05 | Open | UI polish / top controls layout | The 倉頡版本 Cangjie version control currently lives in a separate lower Web Frontend Controls section, but it is tightly related to schema choice. The user wants it moved to the top beside the schema selection so the page does not spend vertical space on a one-control section. |
User-reported during manual dogfooding on 2026-06-21. Local verification found the control in apps/yune-web/source/src/Preferences.tsx under Web Frontend Controls, using prefs.isCangjie5 with 三代 Version 3 and 五代 Version 5 segments. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-05/cangjie-version-top-controls-report.json. |
apps/yune-web/source/src/App.tsx, apps/yune-web/source/src/Toolbar.tsx, apps/yune-web/source/src/SchemaSwitcher.tsx, apps/yune-web/source/src/Preferences.tsx, apps/yune-web/source/src/Inputs.tsx, apps/yune-web/e2e/yune-typeduck.spec.ts. |
Close when the Cangjie version segmented control is colocated with the schema selector in the top control band on desktop and wraps within that same compact band on narrow screens, the lower Web Frontend Controls section is removed if it has no remaining controls, and changing 三代/五代 still updates the isCangjie5 customize path. Add Playwright evidence for layout and both Cangjie version values; regenerate and reverse/forward check apps/yune-web/patches/yune-web-runtime.patch if source changed. |
| M25-DOGFOOD-06 | Open | UI polish / settings section order | The 顯示設定 Display controls section is currently in the second-row right column. The user wants it in the top-row right column, meaning 顯示設定 Display controls and 即時狀態 Live session controls should exchange positions. |
User-reported during manual dogfooding on 2026-06-21. Local verification found Preferences.tsx renders 即時狀態 Live session controls before 顯示設定 Display controls, causing Display controls to land below the top-right slot in the two-column settings grid. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-06/display-live-section-order-report.json. |
apps/yune-web/source/src/Preferences.tsx, apps/yune-web/source/src/Inputs.tsx, apps/yune-web/e2e/yune-typeduck.spec.ts. |
Close when Display controls render before Live session controls in the settings grid, occupy the top-right column on desktop, and Live session controls move to the position Display controls previously occupied. Preserve all existing control labels, state bindings, and live-option behavior. Add Playwright desktop and narrow-viewport evidence for the section order and bounding boxes; regenerate and reverse/forward check apps/yune-web/patches/yune-web-runtime.patch if source changed. |
| M25-DOGFOOD-07 | Open | UI polish / binary control affordance | Binary settings are currently shown as rounded rectangle pill switches that fill blue when checked. The user finds this unintuitive and wants these binary toggles to be raw checkbox-style controls instead. | User-reported during manual dogfooding on 2026-06-21 with screenshot codex-clipboard-da4df560-7b7a-49fd-814f-4cfe5cbd6968.png. Local verification found Toggle renders input type="checkbox" with className="yd-switch" in Inputs.tsx, and .yd-switch applies the pill styling in index.css; .yd-check already exists as a square checkbox style. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-07/raw-checkbox-binary-controls-report.json. |
apps/yune-web/source/src/Inputs.tsx, apps/yune-web/source/src/index.css, apps/yune-web/source/src/App.tsx, apps/yune-web/source/src/ThemeSwitcher.tsx, apps/yune-web/source/src/Preferences.tsx, apps/yune-web/e2e/yune-typeduck.spec.ts. |
Close when binary dogfood controls render as checkbox-style controls rather than pill switches, checked/unchecked states remain clear and keyboard-accessible, and no settings-panel binary control uses the blue rounded-pill affordance. Decide explicitly whether the app theme toggle remains a specialized icon switch or also moves to checkbox styling. Add Playwright evidence for checked and unchecked states across engine/session/display/frontend settings plus the inspector toggle; regenerate and reverse/forward check apps/yune-web/patches/yune-web-runtime.patch if source changed. |
| M25-DOGFOOD-08 | Open | Browser integration / reverse lookup schema trigger | Mandarin luna_pinyin reverse lookup inside the jyut6ping3 schema currently appears to require `p. The user clarified that the single grave accent ` should trigger the luna_pinyin affix translator. The current bare-grave reverse_lookup tag is vestigial in the desktop schema because it has no translator/dictionary, so it should be deleted unless a real consumer is proven. Useful secondary lookup paths such as Loengfan and Cangjie should move out of the bare/Mandarin namespace, preferably to v-prefixed triggers such as `vl and `vc; avoid single-letter prefixes that shadow normal pinyin initials. The web UI must explicitly show which trigger maps to which lookup/schema. While trying reverse lookup, the app is also slow and can show 執行操作時發生錯誤。如輸入法不能正常運作,請重新載入頁面。 / An error occurred while performing the operation...; treat that performance/error symptom as related to M25-DOGFOOD-01/M25-DOGFOOD-03 unless a fresh repro proves a distinct reverse-lookup crash. |
User-reported during manual dogfooding on 2026-06-21, then clarified after external review on 2026-06-21. Local verification found apps/yune-web/source/schema/jyut6ping3.schema.yaml has a current vestigial bare-grave reverse_lookup tag with no translator/dictionary, while functional luna_pinyin, loengfan, and cangjie lookups use `p, `l, and `c respectively; jyut6ping3_mobile.schema.yaml only exposes the current `p luna_pinyin path. The TypeDuck v1.1.2 captured schema also has `p, so this is a documented M25 web-profile divergence rather than an oracle match. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-08/reverse-lookup-bare-grave-trigger-report.json. |
apps/yune-web/source/schema/jyut6ping3.schema.yaml, apps/yune-web/source/schema/jyut6ping3_mobile.schema.yaml, apps/yune-web/source/src/consts.ts, apps/yune-web/source/src/SchemaSwitcher.tsx, crates/yune-rime-api/src/schema_install.rs, crates/yune-rime-api/tests/typeduck_web.rs, apps/yune-web/e2e/yune-typeduck.spec.ts. |
Close when the web Jyutping schema uses bare ` for luna_pinyin reverse lookup; the vestigial bare-grave reverse_lookup tag is removed or justified with a concrete non-bare prefix; retained Loengfan/Cangjie side lookups use explicit non-bare triggers such as `vl / `vc; browser evidence shows typing `zhe reaches luna_pinyin without needing p; native/browser tests prove pinyin initials such as lai, ci, xi, and re are not stolen by secondary lookups; and the web UI lists the lookup triggers per active schema. Document the deliberate divergence from TypeDuck v1.1.2 `p behavior, update visible hints, capture browser JSON/screenshot evidence, and regenerate plus reverse/forward check apps/yune-web/patches/yune-web-runtime.patch if source changed. |
| M25-DOGFOOD-09 | Open | UI polish / control affordance | The 候選排版 Candidate Menu Layout setting currently uses a segmented control. The user wants it changed to a radio selection. |
User-reported during manual dogfooding on 2026-06-21. Local verification found Preferences.tsx maps CANDIDATE_MENU_LAYOUT_LABELS into Segment controls for prefs.candidateMenuLayout; Inputs.tsx already provides a Radio component. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-09/candidate-layout-radio-report.json. |
apps/yune-web/source/src/Preferences.tsx, apps/yune-web/source/src/Inputs.tsx, apps/yune-web/source/src/consts.ts, apps/yune-web/e2e/yune-typeduck.spec.ts. |
Close when Candidate Menu Layout renders as radio choices, not a segmented group; both horizontal and vertical options remain labeled, keyboard-accessible, and bound to candidateMenuLayout; switching options still changes the candidate panel layout. Add Playwright evidence for selecting both options and capturing the resulting panel layout; regenerate and reverse/forward check apps/yune-web/patches/yune-web-runtime.patch if source changed. |
| M25-DOGFOOD-10 | Open | UI polish / layout alignment | The 輸入法設定 IME Settings section appears to use a different horizontal margin than the playground section above it. The user wants the settings block aligned with the content above so the page columns share the same left/right edges. |
User-reported during manual dogfooding on 2026-06-21. Local verification found App.tsx renders the main playground inside <main className="m-auto p-8 max-w-7xl">, while Preferences.tsx wraps IME Settings in a nested <section className="mx-auto max-w-6xl">, making settings narrower than the preceding content stack. Evidence: apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-10/ime-settings-margin-alignment-report.json. |
apps/yune-web/source/src/App.tsx, apps/yune-web/source/src/Preferences.tsx, apps/yune-web/source/src/index.css, apps/yune-web/e2e/yune-typeduck.spec.ts. |
Close when the IME Settings heading and settings grid align horizontally with the content above on desktop and narrow viewports. Add Playwright evidence comparing bounding boxes for the playground content and IME Settings wrapper; left and right edge differences should be within 2 px unless a documented scrollbar/device-pixel reason requires a slightly wider tolerance. Preserve the settings grid behavior from M25-DOGFOOD-06 and regenerate plus reverse/forward check apps/yune-web/patches/yune-web-runtime.patch if source changed. |
M25-DOGFOOD-01startup work must address the two validated P0 suspects before broader micro-optimization: the browser WASM artifact is currently built fromtarget/wasm32-unknown-emscripten/debug, and the TypeDuck-Web adapter forces deploy freshness by callinginvalidateDeployedSchema(...)beforecurrentRuntime.deploy(). The review evidence is tracked atapps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-01/claude-review-p0-notes.json.- Do not treat fast asset fetches as proof that startup is network-bound. The current reproduction showed assets loading in under 100 ms while
runtime:initializedconsumed about 47 seconds, so Task 2 must re-measure release WASM and deploy reuse before setting the final warm-reload budget. M25-DOGFOOD-08follow-up review found that desktopjyut6ping3's current bare-gravereverse_lookuptag has no translator/dictionary and is not a functional Jyutping lookup. The M25 decision is therefore: bare`belongs toluna_pinyin; useful secondary lookups such as Loengfan/Cangjie should move to an explicit non-bare namespace, preferablyv-prefixed triggers such as`vland`vcto avoid shadowing normal pinyin initials; the web UI must show which trigger maps to which lookup.
All M25 rows closed on 2026-06-21. The intake rows above are preserved as the original report text; this closeout table is the authoritative status, owning-test, and evidence index for the completed ledger.
| Row | Status | Owning tests | Evidence |
|---|---|---|---|
| M25-DOGFOOD-01 | Closed | M25 DOGFOOD-01 startup uses release wasm and records deploy cache reuse; npm.cmd --prefix apps/yune-web/source run build |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-01/startup-after.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-01/screenshot-startup-ready.png |
| M25-DOGFOOD-02 | Closed | typeduck_adapter_real_assets_page_size_customize_limits_context_page; M25 DOGFOOD-02 page-size slider caps rows and preserves candidate paging |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-02/page-size-summary.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-02/screenshot-page-size-9-hai.png |
| M25-DOGFOOD-03 | Closed | M25 DOGFOOD-03 typing stays out of global loading and records key latency |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-03/typing-latency-after.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-03/screenshot-typing-responsive.png |
| M25-DOGFOOD-04 | Closed | M25 DOGFOOD-04 schema switcher shares the top control row |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-04/schema-switcher-toolbar-desktop.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-04/screenshot-schema-switcher-toolbar-desktop.png; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-04/screenshot-schema-switcher-toolbar-mobile.png |
| M25-DOGFOOD-05 | Closed | M25 DOGFOOD-05 Cangjie version control lives in the top control band |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-05/cangjie-version-top-controls.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-05/screenshot-cangjie-version-top-controls.png |
| M25-DOGFOOD-06 | Closed | M25 DOGFOOD-06 display controls precede live session controls |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-06/display-live-section-order-desktop.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-06/screenshot-display-live-section-order-desktop.png; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-06/screenshot-display-live-section-order-mobile.png |
| M25-DOGFOOD-07 | Closed | M25 DOGFOOD-07 binary controls use checkbox affordance |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-07/checkbox-affordance-summary.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-07/screenshot-checkbox-affordance-desktop.png; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-07/screenshot-checkbox-affordance-mobile.png |
| M25-DOGFOOD-08 | Closed | typeduck_adapter_browser_app_assets_load_jyutping_mandarin_pinyin_reverse_lookup; M25 DOGFOOD-08 Jyutping bare grave routes to Luna reverse lookup |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-08/reverse-lookup-summary.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-08/bare-grave-zhe-state.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-08/screenshot-bare-grave-zhe.png |
| M25-DOGFOOD-09 | Closed | M25 DOGFOOD-09 candidate menu layout uses radio choices |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-09/candidate-layout-horizontal.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-09/candidate-layout-vertical.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-09/screenshot-candidate-layout-horizontal.png; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-09/screenshot-candidate-layout-vertical.png |
| M25-DOGFOOD-10 | Closed | M25 DOGFOOD-10 IME settings align with the playground content edges |
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-10/settings-alignment-desktop.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-10/settings-alignment-mobile.json; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-10/screenshot-settings-alignment-desktop.png; apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-10/screenshot-settings-alignment-mobile.png |
Patch discipline evidence: apps/yune-web/patches/yune-web-runtime.patch was regenerated from apps/yune-web/source/ and checked with both reverse and forward git apply --check paths after source changes.
Files:
-
Modify:
docs/plans/m25-plan-typeduck-web-dogfooding-round-2.md -
Create evidence as needed:
apps/yune-web/e2e/results/m25-dogfooding/<issue-id>/ -
Step 1: Split the feedback into distinct symptoms
Treat each independently reproducible behavior as one row. If two comments share one root cause but have different user-visible symptoms, keep separate rows until triage proves they should close together.
-
Step 2: Assign stable ids
Use
M25-DOGFOOD-01,M25-DOGFOOD-02, and so on. Do not renumber rows after they are referenced by evidence, commits, or tests. -
Step 3: Classify each row before implementation
Use one of the classifications in this plan. Mark ambiguous reports as
Needs triage; do not force them into browser or engine buckets prematurely. -
Step 4: Name the first evidence path
For browser reports, record the intended evidence directory, for example:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-01/For engine reports, record the fixture family first:
typeduck-v1.1.2for TypeDuck profile behavior orupstream-1.17.0for default upstream behavior. -
Step 5: Name the owning surfaces
List the files or test families to inspect first. For TypeDuck-Web source changes, include the
source/file and the tracked patch requirement. -
Step 6: Write close criteria that can be verified
Each row needs concrete close criteria: owning test, evidence path, patch regeneration if applicable, and the focused gate to run.
Files:
-
Modify:
Cargo.toml -
Modify:
scripts/typeduck-wasm-build.sh -
Modify:
apps/yune-web/source/src/worker.ts -
Modify:
apps/yune-web/source/src/yune-integration/adapter.ts -
Modify if profiling still points there:
packages/yune-typeduck-runtime/src/typeduck.ts -
Modify if profiling still points there:
packages/yune-typeduck-runtime/src/filesystem.ts -
Modify if profiling still points there:
crates/yune-rime-api/src/typeduck_web.rs -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-01/ -
Step 1: Preserve the failing startup characterization and artifact facts
Keep the current reload evidence as the baseline, then add a Playwright startup budget characterization that reloads
/web/, waits fordocument.documentElement.dataset.yuneLoading === "false", captures the lateststartup:completediagnostic, and writesm25-dogfooding/M25-DOGFOOD-01/startup-before.json. Also record the shipped.wasmsize, whether the build came fromdebugorrelease, and whether Emscripten/wasm optimization flags were applied. The first test may fail against the current ~47s local baseline, but the final budget should be chosen after the P0 fixes below are measured. -
Step 2: Ship release-mode browser WASM first
Update the TypeDuck-Web build path so the browser artifact is not copied from
target/wasm32-unknown-emscripten/debug. Add conservative release tuning inCargo.tomlfor the existing workspace (opt-level = 3,lto = true,codegen-units = 1,panic = "abort", andstrip = trueif supported by the active toolchain), updatescripts/typeduck-wasm-build.shto buildtypeduck_web_modulewith--release, copy fromtarget/wasm32-unknown-emscripten/release, and add Emscripten optimization flags such as-O3or-Ozto the link path. Runwasm-opt -O3only when available and record whether it was skipped. Preserve the existing JS glue contract:createYuneTypeduckModule,cwrap,UTF8ToString,FS, andIDBFSmust still exist after the build. -
Step 3: Stop rebuilding/deploying fresh state on every reload
Remove or guard the unconditional
invalidateDeployedSchema(currentFs, currentPrepareOptions)call beforecurrentRuntime.deploy(). Do not simply delete the safety check; replace it with freshness validation based onassetVersion, schema/resource checksums, or another deterministic deploy stamp. Persist and reuse/rime/buildthrough IDBFS when fresh. Ifjyut6ping3_mobilestill spends startup time generating stable table/reverse artifacts, ship or prefer precompiled artifacts only after adding fixture-backed evidence that the resulting candidates and paging behavior remain unchanged. -
Step 4: Re-measure after the P0 fixes before choosing deeper work
Rebuild the patchable TypeDuck-Web source, reload
/web/, and savestartup-after-release-wasm.jsonandstartup-after-deploy-reuse.jsonas applicable. Comparestartup:complete.totalMs, the user-visible loading duration,.wasmsize, and deploy/cache diagnostics againstreload-startup-repro.json. The final warm-reload budget must be written from this measured optimized baseline, not guessed from the pre-fix 47s number. -
Step 5: Add finer
runtime:initializedphase markers only if neededIf release WASM plus deploy reuse do not meet the startup budget, split
runtime:initializedinto nested markers aroundTypeDuckRuntime.init, filesystem mount/sync, schema deploy, default schema selection, dictionary/table loading, and persistence sync. The next evidence file must show which sub-phase owns the remaining delay. -
Step 6: Keep startup latency separate from typing latency
Startup optimization must not hide the separate typing-stutter problem. If typing is still blocked while startup continues, keep
M25-DOGFOOD-03open and measure it separately instead of claiming startup fixed the perceived performance problem. -
Step 7: Prove the improvement and regenerate the TypeDuck-Web patch if source changed
Re-run the startup test, save the final
startup-after.json, and comparestartup:complete.totalMsplus the visible loading duration against the baseline evidence. The final row update must state before/after timings, the release/deploy evidence paths, and any remaining bottleneck. If any file underapps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/src/Preferences.tsx -
Modify if needed:
apps/yune-web/source/src/Inputs.tsx -
Modify if needed:
apps/yune-web/source/src/CandidatePanel.tsx -
Modify if needed:
apps/yune-web/source/src/yune-integration/adapter.ts -
Modify if needed:
crates/yune-rime-api/src/typeduck_web.rs -
Test:
crates/yune-rime-api/tests/typeduck_web.rs -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-02/ -
Step 1: Add failing native page-size coverage for 3 and 9
Extend
typeduck_adapter_real_assets_page_size_customize_limits_context_pageor add a focused companion test that customizesmenu/page_sizeto3and9, types a high-candidate input such ashai, and assertscontext.page_sizeandcontext.candidates.len()match the requested cap. -
Step 2: Add failing browser coverage for the visible control
Add a Playwright test that locates the page-size control by its Cantonese/English label, asserts the range is
3through10, sets it to9, typeshai, and asserts exactly 9 visible candidate rows. Repeat with3to protect the lower bound. -
Step 3: Fix discoverability and the allowed range
Keep the control in the settings UI, but make it visibly discoverable in the candidate/display area and set its range to
3..10. The visible value must update immediately when the slider changes. -
Step 4: Fix the runtime cap
Trace the browser response after
Rime.customize({ pageSize }). If nativetypeduck_webreturns a pagedcontext.candidatesarray but the browser renders all candidates, fixCandidatePanelor the adapter mapping. If native returns too many candidates formenu/page_size, fixtypeduck_web.rsorcontext_api.rs. Do not hide extra rows with CSS while leaving selection/page navigation semantics wrong. -
Step 5: Prove paging and selection still work
The browser test must verify first-page row count, next/previous page behavior, and digit selection after the cap is applied. Save JSON and screenshots under
M25-DOGFOOD-02. -
Step 6: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/src/CandidatePanel.tsx -
Modify:
apps/yune-web/source/src/rime.ts -
Modify:
apps/yune-web/source/src/worker.ts -
Modify:
apps/yune-web/source/src/App.tsx -
Modify:
apps/yune-web/source/src/Toolbar.tsx -
Modify if profiling points there:
apps/yune-web/source/src/yune-integration/adapter.ts -
Modify if profiling points there:
packages/yune-typeduck-runtime/src/typeduck.ts -
Modify if profiling points there:
crates/yune-rime-api/src/typeduck_web.rs -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-03/ -
Step 1: Add keypress latency instrumentation
Add temporary or permanent diagnostics that record
keydowntime,Rime.processKeyqueued time, worker receive time, Rust response time, page receive time, and candidate-panel render time. Save a browser JSON baseline forh,ha,hai,nei, andjigaajiusihaaunderM25-DOGFOOD-03. -
Step 2: Add a failing Playwright responsiveness test
Add a Playwright test that types into the real textarea using actual keypresses and asserts an explicit latency budget for keydown-to-candidate update. The first version should fail or record the current bad p95/p99 latency so the implementation has a hard target.
-
Step 3: Separate global loading state from normal composition
載入中 Loading...should mean startup, schema deploy, or settings redeploy, not ordinary per-key composition. Split loading state inApp.tsx/Toolbar.tsx/rime.tsso normalprocessKey,stageAi, page flip, and candidate selection do not keep the global loading indicator active. If an action needs a local pending state, show it near that control instead of blocking the textbox. -
Step 4: Avoid queueing keystrokes behind long non-key actions
Inspect the
rime.tssingle-message queue. IfprocessKeymessages wait behind startup/customize/deploy, split high-priority key events from low-priority settings work, coalesce stale option/customize calls, or block typing until the IME is truly ready with an explicit disabled state. Do not let the user type into an apparently live IME while key events are silently delayed. -
Step 5: Reduce the slowest measured per-key path
If latency is dominated by Rust
process_key, profileyune_typeduck_process_keyand candidate serialization. If latency is dominated by rendering, cap rendered rows throughM25-DOGFOOD-02and avoid rebuilding expensive dictionary/detail data for every row. If latency is dominated by AI staging, ensure AI remains second-pass, cancellable/stale-result guarded, and default-off. -
Step 6: Prove typing responsiveness after the fix
Re-run the browser responsiveness test and save
typing-latency-after.jsonwith before/after p50/p95/p99 timings. The final evidence must show the textbox accepts keypresses without a visible one-second stall and the global loading indicator does not appear for normal composition. -
Step 7: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/src/App.tsx -
Modify:
apps/yune-web/source/src/Toolbar.tsx -
Modify:
apps/yune-web/source/src/SchemaSwitcher.tsx -
Modify:
apps/yune-web/source/src/consts.ts -
Modify if segment markup needs shared layout changes:
apps/yune-web/source/src/Inputs.tsx -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-04/ -
Step 1: Preserve the schema-name verification
Keep
schema-switcher-toolbar-and-luna-name-report.jsonas the issue baseline. The implementation should treat the current web label普通話as a UI/source mismatch to resolve, not as proof that the user-facing label is correct. Upstream1.17.0namesluna_pinyinas朙月拼音; TypeDuck v1.1.2 names it普通話, so any decision to keep普通話must be documented in this row with explicit TypeDuck-specific rationale. -
Step 2: Add failing browser layout coverage
Add a Playwright test that loads
/web/at a desktop viewport and asserts the schema switcher belongs to the same top control band as the ASCII/simplification/full-shape buttons. The test should check that[data-yune-schema-switcher]is inside or directly owned by the toolbar/top-controls component, the top edges of the three mode buttons and schema segments are aligned within one compact row, and no separate standalone schema block appears between the toolbar and status strip. Add a narrow viewport assertion that the same controls wrap within one compact band instead of becoming a separate full-width section. -
Step 3: Move the schema switcher into the toolbar row
Refactor
Toolbar.tsxto acceptactiveSchemaandsetActiveSchema, renderSchemaSwitchernext to the three mode buttons, and keep the loading indicator in the same top control area without pushing the textarea downward. Remove the standalone<SchemaSwitcher ... />call fromApp.tsx. Keep accessible labels for each schema option and preserve the existingdata-yune-schema-switcherhook for tests. -
Step 4: Make the Luna label explicit
Update
SCHEMA_OPTIONSinconsts.tssoluna_pinyinuses朙月拼音as the primary visibleschemaName. KeepLuna PinyinorMandarinonly as a secondary label or tooltip if useful. Do not change engine output/ranking behavior for this UI-only row. -
Step 5: Prove schema switching still works
In the browser test, switch from
jyut6ping3_mobiletoluna_pinyinand back, assert the visible active option changes, assert the Luna option text includes朙月拼音, and type a short smoke input after switching to prove the selector still drives the worker path. -
Step 6: Capture visual evidence
Save desktop and narrow-viewport screenshots plus a JSON summary under
M25-DOGFOOD-04. The JSON should include viewport size, active schema before/after switching, bounding boxes for the three mode buttons and schema switcher, and the visible text for the Luna option. -
Step 7: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/src/App.tsx -
Modify:
apps/yune-web/source/src/Toolbar.tsx -
Modify:
apps/yune-web/source/src/SchemaSwitcher.tsxif the schema row becomes the shared top-control layout -
Modify:
apps/yune-web/source/src/Preferences.tsx -
Modify if segment markup needs shared compact styling:
apps/yune-web/source/src/Inputs.tsx -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-05/ -
Step 1: Preserve the Cangjie-version baseline
Keep
cangjie-version-top-controls-report.jsonas the issue baseline. The current control lives inPreferences.tsxunderWeb Frontend Controls, usesprefs.isCangjie5, and exposes三代 Version 3plus五代 Version 5. The implementation must move this existing control, not create a second independent setting. -
Step 2: Add failing browser layout coverage
Add a Playwright test that loads
/web/at a desktop viewport and asserts the Cangjie version control is in the same top control band as[data-yune-schema-switcher]. The test should assert that the三代 Version 3and五代 Version 5controls appear before the status strip and textarea, and that no lower section titledWeb Frontend Controlsremains when this was its only control. Add a narrow viewport assertion that schema selection and Cangjie version wrap within the same compact top band. -
Step 3: Move the control into the top control component
Pass
isCangjie5andsetIsCangjie5fromApp.tsxinto the top toolbar/control component created or updated byM25-DOGFOOD-04. Render a compact segmented control next to the schema selector with the label倉頡版本 Cangjie version, options三代 Version 3and五代 Version 5, and the existingSegmentinteraction pattern. Keep the control keyboard-accessible and do not add another UI framework. -
Step 4: Remove the empty lower section
Delete the
Web Frontend Controlssection fromPreferences.tsxif moving Cangjie version leaves it empty. If later rows add other frontend controls, keep those controls in an appropriately named settings group, but do not keep a one-control Cangjie section at the bottom. -
Step 5: Prove the existing customize path still works
In Playwright, switch to the Cangjie schema, choose
三代 Version 3, apply or trigger the existing settings flow, then choose五代 Version 5. Capture the active segment state and a worker/debug signal showingisCangjie5changed, or use the existing UI/evidence hook that provesRime.customize({ isCangjie5 })was invoked. Do not change engine behavior or Cangjie candidate ordering in this UI row. -
Step 6: Capture visual evidence
Save desktop and narrow-viewport screenshots plus a JSON summary under
M25-DOGFOOD-05. The JSON should include viewport size, bounding boxes for schema selector and Cangjie version, active Cangjie version before/after toggling, and whether any lowerWeb Frontend Controlssection remains. -
Step 7: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/src/Preferences.tsx -
Modify if shared section markup needs a test hook:
apps/yune-web/source/src/Inputs.tsx -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-06/ -
Step 1: Preserve the section-order baseline
Keep
display-live-section-order-report.jsonas the issue baseline. The currentPreferences.tsxorder is engine/session dogfood controls,即時狀態 Live session controls,顯示設定 Display controls, then網頁前端 Web Frontend Controls. The requested order swaps Display controls and Live session controls without changing the controls inside either section. -
Step 2: Add failing browser layout coverage
Add a Playwright test that loads
/web/at a desktop viewport and locates the headings顯示設定 Display controlsand即時狀態 Live session controls. Assert that Display controls is visually above Live session controls and occupies the top-right settings-grid slot. Use bounding boxes rather than only DOM order, because the user complaint is about the rendered two-column layout. Add a narrow viewport assertion that Display controls still appears before Live session controls in a readable single-column flow. -
Step 3: Swap the sections in
Preferences.tsxMove the full
顯示設定 Display controlssection above the即時狀態 Live session controlssection. Preserve every existing Display control: display languages, page size, candidate menu layout, Chinese typeface, candidate Jyutping, and reverse code display. Preserve every existing Live session control: ASCII mode, full shape, simplification, traditionalization, extended charset, and disabled. -
Step 4: Prove state bindings still work
Extend the browser test to toggle one Display control and one Live session control after the swap. Use existing UI state hooks where available, or assert the active/checked DOM state directly. This prevents a layout-only edit from accidentally detaching
prefs.displayLanguages,prefs.pageSize,prefs.isAsciiMode, or related state setters. -
Step 5: Capture visual evidence
Save desktop and narrow-viewport screenshots plus a JSON summary under
M25-DOGFOOD-06. The JSON should include viewport size, section heading order, bounding boxes for Display controls and Live session controls, and the toggled control state checked in Step 4. -
Step 6: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/src/Inputs.tsx -
Modify:
apps/yune-web/source/src/index.css -
Modify:
apps/yune-web/source/src/App.tsx -
Modify if the theme toggle is included in the change:
apps/yune-web/source/src/ThemeSwitcher.tsx -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-07/ -
Step 1: Preserve the binary-control baseline
Keep
raw-checkbox-binary-controls-report.jsonas the issue baseline. The current sharedTogglecomponent inInputs.tsxrendersinput type="checkbox"withclassName="yd-switch", and.yd-switchinindex.csscreates the rounded pill switch shown in the user screenshot..yd-checkalready exists as the square checkbox styling used by checklist controls. -
Step 2: Add failing browser affordance coverage
Add a Playwright test that loads
/web/, finds representative binary settings in the preferences area, and asserts they do not use the rounded pill visual dimensions. Cover at least one engine/session toggle, one live-session toggle, one display/settings toggle if present afterM25-DOGFOOD-06, and the Yune inspector toggle. The test should record each control's label, checked state, class list, bounding box, and computed border radius. -
Step 3: Change the shared
Togglepresentation to checkbox styleUpdate
ToggleinInputs.tsxto render the binary control with the checkbox-style class used by raw checkboxes, or rename the shared class so bothToggleandCheckboxuse the same square checkbox affordance. Keep semanticinput type="checkbox", label click behavior, checked binding,NO_AUTO_FILL, and focus-ring accessibility. Do not changeRadio,Segment, orRangecontrols. -
Step 4: Retire or narrow
yd-switchusageRemove the rounded-pill styling from settings-panel binary controls. Update the Yune inspector toggle in
App.tsxto use the same checkbox affordance. Decide explicitly in the row evidence whetherThemeSwitcher.tsxremains a specialized icon switch because it overlays sun/moon icons, or whether it also becomes a checkbox-style control. If the theme toggle remains specialized, rename its class or add a comment soyd-switchno longer means generic binary setting. -
Step 5: Prove checked and unchecked states remain obvious
In Playwright, toggle representative controls on and off and assert both states are visible through the checkbox mark/fill, not only through surrounding text. Verify keyboard focus still lands on the input and Space toggles the checked state.
-
Step 6: Capture visual evidence
Save desktop and narrow-viewport screenshots plus a JSON summary under
M25-DOGFOOD-07. The JSON should include the class names and computed styles for each checked control, unchecked control, and any explicitly exempted theme toggle. -
Step 7: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/schema/jyut6ping3.schema.yaml -
Modify:
apps/yune-web/source/schema/jyut6ping3_mobile.schema.yaml -
Modify:
apps/yune-web/source/src/consts.ts -
Modify if trigger copy needs a richer display:
apps/yune-web/source/src/SchemaSwitcher.tsx -
Modify if schema installation rejects the reassigned prefixes:
crates/yune-rime-api/src/schema_install.rs -
Test:
crates/yune-rime-api/tests/typeduck_web.rs -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-08/ -
Step 1: Preserve the trigger baseline and superseded assumption
Keep
reverse-lookup-bare-grave-trigger-report.jsonas the issue baseline. Current web schemas useluna_pinyin.prefix: "`p"andluna_pinyin: "^`p[a-z']*;?$"underspeller/algebra; desktopjyut6ping3.schema.yamlalso hasreverse_lookup.prefix: ""andreverse_lookup: "^[a-z']*;?$", but that tag has no translator/dictionary and currently produces no useful lookup. Functional secondary lookups areloengfanon`landcangjieon`c. TypeDuck v1.1.2 captured schema data also uses`pforluna_pinyin. Treat M25 as an intentional web-profile divergence that gives bare`toluna_pinyin. -
Step 2: Add failing native/schema coverage
Add or extend a
typeduck_webtest that installs the Jyutping web/mobile schema and proves an input beginning with bare grave, for example`zhe, routes into theluna_pinyinaffix translator. The test should also prove the vestigial desktopreverse_lookuptag was removed or moved away from bare grave, and that retained secondary lookups use explicitv-prefixed triggers such as`vlfor Loengfan and`vcfor Cangjie. Add overlap probes such as`lai,`ci,`xi, and`reto prove normal pinyin initials still route toluna_pinyin; if any secondary lookup intentionally uses another prefix, document the dead-zone it creates and justify it in evidence. Record whether`pzheremains accepted as a compatibility alias or is no longer required. -
Step 3: Add failing browser coverage
Add a Playwright test that loads
/web/, keeps the Jyutping schema active, types`zhe, and asserts the candidate/debug state identifies theluna_pinyinreverse-lookup path without requiring thepprefix. If Loengfan/Cangjie secondary lookups remain exposed, add browser assertions for their explicit triggers, for example`vland`vc. Also assert the visible UI explains the trigger map for the active schema. Capture whether the generic operation-error toast appears; if the toast appears because the worker is blocked or slow rather than because of trigger parsing, link that finding back toM25-DOGFOOD-01andM25-DOGFOOD-03instead of broadening this row. -
Step 4: Change the schema trigger
Update the
luna_pinyinaffix translator settings injyut6ping3.schema.yamlandjyut6ping3_mobile.schema.yamlso the prefix is bare`and the alphabet regex accepts bare-grave Mandarin pinyin input, for example^`[a-z']*;?$. In desktopjyut6ping3.schema.yaml, remove the vestigial bare-gravereverse_lookuptag unless implementation finds a real translator/dictionary consumer. Move retained Loengfan and Cangjie side lookups out of single-letter pinyin-initial prefixes, preferably to avnamespace such as`vland`vc. If Yune/librime first-match ordering requires it, place specificv-prefixed affix segmentors before the bare-graveluna_pinyincatch-all and prove the order with the tests from Step 2. Keep any differentr/x/vdecision explicit in evidence. -
Step 5: Update user-facing hints
Update
SCHEMA_OPTIONSinconsts.tsand, if necessary,SchemaSwitcher.tsxso the web UI is explicit about triggers for each active schema. Forjyut6ping3, show bare-grave Mandarin lookup such as`zhe -> 這(普通話 / luna_pinyin 反查)and list retained secondary triggers separately, for example`vl...for Loengfan and`vc...for Cangjie. Do not describe bare`as a generic Jyutping lookup trigger. Ifluna_pinyinor Cangjie schemas have their own lookup hints, keep them separate so users can see which trigger belongs to which schema. -
Step 6: Prove behavior and error handling
Run the native/schema test and browser test. Save JSON evidence with the typed input, active schema id, reverse-lookup tag/source if available, first candidates, selected secondary namespace, affix segmentor order, visible UI trigger copy, whether the operation-error toast appeared, and per-key timing if available. The evidence must state that this is a deliberate web-profile divergence from TypeDuck v1.1.2's
`pluna_pinyintrigger. If reverse lookup remains slow after the trigger fix, leave or cross-link the latency toM25-DOGFOOD-01/M25-DOGFOOD-03; open a new row only if the trigger fix exposes a distinct crash. -
Step 7: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/src/Preferences.tsx -
Modify if radio markup/styling needs adjustment:
apps/yune-web/source/src/Inputs.tsx -
Modify if labels need copy changes:
apps/yune-web/source/src/consts.ts -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-09/ -
Step 1: Preserve the control baseline
Keep
candidate-layout-radio-report.jsonas the issue baseline. CurrentPreferences.tsxrenders候選排版 Candidate Menu LayoutasSegmentoptions backed byprefs.candidateMenuLayout;Inputs.tsxalready exposesRadio. -
Step 2: Add failing browser coverage
Add a Playwright test that finds the Candidate Menu Layout field and asserts it contains radio inputs for horizontal/vertical instead of
.yd-segmentlabels. Assert the accessible names include the existing labels fromCANDIDATE_MENU_LAYOUT_LABELS. -
Step 3: Replace the segmented control with radios
In
Preferences.tsx, replace theSegmentmapping forcandidateMenuLayoutwithRadiocomponents using the samename, labels,prefs.candidateMenuLayout, andprefs.setCandidateMenuLayout. Do not changeSegmentglobally because schema, Cangjie version, and other controls may still need segmented styling. -
Step 4: Prove layout behavior remains intact
In Playwright, select horizontal and vertical radio options and type a short input such as
hai. Assert the selected radio state changes and the candidate panel uses the expected horizontal/vertical layout class or DOM shape for each option. -
Step 5: Capture evidence
Save desktop and narrow-viewport screenshots plus JSON under
M25-DOGFOOD-09. Include radio labels, selected value before/after, and candidate panel layout evidence. -
Step 6: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
Files:
-
Modify:
apps/yune-web/source/src/Preferences.tsx -
Modify if shared layout styling is introduced:
apps/yune-web/source/src/App.tsx -
Modify if a reusable class is needed:
apps/yune-web/source/src/index.css -
Test:
apps/yune-web/e2e/yune-typeduck.spec.ts -
Evidence:
apps/yune-web/e2e/results/m25-dogfooding/M25-DOGFOOD-10/ -
Step 1: Preserve the alignment baseline
Keep
ime-settings-margin-alignment-report.jsonas the issue baseline. CurrentApp.tsxrenders the playground stack directly insidemain.m-auto.p-8.max-w-7xl, whilePreferences.tsxadds a nestedmx-auto max-w-6xlwrapper around輸入法設定 IME Settings. -
Step 2: Add failing visual-layout coverage
Add a Playwright test that loads
/web/at a desktop viewport, locates the textarea or first stable playground content block above settings, locates the輸入法設定 IME Settingswrapper, and compares bounding boxes. AssertMath.abs(playground.left - settings.left) <= 2andMath.abs(playground.right - settings.right) <= 2. -
Step 3: Add narrow-viewport coverage
Repeat the bounding-box check at a narrow mobile viewport. Assert the settings wrapper does not introduce extra side margins compared with the textarea/main playground content and that no settings heading text overflows.
-
Step 4: Remove the nested width mismatch
In
Preferences.tsx, remove the nestedmx-auto max-w-6xlconstraint or replace it with a shared class that inherits the main content width. Prefer the minimal change: make the preferences root use the same available width as the content above, without changingmainwidth, section card padding, or the two-column settings grid. -
Step 5: Protect adjacent M25 layout changes
Run the layout checks added for
M25-DOGFOOD-06,M25-DOGFOOD-07, andM25-DOGFOOD-09if they exist by implementation time. If they do not exist yet, capture a JSON note underM25-DOGFOOD-10stating that the alignment fix preserved the current section order, checkbox/radio affordances, and settings grid class names. -
Step 6: Capture evidence
Save desktop and narrow-viewport screenshots plus JSON under
M25-DOGFOOD-10. Include viewport sizes, left/right bounding-box values for the playground content and IME Settings wrapper, the pixel deltas, and the final preferences root class name. -
Step 7: Regenerate the TypeDuck-Web patch if source changed
If any file under
apps/yune-web/source/changed, regenerateapps/yune-web/patches/yune-web-runtime.patch, reverse-check it fromsource/, and forward-check it on a clean source checkout.
When the feedback list exists, execute in this order unless the ledger explicitly says otherwise:
- Reproduce and capture evidence for all
Needs triageandBrowser integrationrows. - Fix runtime/browser correctness rows before broad UI polish.
- Capture or reuse pinned oracle fixtures before any
Engine correctnessimplementation. - Batch adjacent UI polish only when it touches the same local components and does not blur issue ownership.
- Regenerate and reverse/forward check the TypeDuck-Web patch after every source-changing slice.
- Close ledger rows as evidence lands; do not wait until the end to update row status.
- Run focused gates for touched layers, then broad closeout gates if the batch changes shared behavior.
Before M25 can be archived:
cargo fmt
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
npm.cmd --prefix packages/yune-typeduck-runtime test
npm.cmd --prefix packages/yune-typeduck-runtime run build
npm.cmd --prefix apps/yune-web/source run build
git diff --checkRun the real TypeDuck-Web Playwright tests for every closed browser-visible row. If source files under apps/yune-web/source/ changed, also run the patch reverse/forward checks from this plan.
Archive this plan only after all M25 rows are Closed, Deferred, or Rejected with evidence/rationale. Update docs/roadmap.md and docs/requirements.md only for durable milestone status or new requirements, not for every intake row.