← 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-day90is Nina's quiet machine (ten Observed, Quietly);freshhas none.--data localwith no qcontrol shows a standalone machine with no policies yet. - Against a plane:
cargo run -p fixture-plane -- --world pilotandcargo run -p app -- --data plane --plane-url <its url>: the same record over HTTP. - A notification:
QPOINT_SPIKE_DRILL=5announces 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 (threadc147-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 againstfinding-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::SeverityTagandcard(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
SecuritySeverityTagdrawn (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
- 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-completedto 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. - 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.
- 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
requiredcame 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. - 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
announcedtable). Default: keep. - 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.
- The footer says
findings: derivedon 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. - The routes are
/safety,/safety/finding?findingId=,/safety/settingsand/training(the app map names native pages by a path; qdash's/findingsis 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. - c4's types stay.
types::messages, its fixture, its drift test againstbones.md, and themessagescrate's inbox store and delivery rules stay as c4's record; nothing in the app reads them, and/api/messagesis 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. - The rail.
mainalready draws c7's rail (uppercase section titles over a hairline, no icons;shell.rssays so), so Safety & Security is aRailSectionlike 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. - 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.pystill 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 byevery_region_is_foundin 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.