# WeChat Developer Tools validation

## Status source

The repository has two independent real-host fixtures:

| Fixture | Scope | CI artifact |
| --- | --- | --- |
| `examples/miniapp_conformance_app` | Core, seven routes / four native tabs | `minimoon-devtools-<commit>` |
| `ui/examples/showcase` | UI, six pages | `minimoon-ui-devtools-<commit>` |

Each tracked `generated/verify_report.json` records reproducible candidate status.
`generated/devtools.evidence.json`, when present, is the local,
fingerprint-bound host record and is ignored by Git. A local release decision
is valid only when both local reports have `status: "passed"` and
`release: true`, and their separate evidence matches each exact artifact set.

Do not copy a validation date, tool version, evidence file, or artifact
fingerprint from an older build or another application.

Fingerprint v2 includes every regular `dist/` file except the ignored local
`project.private.config.json`, plus the exact generated manifest and smoke
checklist. A checklist-only, `app.wxss`, shared-template, or future static-asset
change is therefore a new host candidate even when application JavaScript is
unchanged.

## Prepare the exact release

```bash
moon run src/cmd/minimoon --target native -- \
  build examples/miniapp_conformance_app --mode release
moon run src/cmd/minimoon --target native -- \
  build ui/examples/showcase --mode release
```

Import each fixture's `dist/` directly. Do not edit generated files after
building. Run the entire relevant checklist separately in the Skyline
simulator, physical-device preview and device debugging with a clean console.

For CI candidates, download both artifacts from the same frozen commit. In
each bundle, run `sha256sum --check --strict SHA256SUMS` and
`sha256sum --check --strict ARCHIVE_SHA256`. The core ZIP is
`miniapp_conformance_app-dist.zip`; the UI ZIP is
`minimoon_ui_showcase-dist.zip`. Import each unchanged `dist/`. Confirm that
each `HANDOFF.json` names the expected commit, app directory, core/UI versions,
toolchain and the same framework artifact fingerprint as its report. Keep
manifests and checklists paired with their own application.

## Tool settings

- rendering engine: Skyline
- “将 JS 编译成 ES5” and enhanced compilation: on
  (`setting.es6=true`, `setting.enhance=true`)
- code minification: on (`setting.minified=true`)
- online minimum base library: 3.17.0 or newer
- clear all cache before validating newly generated bytes

These values belong in public `project.config.json`, not the local private
override. The generated CommonJS must pass without an additional Developer
Tools transform. See the official [code compilation](https://developers.weixin.qq.com/miniprogram/dev/devtools/codecompile.html)
and [project configuration](https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html)
references.

For blank pages, preview parser errors, device compilation prompts, or missing
host globals such as `TextEncoder`, follow the
[`MiniApp JavaScript compatibility troubleshooting`](miniapp_javascript_compatibility.md)
runbook before recording evidence.

## Core interaction checklist

All seven routes use the same core artifact fingerprint. The [fixture guide](https://github.com/lucavance/minimoon/blob/main/examples/miniapp_conformance_app/README.md)
maps source ownership and the three user workflows. Do not enter the removed
Capability Probe/Lab/Details routes or treat a Tab hide as an unload.

Native navigation and top layout:

- Cold launch opens 首页; white status text stays on deep blue even during empty
  boot. The first business tree reserves capsule/status-bar space.
- Native Tabs are 首页 / 交互 / 平台 / 应用 with distinct selected icon states.
  Switch repeatedly: local edits survive Hide/Show, Load/init does not repeat,
  and the page stack does not grow.
- 首页 contains local count/reset and only three query-free Tab shortcuts.
  交互 links to Runtime Lab; 平台 links to Request Lifecycle; 应用 links to
  Draft Editor and Request Lifecycle. No Tab expands into an entire second page.
- The fixed navigation band stays rectangular. Only the scrolling blue hero
  has a shallow curved bottom edge. Check the arc in actual Skyline: no text
  clipping, horizontal overflow, unexpected seams or blank strips.
- On iOS/Android, check normal/notched screens, short windows, keyboard and
  resize. Header text must clear the capsule, long titles must remain readable,
  and bottom padding must not double-count the native TabBar.
- Resize/foreground update layout without a stale intermediate business tree.
  Missing metrics use the conservative fallback; later read failure retains
  valid prior metrics. Capture four primary pages and representative error states.

Showcase / 首页:

- Increment/reset affect the local card; rapid taps are lossless.
- Technical notes are folded initially; expanding shows the visit count.
  Returning to the Tab updates visits without resetting its local count.
- The three shortcuts use switchTab without query. There are no direct secondary links.

Interaction / 交互:

- Input, checkbox, radio, picker and swiper update their own adjacent results.
  Tab hide/show retains input and each local component's switch/textarea/timer state.
- Disclosure, non-collapsible single Accordion, multiple Accordion and Tabs
  expose the expected panels. These are the sole core component catalogue.
- Expand the reactive child counter, increment to 1, tap 更新父层, collapse and
  reopen: the child is still 1. A parent render must not recreate component scopes.
- Dialog closes through its content button and overlay. Sheet closes through
  its close button and overlay; no stale layer remains.
- Dropdown checkbox/radio changes stay visible while open; submenu action and
  the dismiss layer close the menu.
- Open Runtime Lab for identity, structural reconciliation and disposal; no
  “expand full Lab” duplicate exists in this Tab.

Platform / 平台:

- Login, editable storage get/set, toast, location and media show results beside
  their own controls. Clearing a login code only clears its display.
- Trigger failures and retry where feasible: another capability must not erase
  an HTTP error or replace the editable storage value.
- Select each public HTTP scenario, send, and expand response details:
  GET Unicode/special-character/repeated queries; JSON POST; repeated-field form
  POST; raw-text PUT; DELETE; HEAD/OPTIONS empty bodies; HTTP 400/500 as loaded
  results; /delay/3 with a 500 ms timeout as failed. Check status and selected echo
  fields, method and request headers in Network. Use only built-in synthetic data.
- Folding response details preserves the selected scenario, base URL and storage input.
  This page does not show a shared App controller.
- Default service is public https://httpbingo.org; an alternative must implement
  the same public HTTPS echo protocol and be recorded. No private test server is needed.
  Public-service or platform failure is a blocked case, never a silent mock pass.
- urlCheck bypass does not establish production request-domain eligibility,
  complete HTTPS/TLS acceptance, account readiness or domain registration.
  Do not commit AppIDs or private configuration.

Application and Draft Editor / 应用与草稿编辑:

- Application is the only complete shared controller. Increment there, enter
  Draft Editor, increment its shared counter and return: both show the same
  count while the local draft remains independent. A hidden observer catches up on Show.
- Empty/whitespace-only title sends no HTTP. Valid title/note posts to public
  https://httpbingo.org/post: editor shows submission status, Application shows
  only the typed echo, not origin/private headers. This is not persistent saving.
- Editing, resubmitting or unloading while saving rejects obsolete replies.
  HTTP/schema/mismatched echo failures do not publish a draft to shared state.
- Editor source/visit diagnostics remain folded until opened. Missing or empty
  from input fails closed before graph creation, with no preview flash or live instance.
- Normal Back preserves the previous page. Direct valid editor input falls back
  with switchTab to 首页, never redirectTo a native Tab.

Request Lifecycle / 请求生命周期:

- The optional name query seeds a local marker. Edit it, open Draft Editor and
  return: the marker and page instance survive Hide/Show.
- Start the roughly two-second page-owned request, immediately open the editor,
  and return: hiding retains ownership and a completion can be observed on return.
- Start again and immediately replace the page with the editor: RedirectTo
  truly unloads it, aborts the request exactly once and rejects late callbacks.
- Start the separate App-owned request, then replace/unload this page: the result
  still reaches Application or the editor observer without another tap.
- Start the App request and immediately clear it: the old response must not
  overwrite idle. Background/foreground retains shared data.
- This page contains only marker/request/navigation comparisons, not the
  Platform catalogue or a shared counter panel. Direct Back falls back to 首页.

Runtime Lab / 状态与渲染实验:

- the page scrolls vertically and all four section-jump buttons (state/structure, native identity, component lifetime, async/disposal) work
- each section-jump target is consumed once: after the jump, ordinary manual
  scrolling must not snap back to the old target
- toggle branch and add/remove note exercise structural splice updates
- keyed item reorder preserves identity and selection
- arm the focused keyed reorder, enter `Minimoon 012345`, focus the input, and
  select a non-empty substring without touching the page again; within ten
  seconds the order becomes C/A/B while the keyboard, focus, cursor, selection,
  value, and greeting remain unchanged
- from fresh renderer stats, that focused reorder increases `moveOps` by
  exactly one and `hostWriteOps` by exactly two, with no replacement, fallback,
  retry, timeout, or scheduler failure
- controlled input updates the greeting; after entering a non-default value,
  `Reset controlled input` restores both the input and greeting to `Minimoon`
- from fresh renderer stats, that controlled reset produces one render commit
  with scalar `set` work and no additional move, splice, replacement, fallback,
  retry, timeout, or scheduler failure; the keyed native input identity remains
  the same
- enter a non-default input value, then tap
  `Reconcile controlled native values`; sibling keys must move while the same
  keyed native input is retained and displays the controlled `Minimoon` value
  without a stale frame (the tap itself may blur the input)
- before that reconciliation, note the visible picker option and swiper page;
  afterward their ranges/children and selected/current indexes must have
  changed together while the same logical option/page remains visible, opens
  consistently, and remains interactive
- focus, blur, and confirm each update the input-event status; input blur must
  work with the native payload that has no cursor field
- full root replacement keeps the vertical Runtime Lab usable
- both local cards have independent switch, textarea, and interval state
- card reorder preserves local state; removal stops its subscription; restore
  creates fresh state
- picker, swiper and the second-instance native navigator remain usable after reconciliation
- opening a second `pages/runtime_lab/runtime_lab?instance=secondary` instance does not share
  page or component state with the first
- start the async probe and do not touch the page; `perform` success and
  `attempt` failure must both update visibly
- start the delay probe and do not touch the page; its 600 ms completion must
  update visibly
- start each delayed probe and immediately leave the page; unload must cancel
  ownership with no later UI update or console error
- Back returns one stack entry; direct Runtime Lab entry uses switchTab to Showcase


Across all routes, require a clean console, clean unload, and no delayed update
after a page or component is disposed.

When investigating interaction latency, inspect
`__minimoonRendererStats()` for `receivedUiEvents`, `coalescedEvents`,
`eventBatches`, `maxEventBatch`, `maxQueueDepth`, `renderCommits`,
`renderAckSamples`, `renderAckTotalMs`, `renderAckMaxMs`, `renderAckLastMs`,
`renderRetries`, `renderTimeouts`, `schedulerFailures`, and `shadowCopies`.
Also inspect `asyncDrains`; no-touch async completion must increase it while
same-turn completions may share one drain. An intervening UI or lifecycle entry
must remain an ordering barrier rather than being crossed by a later drain.
Normal interaction must not report a retry, timeout, or scheduler failure. Rapid
tap counts must remain exact. Sustained scroll may increase `coalescedEvents`,
but scroll coalescing must never cross an intervening tap or another event key.

## UI interaction checklist

The UI fixture's `generated/smoke_checklist.json` is the exact acceptance
scope, including its ordered `devtoolsChecks`. Exercise all six pages:

- Foundation: disabled buttons, labels, avatar load/error/fallback and status
  content; switch light/dark and LTR/RTL without resetting local state.
- Overlays: nested surfaces, viewport placement, controlled state on hide/show,
  dismiss layers, long-press and explicit-tap context menus, touch tooltips.
- Forms: native IME/paste/confirm, errors, OTP completion, submit/reset,
  disabled choices, single and multiple selection, native/custom sliders,
  minimum gap, vertical/RTL measurement and touch cancellation.
- Data: calendar modes and month boundaries, disabled dates, date presets,
  Command/Combobox filtering, numeric table sort, column visibility, keyed row
  selection/pagination, native cell spans and chart presentation primitives.
- Layout: native Carousel swipe, Resizable bounds/cancel, Scroll Area, Sidebar
  gestures/navigation and Attachment actions.
- Feedback: message end-following and prepend reading-position retention,
  Toast/Sonner actions, expiry, dismissal and hidden-page timer pausing.

Every route must unload without stale measurement, timer, event or state
delivery. Native substitutions in the
[migration map](https://github.com/lucavance/minimoon/blob/main/ui/docs/migration.md)
define the supported behavior; they are not missing browser tests.

## Record evidence

Only after the exact release directory passes:

```bash
minimoon devtools record examples/miniapp_conformance_app \
  --status passed \
  --recorded-at <actual-timestamp> \
  --tool-version "<actual-version>" \
  --notes "exact seven-route release checklist passed"
minimoon verify examples/miniapp_conformance_app --release
minimoon devtools record ui/examples/showcase \
  --status passed \
  --recorded-at <actual-timestamp> \
  --tool-version "<actual-version>" \
  --notes "exact six-page release checklist passed"
minimoon verify ui/examples/showcase --release
```

Record each application only after its own actual pass, in the validation
checkout that contains the tested artifacts. `verify` rebuilds artifacts;
compare its resulting fingerprint with the one actually tested. An unchanged
fingerprint preserves evidence; any changed distributable, manifest or
checklist requires renewed acceptance. Do not copy an evidence file from
another application or checkout. Automated checks do not substitute for this
interaction checklist. Each evidence file, including its
timestamp, tool version, notes, and result, must remain local and must never be
committed or pushed.

If any checklist item fails, record the real outcome locally with
`--status failed`, the actual timestamp and tool version, and notes containing
the first console error plus relevant renderer counters. Keep screenshots and
raw logs outside the repository and never edit evidence JSON manually. Do not
commit or push a release claim. Fix the source, rebuild, and repeat the entire
validation whenever the artifact fingerprint changes. Passed evidence remains
local as well. Follow the
[release handoff](release_candidate_handoff.md): run `check:all`, then
`check:mvp`, and restore candidate reports with `check:candidate` before
publication or handoff. Require unchanged fingerprints and a clean source
checkout; candidate restoration does not delete ignored evidence.
