← Design · c139

review-1.md

c139 review 1: the model and the mocks

Stopped: 2026-11-11, after Phases 0–3. For: Mark (the calls), Tyler (the dependencies). Nothing is ruled yet; every default below is the plan's proposal (plan.md D1–D16).

Use this file to rule the eleven calls below, one at a time, each with a recommended default and the capture that shows it, then hand Tyler his six. The model is in plan.md §The shape and drawn as An update and its states. The reading behind it is prior-art.md: twelve precedents, what we copy and refuse, sixteen questions with defaults.

What to look at

  • Settings › Updates in the native skin. The lab bar sets the world, the era (now or later), whether the release carries qcontrol, and the skip button. The knob row sets the update state. The buttons walk the machine: Update, then downloading, then ready; Restart now, then installed; Check now, then checking, then available.
  • The tray and the trace: the menu on a desktop, the window's footer in the same state, and every state side by side.
  • The captures, at 2× and clipped to the window, in the cycle's visual record. Twelve are of Settings › Updates: c139-updates-up-to-date, -checking, -available, -available-skip, -downloading, -ready, -installed, -failed, -offline, -managed, -available-later and -policy-later. Four are of the tray: c139-tray-ready-ask, -ready-auto, -installed and -failed. The last is the sketch, c139-sketch-updates.

What the mocks read from the worlds (c132 D6): qcontrol's running version and state (the bridge's /api/sensor), the harness count (/api/installations), and the footer's source line. What is the knob (app/data/c139-updates-mock.ts, marked): the next release's sidecar, its notes and the history. No release of the app has been cut. The notes for "0.2.0" are drafted from what the cycle records say was built (c131 D19–D21, c132 Phase 4, c138 Phase 0), each line citing its record. That is how D7 proposes the real ones get written.

The calls

1. The preference's default

Recommended: Automatically, marked "(recommended)", with no first-run question (D1). It installs at quit, at the machine's restart, or when nobody is looking (call 7). Never while the window is open.

  • Why: fixes reach partners without anyone acting, and the trace keeps it honest. Zed, VS Code, Firefox, Slack and the Claude app all default to automatic. Sparkle is the exception: it asks on the second launch.
  • The alternative: Ask me first, while the program is with build partners. They are testers and may be mid-demo. Since nothing restarts while the window is open (R1), the demo risk is already covered.
  • See: c139-updates-available (the tiles), c139-updates-ready (Ask me first).

2. What "invisible" means

Recommended: a quiet trace in the app, and nothing pushed (D2, R3). For seven days the footer's version reads v0.2.0 · updated and the tray's version line says updated. The Updates page leads with what's new, and the history says how each version came. There is no inbox message, OS notification, toast or dialog.

  • Why: "invisible when working" means nothing interrupts, not that nothing is said. c137's inventory dropped app-update as a message type on these grounds (row 24: "the updater and the Status page's version line handle it"). This keeps that ruling.
  • The alternatives:
    • (a) No notice at all. The Claude desktop app's idle "stealth update" is traced only in a log line, and the prior art refuses it.
    • (b) A quiet inbox entry. This would be an app-updated kind at inbox delivery in c137's quiet mode, so partners would find the internal notes in their inbox. It reopens c137 row 24 and adds a kind to the inventory.
  • See: c139-updates-installed, c139-tray-installed.

Recommended: inline, at partner altitude, with the trail one link away (D6, D16). The notes give what changed for you, what to try, the sharp edges fixed (it says "None in this version" rather than padding) and the ones still known. Each line cites its record. The trail is the cycle records and the commit range.

  • Why: the Updates page is where the person decides, so a link alone sends them away. The notes are five to eight lines, and the full trail would be too long to show inline. VS Code, Sparkle, Zed and TestFlight all show their notes in the app.
  • The alternative: a link only, to the cycle record or to the partner portal, if c62 is built.
  • See: c139-updates-available and c139-updates-installed (the right column); c139-updates-available-later for the public notes.

4. Skip this version

Recommended: none. "Not now" defers until the next quit or the next day, and holding a version back is the organisation's decision, made later from the plane (D8, R6).

  • Why: the sensor is the evidence. A version a person skipped is a gap the organisation did not choose, and supporting several versions across a small cohort costs us. Of the precedents, only Sparkle offers a skip, and it withdraws the skip for critical updates.
  • The alternative: a skip that a security release overrides, using the later min_version field.
  • See: c139-updates-available-skip shows what the link would look like.

5. Who owns release notes

Recommended: the person who cuts the release owns the facts and decides whether the notes ship (engineering, so Tyler or whoever he names). The draft is generated at release time from the /partner-digest sources: Key Changes, sharp edges and commits. Design owns the template and the voice check. The notes ship beside the sidecar as the notes field (D7).

  • The alternatives: design writes them, which doesn't scale with CI's cadence. Or product writes the public notes later; that fits the later era, and the partner notes stay with engineering.

6. One update or two

Recommended: one page and one Update for the app and qcontrol, with each binary in its own row and its own state (D9). After a quiet install, qcontrol's part waits on the page. It installs for every user of the machine, so it needs an administrator's password, and a quiet install never asks for one.

  • The alternative: the app updates only itself, and qcontrol keeps its own path (qupdate's command line, a package, or the plane later).
  • Depends on: Tyler's T2 and T4.
  • See: c139-updates-available with the knob "qcontrol in the release: yes", and c139-updates-installed.

7. When an automatic update may restart the app

Recommended: at quit, at the machine's restart, or after ten idle minutes with the window closed to the tray (D3). That last case only runs under a guard (D13): no focus is taken, and nothing is running that wouldn't come back as it was. That rules out a developer stream (c138), a mechanic session (c136), an open required message (c137), a tour and a capture. Ask me first and Manually offer Restart now · When I quit · When I'm away.

  • Why: a tray app is rarely quit, so "at quit" alone leaves machines stale for weeks. VS Code guards its idle restart this way. The Claude desktop app's unguarded one cut live sessions.
  • The alternative: at quit and at restart only. Tyler's T6 may narrow it to that anyway.
  • See: c139-updates-ready, c139-tray-ready-auto.

8. The version string in the partner era

Recommended: the tag with the 7-character SHA beside it (0.2.0 · a3f9e12), with Copy version info for bug reports (D5). Later, the tag alone, with the SHA kept in Copy version info. This needs every build that reaches partners to carry a tag (T3).

9. Where Settings lives

Recommended: a Settings page at the foot of the rail (⌘,), with five sections: General · Updates · Notifications · Data · Developer. The tray menu gets Check for updates… and Settings… (D11).

  • Why: one window and one rail. The settings that already exist have homes waiting: c131 D16's world switch under Data, c138's developer toggle under Developer, and c137's delivery controls under Notifications. This cycle mocks Updates only.
  • The alternative: a separate settings window, the macOS convention.

10. Ask me first's clock, and the install that stopped updating

Recommended:

  • Ask me first never prompts. It says how long the update has waited ("Ready since yesterday at 18:40"), and after three days the tray and the footer say "ready since Tuesday" (D14).
  • An install 60 days without an update gets one tray line, at most once a month, under any preference including managed (D15).

Both came from the prior art: Chrome's "available for # days", and VS Code's and Firefox's age lines.

11. The era as data

Recommended: the era comes from the feed, not from a flag in the app (D12). channel: partners and notes.audience: partners give today's page. When the feed carries stable or beta and public notes, the same page shows the channel switch and the public notes. Nothing built now is thrown away (R7).

  • See: compare c139-updates-available with c139-updates-available-later, and see c139-updates-policy-later for the organisation's policy.

Answered (Mark, 2026-11-13, walked)

#Ruled
1Automatically is the default, marked "(recommended)", with no first-run question. (D1, default)
2A quiet trace and nothing pushed: seven days of v0.2.0 · updated in the footer and the tray's version line; the Updates page leads with what's new. (D2, default; the old alternative, a quiet inbox entry, is gone with c137, closed into design c147)
3The notes inline, at partner altitude, each line citing its record; the trail one link away. (D6, D16, default)
4No skip. "Not now" defers; holding a version back is the organisation's decision, later from the plane. (D8, default)
5The notes are drafted automatically, summarised from the commit messages, with room for manual additions; engineering and design write the additions now, product later. (changed: Mark's words, in place of "whoever cuts the release owns them")
6One page and one Update, the app and qcontrol each in its own row with its own state. (D9, default; Tyler's T2 and T4)
7Restart at quit, at the machine's restart, or after ten idle minutes with the window in the tray, only under D13's guard. (D3, D13, default; Tyler's T6)
8The tag with the short SHA in the partner era (0.2.0 · a3f9e12), Copy version info for bug reports; the tag alone later. (D5, default; T3)
9An App Settings rail item with a submenu (the layer's submenu pattern, design c98's NavVerticalSubmenu) grouping the settings pages: General · Updates · Notifications · Data · Developer, ⌘, opening it; the tray menu gets Check for updates… and Settings…. (changed: Mark, "utilize the sub menu pattern to group these pages under a general App Settings nav item") Read with c147: Safety & Security keeps its own Settings (the noise control, c147 Q1), and App Settings › Notifications points to it. That reading is the recorder's, to confirm.
10Ask me first never prompts, it says how long the update has waited ("ready since Tuesday" after three days); an install 60 days without an update gets one tray line, at most once a month. (D14, D15, default)
11The era comes from the feed, not a flag in the app. (D12, default)

Next: Phase 4, the native build in ../qnative, and the lab mock brought to calls 5 and 9, both for cloud sessions (Mark, 2026-11-13); their briefs are cloud-native-prompt.md and cloud-lab-prompt.md. Tyler's six below still stand.

Tyler's dependencies

Each one is listed with what the design assumes until he answers. These are engineering calls; the page works whichever way they go.

#QuestionUntil then
T1Signing. The Apple Developer ID and notarisation of every update; Windows Authenticode; whether an update signature (minisign or EdDSA) is checked beyond SHA-256. This is c130 shape.md Q7.The checksum is the check. A signature failure would read like the checksum failure in c139-updates-failed.
T2The updater. Either qupdate grows bundle-awareness (the macOS .app replace, a Windows helper for the running exe, a per-user install), or the app gets its own (cargo-packager-updater, self_update). Also: one updater for both binaries, or two.One state machine per binary behind one page. The page doesn't know which updater runs.
T3The feed. channel, notes and size now; a tag for every build that reaches partners; a way to pull a release. Later: rollout and min_version.The mock's sidecar has these fields, marked as proposals.
T4qcontrol's update. Elevation on each OS; whether the sensor's restart drops events, and for how long; whether the app's update ever carries the sensor.The row asks for an administrator. The mock gives no figure for the gap.
T5Knowing a package manager or MDM owns the install. For example, a build-time explanation like Zed's ZED_UPDATE_EXPLANATION, the install path, or a marker file.Homebrew is the mock's example, with its sentence and its command.
T6Restart mechanics. The relaunch path on three OSes, and whether the guarded idle relaunch (call 7) is safe on each.D3 and D13 as written.

Out of scope, named so nothing is lost

  • Updating the self-hosted control plane (c134). That is an operator updating a server: a Docker image tag, migrations, a maintenance window. It will be its own cycle when the plane exists.
  • The organisation's update-policy page on the plane, for the later era, designed with c134. The native page already has a slot for it (c139-updates-policy-later).
  • First install and the installer (c131 packaging), and downgrade.

For the layer (flagged, not done)

The layer was not touched.

  • The preference tiles are design-local because the layer's UxToggleCard has no dense size (min-width 360 px, 24 px padding, a 3 px border). The candidate is a size="sm" for c135's census.
  • The state card (icon, one sentence, the next step, actions) is close to c137's message card and to Status's sentence band. It is a candidate pattern once a second native page uses it.
  • NativeTrayMenu, the new NativeIcon glyphs, and the rail's Settings item and footer trace in NativeWindow belong to the native skin and stay design-local under c132's rules.

What was and wasn't verified

  • The pages. The dev server rendered both pages in every state with no console errors, and the actions walk the machine.
  • The captures. The capture program ran: seventeen captures at 2×, filed in public/cycles/design/manifest.json. All 700 earlier entries were unchanged; the tool re-sorts the file.
  • The install. The layer package returned 401 from GitHub Packages here. I installed against a read-only clone of qpoint-io/q-nuxt-layer at its published version (0.9.37) and restored package.json and the lockfile afterwards. @qpoint-io/qmap and qflow are also private, so I put throwaway stubs in node_modules (untracked, not committed); pages that draw them were not checked.
  • The build. nuxt build passed (with NODE_OPTIONS=--max-old-space-size=12288; the default heap ran out during the client bundle). The prerender rendered /cycles/design/sketches/c139-updates, /pages/native/settings-updates and /pages/native/updates-tray. The only errors in its log are 404s for /cycles/qnative/… pages, because ../qnative isn't in this checkout; the partner notes' links to /cycles/qnative/c2 resolve where it is.
  • Types. tsc over the project's TypeScript reports nothing in any file this cycle touched. Its errors in cycle-stories-mock.ts (lines 523–538) are in code this cycle didn't change. There is no vue-tsc here, so the .vue files were checked by the build only.
  • The prior art. The reading reached GitHub, Apple, Android and Anthropic's pages directly. Zed, 1Password, Slack, Raycast, Google Play and Microsoft Learn were search excerpts, and the doc marks them as such. Its last section lists six facts to re-read from a laptop.
  • Not done. Nothing was pushed into ../qnative (Phase 4, after the rulings), and nothing was deployed.

Qpoint Brand Style Guide