← Qnative (provisional) · c13

review-1.md

c13 review stop 1: Safety & Security in the app

Written: 2026-11-13, night, after steps 0–7. Reader: Mark (and the c147 session, which briefed it). Status: at review stop 1. Each call has a default; a call you don't answer takes it. Mark Peterson's answers (design peterson.md) amend the brief later; nothing here waits on them.

How to try it

  • The app: QPOINT_WORLD=pilot cargo run -p app (synthetic, Priya's machine): the rail's Safety & Security → Findings · Settings. Findings opens on the first-run card (Teach me, Engineering's default), From HQ, Observed, Prevented. Open Postgres MCP (read-only) for Dana's note and Acknowledge; Linear MCP for the suggested training, Start the training →, then ← Linear MCP. QPOINT_WORLD=meridian-day90 is Nina's quiet machine (ten Observed, Quietly); fresh has none. --data local with no qcontrol shows a standalone machine with no policies yet.
  • Against a plane: cargo run -p fixture-plane -- --world pilot and cargo run -p app -- --data plane --plane-url <its url>: the same record over HTTP.
  • A notification: QPOINT_SPIKE_DRILL=5 announces the world's newest finding as if just found, at your position (Quietly keeps it in Findings; When it matters posts a banner). macOS posts only from a bundle: scripts/bundle-dev-app.sh (c4).
  • Tests: cargo test --workspace; the page tests are in the stories crate's c13 block (cargo test -p stories safety).
  • Captures: design's scripts/shot-programs/c13-native-safety.json (npm run shots -- --native scripts/shot-programs/c13-native-safety.json), 33 shots, mirrored into design c147's atlas (thread c147-phase5-native) beside medium A.

What was built

The record is CYCLE.md §Key Changes, step by step, with the commits. In short:

  • The shape (types::findings) and two drift tests, against every sidecar and against finding-shape.md §3 and §4.
  • The nozzle's three routes, synthetic from the lab's sidecars (scripts/sync-findings.sh), the plane in the same shape, local from the app's own detector.
  • The detector in Rust (nozzle::findings), which reproduces all 67 of the lab's findings exactly: ids, outcomes, evidence, endpoint events, counts and order.
  • The person's events in c4's SQLite store, repurposed (messages::person): notes only, queued for the plane, idempotent by id.
  • Three pages in medium A's words (pages/findings.rs, finding.rs, safety_settings.rs), the region ids medium A's own (safety.*, safety-finding.*, safety-settings.*), qguikit::ux::SeverityTag and card (scorecard row added).
  • c4's UI retired: app::messaging, the Inbox and Messages settings pages, ten stories; the trait, the tray, quiet hours and Focus kept (app::safety, messages::announce).
  • Stories: 22 in place of c4's 10 (and the rail story renamed), each honouring QPOINT_WORLD; 18 region-text tests.

The wipe

Medium B's pages read medium A's sentences word for word: the tests assert them (the HQ line, the history in sentences, the settings preview's none · 5 · 19, the prevented card's 45 stops and 23 days). Where the spec's measures and the native pages differ, on purpose or by the kit:

  • The table's widths are the spec's measured ones (116 · flexible · 170 · 197 · 147), not the web's declared ones, which sum to 497 of 842 and stretch.
  • Type sizes come from text::T* per the spec's roles (headings T24, T20, T16; ledes T13; cells T14/T12/T13/T11). The PageHeader's own measures (15 px crumbs, the 40 px gap under the subtitle) are the kit's, shared with every page, so the header sits a little lower than medium A's.
  • The severity tag is SecuritySeverityTag drawn (11/11 bold, the severity outline on its fill); the web's colour transition isn't drawn.
  • The first-run card's button is the kit's outline button at the small size (the web's 32 px stroke button is a little taller).

Calls

  1. The lesson and the check in the app, a departure from c147 Q5. Q5 kept the training's lesson and quiz out of the native V1 ("the event and the record only"), and medium A opens the training in the browser. But qnative c9 shipped the four published trainings in the app for a demo (T-MCP-1 among them), launched from the inbox. On the coordinator's word this cycle keeps them: the finding's training card opens c9's lesson when the app bundles it (Start the training →), and the browser link has no product address to open, so an unbundled one says "This training is not on this build yet." Finishing it adds training-completed to the finding (a note; the policy's resolve comes from the plane, review 3, 6a), and the training's back button returns to its finding (← Linear MCP). This is Mark's call, open, not decided. Default: keep c9's lesson in the app, since it exists, and give medium A the same link; or rule Q5 again and turn it off.
  2. The new words. Medium A has no sentence for six states the app has: the training card's link and its fallback, the finished training ("You finished the training. Your company sees the record."), a standalone machine's empty state ("This machine's qcontrol configuration has no policies yet, so qcontrol raises no findings here."), Ask IT on a standalone machine (c4's own sentence: "Your mail app opens with the IT contact this machine's configuration names. Nothing is sent from here."), and the training page's three lines that named the inbox. All voice-checked (brand MCP, productUI): no violations. Default: keep; medium A takes them when it draws these states.
  3. How the positions reach c4's levels. Observed new: active (a banner) at When it matters and Teach me; prevented new: passive (the notification list, no banner, no sound) at Teach me; a follow-up asking for an acknowledgement: active at every position and through the app's quiet hours, as c4's required came through, but never time-sensitive, so the OS's Focus always holds ("Your Mac's Focus is always honoured"); recurrences never; quiet hours hold the rest in Findings. Default: keep.
  4. No backlog of notifications. The first read of a source takes what is there as known and announces nothing (opening a world, connecting a machine with 51 findings); only what arrives afterwards is announced, once (the announced table). Default: keep.
  5. The count beside Findings in the rail is the tray's count (an unacknowledged follow-up), as c4's inbox carried its count. Default: keep.
  6. The footer says findings: derived on a synthetic world with a machine (medium A says "findings derived"), so the person can tell the lab's rows from a sensor's (R1). Default: keep.
  7. The routes are /safety, /safety/finding?findingId=, /safety/settings and /training (the app map names native pages by a path; qdash's /findings is the operator's page, a different grain). The app map's three pages use medium A's region ids, so a test line written for medium A reads here too (c149's simulation rows, when they land). Default: keep.
  8. c4's types stay. types::messages, its fixture, its drift test against bones.md, and the messages crate's inbox store and delivery rules stay as c4's record; nothing in the app reads them, and /api/messages is gone from the nozzle and the fixture plane. Default: keep until c137's types are ruled out for good; then delete them in one commit.
  9. The rail. main already draws c7's rail (uppercase section titles over a hairline, no icons; shell.rs says so), so Safety & Security is a RailSection like the others. The coordinator's note that c7's PR is open stands: whatever its restyle still changes must cover this section too (its test, the_rail_draws_every_section_whole, now counts it). Default: nothing to do here.
  10. New to Qpoint's two inbox stops now go to Findings and Safety & Security's Settings, in medium A's words where it had them (the stop's question "What did qcontrol notice or stop?" and the three spots' lines are new, voice-checked). The tour's owner (c10/c12) may want its own words. Default: keep until that tour is next walked.

Not done

  • The recut (step 8): scripts/cut-world.py still keeps detail for three sessions per world, so 4 sessions in Meridian and 13 in pilot have policy-event counts the detector can't read, and the seed has no MCP versions or consequential flag (P-PROVENANCE, P-TOOL-APPROVAL underivable). Left open, as the brief allowed.
  • Sending the person's notes up: they queue in order and are idempotent by id, but the plane's write API is c134's; nothing sends.
  • The guide check (scripts/guide-check.py, against a running app) and the budget with strobe were not run; the app map's regions are checked by every_region_is_found in the tests.
  • The lean check (scripts/check-lean.sh, two release builds) was not run; nothing here touches developer mode.
  • Peterson's answers (design peterson.md): when they come, they amend the brief and this cycle.

Qpoint Brand Style Guide