← Design · c138

plan.md

c138. Developer Mode (plan)

Prompt: initial-prompt.md (Mark, 2026-11-11, three messages). Written: 2026-11-11, from two surveys of the code (qdash's Live Activity page with its backend, and Kamal's monitor TUI, both on qcontrol main of 2026-11-10; kept in full in research/) and a read of ../qnative at c2's review stop 3 with its measured budget. Ruled: the nine calls were walked with Mark the same day (§ Rulings); the sections below carry them. Kicked off: 2026-11-11, on Mark's word, starting with the Guide for Kamal. Runs in: ../qnative as its cycle c3, the twin of this one. It starts once c2's Tour stretch has landed, because both touch router.rs and shell.rs; Phase 1 below touches neither and can start sooner. Mode: archaeology (two tools that emerged from real use) into evolution (the live viewer improved), with one formula (the gate). Reader: Mark, then the executor, then Kamal (§ The instruction pages is written for him).

Why

Two developer tools exist today and both leave with the roadmap. The live viewer lives in qdash, which is leaving qcontrol. The monitor TUI lives inside the sensor's own process and only in dev builds. The native app is where a person at the endpoint will look, so both move there, next to the Tools and Components pages the app already has. Together they are developer mode.

Developer mode must cost a customer nothing they did not ask for. Some clients will measure the app's CPU and memory, so the plan treats the cost as a first-class result: measure it, offer a way to switch it off, offer a way to ship without it, and be able to say so in plain words.

The third reason is the hand-off. The pages are built on synthetic data through the real pipes, then Kamal points them at a sensor on his machine. That only works if what we expect from the sensor is written down per feed, with what exists today and what does not.

What we are replicating

The live viewer (qdash /live)

../qcontrol/crates/qdash/ui/app/pages/live.vue, with 13 components, 10 composables and 8,500 lines in all. About 2,700 of those lines are 13 framework-free modules in lib/live/, covered by most of the page's 107 specs; they port to Rust as they are.

  • Regions: stream status, a presence row of mini agent cards, Events and Tally tabs, a filter bar with a family → type tree, the firehose table (virtualised, live-follow), a detail sidebar with the payload tree, retention controls (5,000 events or 25 MiB), and a file mode that loads a run log JSONL.
  • Contract: GET /api/events/stream on qdash's loopback server. Plain SSE, one event JSON per data: frame, a comment keep-alive every 15 s, no auth, no replay, no cursor, a 1,024-record buffer per subscriber, and a lagging subscriber is disconnected without a marker. The URL is the second line of <data dir>/dash/qdash.sock.lock. The server answers 403 unless the Host is local, and a static qdash serve answers 503.
  • The web page has drifted from the sensor. It still expects sequence, observed_at, a run envelope and several retired event types. The current envelope is { timestamp, severity, entity_id, type, payload } across 14 families. On today's stream its Seq column is a client arrival counter, the Tally tab collapses to one "Unattributed" row, and the process family and netstat.snapshot have no home. The native port is written against the current schema, so it starts ahead of the page it replicates.

The monitor TUI (qcontrol start --tui)

Kamal's, added 2026-10-16 (qcontrol #782), in cli/src/commands/monitor/ui/terminal/ behind the Cargo feature tui; make build-dev turns it on. It is on main only, not on our feat/new-pages checkout. Seven tabs:

TabWhat it showsReachable from another process today
Overviewlive workers, a throughput sparkline per worker, latest events, health (problems first), the event mix of the last 60 s, targetsevents yes; workers, health and the last error no
Qcontrolthe monitor's own process tree: CPU (5 s mean), memory, history, averages over 1, 5, 10 and 30 minno producer; the TUI samples it itself at 1 Hz
Workerseach worker's lifecycle timeline, process facts (signing, inject verdicts, capture), behavior, plugins, process treeno; plugin loads partly, as events
Eventsthe raw event table with source and type filters, pause, an inspect paneyes, the same stream as the live viewer
Configtargets (first match wins), behaviors (own values bright, inherited muted), YAML as written and effectiveno wire; config.yaml on disk is not what the monitor has loaded
Inventoryharnesses and other matched processesyes: installation.snapshot, process.snapshot, or qdash's REST
Logthe monitor's log lines and its stderrno stream; only the log file

The finding that shapes the plan: the TUI is the monitor process. It reads three in-process inputs (a MonitorView snapshot at up to 4 Hz, the event route, a tracing layer) and samples resources on its own thread. None of the snapshot types derive serde. A native replica is therefore a different architecture, not a port: half of what it shows has no surface outside the process. Building the pages is our work; the feed is the ask to Kamal (§ The instruction pages, Monitor contract).

One overlap removes work: the TUI's Events tab and the live viewer are two viewers of one stream. We build it once. Monitor's Events link opens Live with the filter set.

The shape

┌ sensor (qcontrol monitor) ┐      ┌ nozzle ───────────────┐      ┌ the app ──────────────────────┐
│ events        ── SSE ─────┼─────▶│ fetch      (exists)   │      │ Events      a layer of meaning│
│ snapshots     ── bus ─────┼─────▶│ subscribe  (new)      │─────▶│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ monitor view  ── none yet ┼ ─ ─ ▶│ monitor    (new)      │      │ Dev  Tools · Sensor · Config  │
│ resources·log ── none yet ┼ ─ ─ ▶│                       │─────▶│      Components · Live        │
└───────────────────────────┘      │ synthetic │ sensor    │      │      Monitor · Guide          │
                                   └───────────────────────┘      └───────────────────────────────┘
     allowed? (the build, a flag)      a feed lives only while        on? (the person's toggle)
                                       its page is open
  • The Dev group grows from two pages to seven: Tools, Sensor, Config, Components, Live, Monitor, Guide.
  • Events is an ordinary page outside the group: the curated view over the same stream Live shows raw (ruling 2).
  • The stream core (subscribe, the frame parser, the buffer) is shared by Events and Live, so it sits outside developer mode. Only the instruments are gated.

The gate: what turning it off buys

What we know before measuring

  • The whole Inventory section cost +5.3 MB of binary and +11 MB at rest over the spike (c2 review stop 3). Idle CPU did not move (0.005% of a core).
  • Showing pages costs more than holding code: walking 16 pages added 54 MB, almost all GPU atlases, levelling off after the first walk.
  • Today's Dev code is about 2,500 of our 20,500 lines. With Live and Monitor it may reach a third of the code we write.
  • So the honest expectation: compiling developer mode out saves on the order of 1 to 2 MB of a 23 MB binary, and close to nothing at rest compared with switching it off, because code that never runs is not resident. The real costs appear only in use: parsing a stream, holding a buffer, sampling, and the atlases of the pages shown. This is an estimate from line counts; Phase 0 replaces it with readings.
  • The sensor pays too, and that side matters more to a sensitive client. Every stream subscriber makes the monitor re-scan installations and broadcast two snapshots to everyone. Attaching through qdash requires the dashboard subsystem, which also projects the stream into SQLite. The TUI's own work (the 4 Hz view, a full process index, the sampler) runs only when --tui is passed. A feed for the native app should keep that property: no subscriber, no work.

Ruled: the switch and the lean build

Ruling 1 (Mark): "switch + lean build seems right to me, but let's document as an un-informed decisions for a rust expert to be sure to review in case they want to follow a different course for other reasons."

So this is the working decision, and it is marked open to an expert's review in the Guide and in ../qnative's README: neither Mark nor the agent that wrote this plan is a Rust engineer, and the engineers may prefer another course (a separate binary, a workspace split, a runtime plug-in) for reasons this plan does not see. What was weighed:

A. Switch onlyB. Switch + lean build (ruled)C. A separate dev binary
What the customer runsone binary, dev offa binary with no dev codethe app, plus a second program when wanted
At rest, dev unused≈ 0 CPU, ≈ 0 MB, +1–2 MB on disk (est.)exactly 0exactly 0
What we can say"Off by default; does no work until switched on.""The production build contains no developer tooling," checked in CIthe same as B
Cost to usnoneone seam, a second CI buildtwo windows, two processes (+50–80 MB when open), dev pages cannot link into app pages
Riska claim we must keep proving by measurementcfg creep if the seam is wrongsplits the product

The saving is small. What B buys is a claim that can be checked rather than argued, which is the thing worth having with a client who measures.

Allowed, and on

Ruling 8 (Mark): "All of the above. If dev settings are not allowed, no presence is seen. If dev settings are allowed, the user can still toggle off."

Two questions, kept apart:

  1. Is developer mode allowed here? Decided outside the interface. The lean build: never, the code is absent. The full build: by --dev or QPOINT_DEV=1. Not allowed means no presence at all: no Dev group, no toggle, no menu item, no hint.
  2. Is it on? The person's own toggle, visible only where it is allowed, remembered across restarts. Off means no Dev group, no thread, no socket, no timer; the toggle stays so it can come back.
  • The toggle needs a seat outside the Dev group, since the group disappears when it is off. Proposed: an item in the app's menu and the tray menu. To confirm at the Phase 0 review stop.
  • A natural third lever, proposed and not ruled: a key in the sensor's config, so a company can forbid developer mode on its machines whatever the build.
  • One fix is owed today: the shell builds the Tools and Components views at startup even when developer mode is off.
  • The lifecycle rule holds for every stream page, gated or not: a feed exists only while its page is open. Leaving closes the stream, drops the buffer and stops the sampler. Buffers are bounded by count and bytes. It matters most for Events, which a customer on the lean build also has.

The seam, and the burden test

Developer mode becomes an extension the app does not know about:

  • A small protocol: an extension is a sidebar group and a list of pages, each { path, label, icon, build }. The router gets one generic route for an extension page, by path; the two Dev* variants go. The component docs already use this pattern (layer::demos::Doc).
  • A new crate, devmode, holds the seven Dev pages and the homage. It depends on layer, nozzle and types.
  • The binary takes devmode as an optional dependency behind the feature dev, and registers it in one line. Lean is the default build; we run --features dev.
  • Because a page is a registration, moving one between developer mode and the ordinary app (Live, if it earns a wider audience; Config, when it becomes V1's) is a one-line change.

This also tidies the app: the Dev code leaves crates/app. Mark's condition was that the gate must not mess up the structure or become a burden, so Phase 0 applies a test, and failing any line means we ship A and say so:

  1. The app crate contains exactly one cfg(feature = "dev").
  2. No page outside devmode needs a cfg to compile lean.
  3. Both variants build and pass their stories in CI, and a check proves the lean binary carries no dev strings.
  4. Moving today's Tools and Components behind the seam takes one sitting, and leaves the Inventory pages untouched.

Sensor (a Dev page): where the data comes from

Today the Tools page shows the source as read-only text, "chosen once at startup". Mark's first addition makes it a working control, on its own page (ruling 6).

Ruling 3 (Mark): "At this level, the user is only choosing between the real sensor and fake data. I don't see any benefit or logic in connecting to a plane source of the data." Agreed in the walk: anything the plane knows about this machine, the sensor told it. So the page has two choices, and the plane is a connection (company config, messages, grants), not a source of this machine's data.

  • What this changes in earlier rulings. c130 D6 and c131 R2 name three sources behind one switch. The switch stays one per process and never per page. On the page it has two positions; the plane transport stays in the code for tests and for plane-only reads. One caveat is recorded for Tyler and Kamal: the Inventory pages read rollups qdash computes on the machine, and if those leave the sensor with qdash, the plane may become the only place they exist.
  • Changeable while running. In developer mode the source can be changed from this page; it swaps the nozzle for the process and every page re-reads. c131 D16 (ruled the same day) already describes the one re-open path: replace the RepoHandle global, bump a generation, pages observe and reload, and a read that finishes late is dropped. This page is the seat of that setting; whichever cycle builds the path first, the other uses it. Precedence: flag, then environment, then the saved choice, then the build's default. The footer keeps naming the source. The lean build has no page and reads the sensor on this machine.

Synthetic, and its variations

A synthetic choice is three parts: a world (the data at rest), a condition (what is going wrong) and a stream (what plays on Live and Events). Mark's three families map onto them:

FamilyWhat it isExamples
Specific type of usageworlds, and stream scriptsfresh (day 0), first-week, meridian-day90 (the default: a quiet laptop), power-user; streams: a quiet day, a coding session, MCP-heavy, a file storm at high rate (the stress case)
Violationsthe pilot world and the drillpriya's laptop, and c131 D15's drill action, which injects one of three events into the stream: the paste scare, the secret-file read, the incident
Errorsconditions, applied over any worldsensor not running, slow answers, a 503, a shape that does not match, the stream lagging then cut
  • Three worlds are bundled today (meridian-day90, minimal, fresh). c131 D15 (ruled the same day) recuts the default to a quiet laptop and keeps priya's as pilot; the others are named in c131's synthetic-data.md and cut with scripts/cut-world.py. This page lists whatever worlds are bundled; it names none itself.
  • Conditions are a wrapper around any nozzle, so every error is drawn by the page's real honest state (R5) and nothing is faked in a view (R1).
  • Streams are recorded JSONL in the current envelope, replayed at a chosen speed through the same frame parser the live source uses. The seed's runs.ndjson is in the retired envelope and needs converting once.

Attach to Sensor

This machine by default (found through the lock file), or at an address (ruling 3): a sensor on a Linux box, a VM or a container. The sensor answers only on loopback and has no login, so an address means the near end of an SSH tunnel, never an opened port. The transport that reads a base URL already exists and is reused.

Validation is a ladder, and each rung is a state Mark designs; the semantic he asked for is power and connection:

RungThe checkSays
Not foundno lock file in the data dir (this machine only)no sensor on this machine
Foundlock file present, its pid alive (this machine only)installed and running
AnsweringGET /api/stats returns 200connected; version and build
Streamingthe event stream is open, keep-alive under 15 s oldlive; events per second, last event age
Lostwas connected, now retryingreconnecting, with the attempt and the reason
Refused403 (host guard) or 503 (no live hub)reachable but not serving; what to change

The same checks run from the command line as a doctor (--check-sensor), so Kamal can see which rung fails without opening a window.

Config (a Dev page): where the configuration comes from

Mark's second addition, on its own page (ruling 6), built so it can become V1's Config page with little change. What qcontrol has today (crates/qconfig): one config.yaml, local include globs such as conf.d/*.yaml, and a built-in stock document used when the file is missing. It loads no configuration from a URL.

ModeThe person's sentenceWhat the app doesStanding
Manual"I receive my configuration locally."shows the file found, as written and effective; edits locallythe file and the includes exist; who may write them is a question for Kamal
Qplane"I'm part of a company; my config comes from the company plane."takes a plane address and a credential; shows the config read-onlythe credential exchange is unruled (c131 D13, c134); built against the fixture plane with a token
Endpoint"Start me from a documented default."lists the defaults published at an address, fetches one, shows it, validates it, and applies it on confirmationnothing exists; the catalog is a fixture until the docs site publishes one
  • Ruling 4: a starter, fetched once. Any https address works. The file is fetched, shown in full, validated and confirmed by the person, then written locally with where it came from (address, time, hash). It then behaves as Manual; an update happens only when the person fetches again and sees the difference. A config decides what the sensor intercepts and enforces, so an address that could change it silently would be a remote control. Following an address over time is not in this cycle.
  • A fetched default can land as an include file, which uses qconfig's existing mechanism and leaves the main file alone.
  • On the synthetic source the three modes act on a synthetic config and write nothing to disk (R4's one guard).
  • Until this page becomes V1's, the lean build has no config screen.

Live: replicate, then improve

Replicate. The regions above, on the kit's virtual table, resizable panes and tree. The 13 pure modules are ported with cross-language test vectors generated from the TypeScript (previews, presence, the tally's CSV, filters), so the native page and the web page agree by test.

Improve, client only (this cycle):

  1. A real pause. The web's "Paused" only stops scrolling; rows keep arriving and evicting. Freeze the view at a point in the stream, as the TUI does, with a count of what is new.
  2. Honest states. Not running, refused, reconnecting (attempt and last-event age) and filtered-to-nothing are four states, not one empty message.
  3. A pipeline that scales: a ring buffer of raw frames, parsing on demand, counts kept incrementally. The web redoes work over the whole buffer on every flush, which is what holds it at 5,000 events.
  4. A tally that works on today's stream, keyed by process, harness or session.
  5. Filters for severity, process and session; search that reaches the payload.
  6. Keyboard navigation, milliseconds and relative time, export of the filtered set as JSONL.
  7. Masking of payloads documented as secret-bearing (installation.details) before they are shown or copied.

Stretch, client only: a time histogram to scrub within the buffer; tailing a growing file.

Needs the sensor (questions for Kamal): a cursor so a reload or a lag does not lose events; a gap marker with a drop count; a filter on the server; a snapshot for the new subscriber only.

Mark notes the firehose "is nice to have and arguably is also impressive to the non dev user". It is built in developer mode; the seam makes promoting it a one-line change if it earns that.

Events: the layer of meaning

Ruling 2 (Mark): "Let's build both. The firehose live view is nice to have and arguably is also impressive to the non dev user. But the quieter / curated events page is still relevant and provides a layer of filtering and meaning on top of the same underlying firehose event stream. This page is a layer of abstraction and meaning."

So c131 D6 holds and gains a neighbour: Events stays an ordinary page of the app, and Live is the raw instrument beneath it. One stream, two altitudes.

What "meaning" is made of, from keys already on the wire (session id, call id, exchange id, entity id):

  • Rows are activities, not records. A turn of a harness, a tool call with its decision and result, a request with its response and latency, a finding. Not one row per frame.
  • Volume becomes a count. Four hundred file.read events are "read 400 files in src/".
  • What matters rises. Violations, errors and decisions are shown first; routine traffic is quiet.
  • Every row opens to its evidence. The raw events behind an activity, and a jump to Live with the filter set where developer mode is present.

This page needs Mark's design before it is built: a brief and a mock first, then the build (Phase 4). The correlation logic it rests on is written once and tested against the recorded streams. V1's Status page is not touched by this cycle and stays on c131's plan.

Monitor: replicate the TUI

Six native views (Overview, Qcontrol, Workers, Config, Inventory, Log) and the link to Live.

  • Keep: the split and its cross-links (worker, events, target, inventory), the lifecycle timeline with a duration per phase, own-versus-inherited behavior values, problems-first health, pause with a count, the clock ↔ age toggle.
  • Drop: what only a terminal needs (the minimum-size screen, the footer key legend, clipboard escapes, stderr capture) and "quit stops the monitor": closing a viewer must never stop the sensor.
  • Improve: real charts with hover and a time range; sortable tables; search on Log; panes instead of modal pagers; and one addition: the app's own CPU and memory beside the sensor's, so the cost of the app and of developer mode is visible in the place a developer already looks.
  • On each source. synthetic: every view, fully, from fixtures in the proposed shapes. The sensor today: Inventory and the event-derived parts of Overview; every other region draws "this sensor does not serve this yet" with the name of the missing feed. As Kamal lands a feed, its region lights up without a page change.

The homage

Ruling 5: console only, fully hidden. Nothing on the page credits the TUI. The homage is found only by someone running the app from a terminal:

  • The first time Monitor opens in a session, the process prints the TUI's brand bar to stderr in block characters (qcontrol monitor DEV · ● live · pid … · up …) with a line under it: after the qcontrol monitor TUI, by Kamal Nasser. Drawn with qpixel's tools.
  • The TUI's keys work on the Monitor page: 1–7 switch views, t flips clock and ages, space pauses, ? lists them. Anyone with the TUI in their hands is at home.

The instruction pages (the Guide)

One source in Markdown, read in three places: in the app (Dev › Guide, on the kit's Markdown view), in the repository, and on the design site through the cycle's document links. Written first, in Phase 1, so Kamal's answers shape the build rather than follow it. While the pages are proposals they live in the app repository's cycle record (../qnative/cycles/c3.developer-mode/guide/), which is what the design site can render; at hand-off, made final from what was built, they move to docs/dev/ and the app embeds them.

PageWhat it answers
1. What developer mode isthe seven pages and Events, allowed versus on, the lean build, the measured cost, and what is open to an expert's review (ruling 1)
2. Run it on your machinebuild, --dev, choose the source on Sensor; attach to this machine or an address; the ladder and what each failing rung means; the doctor
3. Live and Events: the contractthe endpoint, the frame, the envelope, the families we read, the correlation keys we rely on; the four asks, each with why and the default we would accept
4. Monitor: the contractper view: the proposed type (in TypeScript, the way qdash types a route; what MonitorView, WorkerView and HealthRow hold today is the reference), its fixture, the route the app expects, what is missing today
5. Config: three modesthe states, what the app reads and writes, the questions
6. Synthetic scenariosthe catalog; how to add one; how to record a real session on his sensor into a scenario
7. Footprintthe recipe (strobe), the readings for the app and for the sensor with and without a subscriber

The Monitor feed, as we would propose it. c131 D17 (ruled the same day) settles the mechanism: endpoint-only shapes become qdash routes, typed in TypeScript and generated into Rust like the other 38, never hand-written and never qcontrol's internal structs. It names six shapes; three of them (SensorState, Resources, LogLine) are Monitor's. The Monitor needs four more by the same mechanism: the worker table with its lifecycle, health, the process tree, and targets with behaviors as the monitor has loaded them. Until the routes land, the proposed TypeScript types live with the Guide and generate the Rust and the fixtures, so the mechanism is the same from the first day. Two things from the survey go to Kamal with the ask: serving these today means the dashboard subsystem is on, with its SQLite projection, and the TUI's rule is worth keeping (no subscriber, no work). This gives a concrete form to question 6 of c134's kamal-prep.md (what the app reads locally once the dashboard's UI leaves).

Reconciled 2026-11-12 (c143, on Mark's word). The four further shapes became two: health joins /api/sensor (SensorState's state and degraded are its rollup) and the targets and behaviors as loaded join /api/config's EffectiveConfig, so each route is asked for once. The Monitor's other needs are optional fields on D17's shapes: the monitor's pid, a row per process with 1 Hz samples behind ?samples_since=, the log's target and a sequence number behind ?log_after=. Its own routes are /api/monitor/workers and /api/monitor/processes. The fields live in the bridge (app/data/worlds/bridge/types.ts, mirrored in qnative's crates/types/src/bridge.rs); the Guide's page 4 has them in full.

What "hooking it up" means for him. No page changes. Either the sensor serves a shape where page 3 or 4 says the app looks, or he writes one transport function in nozzle. The doctor reports each feed as served, missing or mismatched.

Questions for Kamal (page by page in the Guide; the short list):

  1. The monitor view as routes beside the six of c131 D17: which of the Monitor's fields on D17's two routes, and its two routes of its own, can he serve (four further shapes until the 2026-11-12 reconciliation), and does the loopback API stay as the sensor's local read surface once the dashboard's UI leaves?
  2. The monitor runs elevated. Can a user-level app read the lock files and reach the control socket?
  3. Resource samples: can the monitor produce them? A user-level app may not be able to read an elevated process.
  4. A log tail as a stream, at what default level?
  5. A cursor or replay on the event stream, and a gap marker when a subscriber lags.
  6. A snapshot for the new subscriber only, in place of a re-scan broadcast to everyone on every connect.
  7. Can the app attach without the dashboard subsystem and its SQLite?
  8. Config: who writes an include file, does the monitor pick it up without a restart?
  9. A worker or run id on the event envelope, so events attribute to a worker without walking the process tree.
  10. Does the app depend on the qevents crate, or keep a tolerant envelope and decode payloads on demand? We lean to the second against version skew.

Getting him the code (ruling 7): a private GitHub repository now, under the provisional name; a later rename redirects. It also lets the three-OS CI workflow run for the first time. Creating it and the first push wait for Mark's word on who does it.

Phases (a review stop after each)

#PhaseDone when
0Measure and seam. Fix the eager construction; move Tools and Components behind the extension seam; allowed versus on, with the toggle's seat; build lean and full; read four states with the README recipe (lean; full, off; full, on, nothing opened; full, Components shown). Apply the burden test.A table of readings; B confirmed or A taken; the note for the expert's review written
1The Guide, v0, and the stream core. The seven pages as proposals; the sketch; types for the event envelope and the monitor view; subscribe on the nozzle; the scenario model. Sent to Kamal.Kamal can read what we will ask of the sensor
2Sensor. The runtime switch, the scenario picker, the condition wrapper, attach (this machine, an address) with the ladder, the saved choice, the doctor; a story per state.Both sources and every scenario selectable in the app; every rung drawn
3Live. Replicate with test vectors, then improvements 1–7. A reading under a stream at 10, 100 and 1,000 events a second.Parity with /live on the current schema, plus the seven; the cost of an open Live page known
4Events. The brief and the mock for Mark's design; the correlation logic; then the page.Mark's design built on the same stream; each activity opens to its raw events
5Monitor. Six views on synthetic; the sensor with honest gaps; the console homage and the keys.The TUI's seven tabs answerable in the app on synthetic data
6Config. Three modes on synthetic and the fixture plane; fetch, show, validate, confirm for Endpoint; a fixture catalog of defaults.The three sentences each lead somewhere honest
7Hand-off. The Guide made final from what was built; a session on Kamal's machine; each feed he serves wired; the sensor-side readings taken; the record.Live and Events run on his sensor; the Monitor gaps are his list, not ours

Every phase follows c2's loop: build, stories light and dark, captures published to the atlas, the record in both repos. The structure is drawn once as a sketch page (c138-developer-mode), lands in ## The shape of the cycle record, and the cycle gets its story, per the c130 rule.

How this sits with work in flight

  • qnative c2 (the Tour stretch) held uncommitted edits across crates/app while this plan was written, so Phase 0, which changes the router, was set to wait. It committed the same day (9d37433); Phase 0 builds on that commit.
  • c131 stays the V1 build. This cycle takes developer mode and the Events page; c131 D6's "Live view is the Events page's live stream" is amended by ruling 2 (Events curated, Live raw), and R2 and c130 D6 by ruling 3 (two positions on the page). Both records carry the amendment.
  • c131 D15–D17, ruled in another session on the day this plan was written, are taken as given: the default world is a quiet laptop and priya's is pilot, with a drill that injects a bad event (D15); the world is chosen in the app's settings through one re-open path (D16); endpoint-only shapes become typed qdash routes (D17). The Sensor page is D16's seat, the drill is this plan's Violations family, and the Monitor's shapes follow D17's mechanism.
  • c134 kamal-prep.md question 6 and gap 26 are this plan's Monitor feed and stream cursor. The Guide replaces neither; it makes them specific.
  • c0: no page or crate bakes in "qnative"; devmode is a working crate name.

Rulings (the walk, 2026-11-11)

#CallRuling
1The gateSwitch + lean build, recorded as an uninformed decision for a Rust expert to review; they may take a different course for other reasons
2Live, Monitor and the V1 pagesBuild both: Live as the firehose, Events as the curated layer of meaning on the same stream
3The plane as a data choiceNot on the page: the choice is the real sensor or fake data. Attach takes this machine or an address
4Endpoint configA starter, fetched once
5Credit and easter eggConsole only, fully hidden; the keys still work
6Where Sensor and Config sitTwo pages in the Dev group
7Kamal's access to the codeA private GitHub repository now
8Switching it onAll three: flag, remembered setting, visible toggle. Not allowed, no presence; allowed, the person can still turn it off
9Home of the workIts own design cycle (this one), with qnative c3 as its twin
10The lean build's info panel (qnative c6; c3's call 14)(b), Mark, 2026-11-11: the panel's row saying this build has no developer mode stays; the lean check's marker is words only devmode carries, not its name

The toggles' seat, Mark's idea (2026-11-11, night)

Open the info panel (c131's Info View, the Tour stretch) and find two toggles there, settable while it is open: Help Mode (on / off) and Dev Mode (on / off). Help Mode is the guide layer staying on as the person moves, the panel's explanations and pointing on every page; Dev Mode is developer mode's switch, the same one the tray menu carries, shown only where it is allowed. The panel is the one place both live; the tray item stays. Built as a quick local sprint on a branch cut from c3/phases (qnative), no deeper plan; the sprint's note says what Help Mode came to mean. One reading to check with Mark: "set when it is open" means shown and settable in the open panel, not switched on by opening it.

Open after the walk

  1. The expert's review of the gate (ruling 1): who, and when. Phase 0's readings and the burden test are what they will be shown.
  2. Who creates the repository and pushes (ruling 7): Mark, Tyler, or the agent on Mark's word. Done 2026-11-11: the agent on Mark's word; qpoint-io/qnative, private, main pushed.
  3. The Events design (ruling 2): Mark's, from a brief written in Phase 4. Whether Live is ever shown outside developer mode is left open by his note.
  4. The toggle's seat (ruling 8): the app menu and the tray are proposed.
  5. A config key that forbids developer mode on a company's machines: proposed, not ruled.

Not in this plan

  • Any change to qcontrol. The feed is asked for, not built here.
  • The real plane credential exchange (c131 D13, c134).
  • V1's Status and Connect pages.
  • Following a config address over time.

Qpoint Brand Style Guide