← Cycles
Qnative (provisional) · c3

Developer Mode

activecreated 2026-11-11· last activity 2026-11-13· mode: archaeology (two tools that emerged from real use) into evolution, with one formula (the gate).

The app's developer mode, and the Events page above it. Live (qdash's live viewer, replicated on the sensor's current event schema and improved), Monitor (Kamal's monitor TUI, replicated), Sensor (the data switch as a working control), Config (three modes), Guide (how to hook a live sensor). Developer mode is an extension behind one Cargo feature, so a build can carry none of it; where it is allowed, the person can still turn it off. Events, the curated layer of meaning over the same stream, is an ordinary page.

New in this cycle: The lean build

Why

The two tools a developer uses to watch the sensor work are leaving or locked inside it: the live viewer lives in qdash, which is leaving qcontrol, and the monitor TUI is the sensor's own process in a dev build. This app is where they go. A customer who measures CPU and memory must pay nothing for them, so the cost is measured, the mode can be switched off, and a build can ship without it. Kamal then points the pages at a sensor on his machine, which works only if what the app expects from the sensor is written down feed by feed.

Objective

Done means, phase by phase (design's plan has the detail and a review stop after each):

  1. Measure and seam. The eager construction of the Dev views fixed; Tools and Components moved behind an extension seam; allowed versus on; lean and full builds read in four states with the README recipe; the four-line burden test applied; the decision written up for a Rust expert's review.
  2. The Guide, v0, and the stream core. The seven pages in guide/; types for the event envelope and the monitor shapes; subscribe on the nozzle; the scenario model.
  3. Sensor. The source changeable while the app runs; worlds, conditions and streams; attach to this machine or an address, with the ladder; --check-sensor.
  4. Live. Parity with qdash /live by cross-language test vectors, then seven improvements; readings under a stream.
  5. Events. Mark's design, built on the same stream.
  6. Monitor. Six views on synthetic data; honest gaps on a real sensor; the TUI's keys.
  7. Config. Manual, Qplane, Endpoint (a starter, fetched once).
  8. Hand-off. The Guide made final and moved to docs/dev/; a session on Kamal's machine.

Scope

  • Here:
    • crates/devmode (new: the seven Dev pages, behind the feature dev);
    • crates/nozzle (subscribe, conditions, the runtime swap);
    • crates/types (the event envelope, the monitor shapes);
    • crates/fixtures (stream scripts, more worlds);
    • crates/app (the extension seam: one generic route, one registration line; the Events page);
    • crates/stories (a story per page state);
    • cycles/c3.developer-mode/guide/ (the Guide while it is a proposal).
  • Read, never changed: ../qcontrol (the live viewer in crates/qdash/ui/app, the monitor TUI in cli/src/commands/monitor/ui/terminal on main).
  • Out: the feeds themselves, which are asked of the sensor's engineer; the plane's credential exchange; the Status and Connect pages.

Key Changes

  • kick-off (2026-11-11): this record and the Guide v0: seven pages written as proposals from two surveys of qcontrol (the live viewer and its backend; the monitor TUI) and this repository at c2's review stop 3. No code. Phase 0 builds on c2's Tour stretch commit (9d37433).
  • Phase 0, measure and seam (2026-11-11), at review stop 0 (review-0.md):
    • Developer mode is an extension the app does not know about. The protocol is layer::extension, the registry is app::extensions, and one Route::Ext(path) replaces the two Dev routes. A new crate, crates/devmode, holds Tools and Components and is carried only with --features dev, through one cfg in main.rs. The default build is lean.
    • Allowed (debug, --dev, QPOINT_DEV=1, full build only) versus on: a checked item in the tray menu, remembered in the app's first settings file (app::settings).
    • The eager construction is fixed: Dev pages are built when opened and dropped when left.
    • scripts/check-lean.sh checks the dependency graph and eight strings, with the full build as its control; CI runs it on three OSes once a remote exists.
    • The burden test passes on all four lines (line 3 on this Mac; CI not yet run), so B holds.
    • Read (strobe, an idle Mac, two rounds):
      • Binary: lean 23.2 MB, full 24.3 MB (+1.07 MB).
      • At rest: 60–63 MB quiet in every state with no Dev page shown (lean, full not allowed, full on, full turned off), at 0.002–0.006% of a core.
      • The Components page shown: 65.0 and 65.3 MB.
      • So the lean build buys about a megabyte on disk and nothing measurable at rest; looking at a Dev page costs a few MB.
  • Phase 1, the stream core (2026-11-11, on Mark's night ruling: Phases 1–3 in sequence, the calls collected at the end of Phase 3; built in ../qnative-c3 on c3/phases):
    • The connection (crates/nozzle): sse is a WHATWG event-stream parser, incremental, tested in every chunking. stream holds Subscription, an async channel a page awaits with no timer. Under it, connect speaks HTTP/1.1 directly to qdash's /api/events/stream, with the web viewer's reconnect schedule, and turns 403 and 503 into honest Refused states with the sensor's own reason; play drives the synthetic scripts. Dropping a subscription closes its socket within a 250 ms tick. Repo::subscribe() sits on top. The plane says it has no stream.
    • The scenario model (nozzle::scenario): world, condition and stream. Six conditions; one per process.
    • The data model (crates/types): the envelope and the four Monitor shapes are TypeScript beside the Guide (guide/types/), generated by scripts/ts-to-rust.py --from. Each field is marked read from code (with its qcontrol file and line) or proposed. events holds the vocabulary: 14 families, entity ids, severity, and RFC 3339 nanoseconds both ways.
    • The stream scripts (fixtures::streams): quiet, coding-session, mcp-heavy and file-storm, written to qevents' payloads as read from code. The full reference is filed in research/qevents-payloads.md.
    • The stream core (crates/stream, new): ingest ports qdash's ingestEvent, ingestParseError, sortedEvents and parseRunLog. It is held to the web by cross-language vectors: qdash's own TypeScript, bundled read-only, run over a 615-line corpus with edge lines (scripts/live-vectors.sh). All 615 agree, and so does the sort order.
    • Guide page 3 is now Live Activity: connection methods, the ladder, the data (read or guessed), the pipeline, the states and six asks. Page 6 records the built scripts.
    • 87 workspace tests pass, with no warnings.
  • Phase 2, Source (2026-11-11):
    • Dev › Source (crates/devmode/src/source.rs) is the plan's Sensor page, named Source because c5's V1 Sensor sits under This machine. It offers synthetic data (a world, a condition, a stream and its rate) or a real sensor on this machine or at an address. A choice applies at once through the one re-open path and is saved; the next start applies it unless a flag or the environment says otherwise. A story covers each rung, plus synthetic and an address.
    • host (new crate) answers the Rust expert's question 2. It holds RepoHandle, Generation, repo, generation and reopen, moved out of app::router (which re-exports them), and a Store of saved values the app installs over settings.json. The extension protocol gains an optional start hook.
    • The condition wrapper (nozzle::scenario::Conditioned): six conditions, each drawn through the real pipes.
    • The ladder (nozzle::ladder): Not found (including a stale lock), Found, Answering, Streaming, Lost and Refused, tested against fake sensors on loopback.
    • The doctor, app --check-sensor [--sensor-url …]. Run for real on this Mac, it found a stale lock (pid 54415). Against the seed qdash serve on :7882 it reached Refused with qdash's own 503 sentence.
    • Local::at attaches to an address, and the footer says sensor at …. The macOS data dir is fixed (com.qpoint.qcontrol).
    • Guide pages 2, 3 and 6 now say what is built.
  • Phase 3, Live Activity (2026-11-11):
    • Dev › Live Activity (crates/devmode/src/live.rs) is qdash's /live on the sensor's current envelope: a state line, a harnesses row, Events and Tally, a rail of severity, harnesses and families, a virtual table that follows the live edge, a detail pane, retention (5k, 20k or 100k), and file mode (open a JSONL run log; return to live). The page owns its subscription inside a task, so leaving the page ends the feed.
    • The seven improvements:
      1. a real pause, with a count of what is new;
      2. honest states (connecting, live with the rate and the last event's age, reconnecting with the reason, refused, no sensor, ended, filtered to nothing);
      3. stream::buffer: a ring of raw frames, a light head, incremental facets and view, rows on demand;
      4. a tally by process, session or type, beside qdash's own;
      5. filters for severity, harness and family, process and session, with search over the whole record;
      6. keys (↑↓ j k, space, t, Esc), milliseconds, clock or ages, and Export JSONL;
      7. stream::mask on the summary, the preview, the JSON and the copy.
    • Parity by cross-language vectors, all regenerated byte-identical by scripts/live-vectors.sh:
      • ingest and previews: 614 records;
      • presence: 99 checkpoints;
      • the tally: 63 views and 126 CSVs, byte for byte;
      • entity-meta and the projections: filters, counts, trees, chips, rows.
      • Four modules were ported by subagents against the vectors. The web quirks are kept, each marked.
    • Stories: eight, app-dev-live*.
    • Readings under a stream (README Footprint, Guide 07, review-3.md):
      • As first built, the page cost 28–34% of a core at 100 and 1,000 events a second, nearly all of it main-thread drawing at about 10 ms per redraw.
      • Leaving it returns the app to rest (0.003%, 6 threads).
      • The page now gathers arrivals for 250 ms and caches the rows on screen, so it redraws at most 4 times a second, the monitor TUI's cadence. The readings at that rate are owed.
      • The recipe is scripts/footprint/.
    • CI: Linux and Windows had failed on the stories crate. c5's cloud session fixed the cause, the crate's manifest (3725719), along with two Windows failures in c3's ladder and events fixture. Run 17 is green on all three systems, and it came into c3 by merge (78238ff), replacing c3's per-system exclusion. Phase 3's first run, on c5's push of 4ccf511, failed on Windows in the projections tests: the corpus read as CRLF counted one line more. Fixed in 948a9b5 (.gitattributes keeps the vectors LF; the trim no longer assumes LF), not yet pushed.
    • The calls: fourteen, in review-3.md (the fourteenth, the lean check after c6's merge, ruled (b) and closed by qpoint-io/qnative#2, 2c435e4).
  • The Monitor's shapes reconciled with the bridge (2026-11-12, design c143, on Mark's word): Guide 4 had asked for richer copies of three shapes c131 D17 already names (SensorState without a pid, Resources as totals, LogLine without a target), plus health beside SensorState's degraded and targets beside EffectiveConfig. Now one ask per route:
    • /api/sensor carries the Monitor's fields: the monitor's pid, a row per process in its tree with 1 Hz samples behind ?samples_since=, health with the last error (SensorState's state and degraded are its rollup), and the log's target and seq behind ?log_after=. /api/config's EffectiveConfig carries the loaded targets, behaviors and include globs.
    • The Monitor keeps two routes of its own, /api/monitor/workers and /api/monitor/processes (guide/types/monitor.ts; crates/types/src/monitor.rs regenerated). No dashboard URL: the app already holds the address it reached.
    • The fields are design's bridge (app/data/worlds/bridge/types.ts), mirrored in crates/types/src/bridge.rs, all optional, so V1's Sensor page and the bridge files are unchanged. Two tests pin it: absent reads as not served, and each served field lands. Guide pages 3 and 4 say so; question 3 asks whether the sensor samples always or only with a reader.
  • The firehose in qdash's grammar (2026-11-13, f19e679, pushed; design c138): Mark found qdash's /live "cleaner, better color coding, filtering". It was compared against main's /live @ 8677b44ed on this cycle's corpus, run from a read-only checkout on :3031. The Events view now draws as qdash's does:
    • each type as its family's chip (family_chip, from main's familyTone);
    • 44 px zebra rows, with time, line and summary in mono and the summary as key=value parts;
    • a column header;
    • qdash's filter card in place of the rail: the search, severity, active filters with Reset, and Harnesses and Events chip rails with counts.

    The seven improvements stay. Still open from design c138's Phase 3: the status pill, presence cards, the buffer cluster, the scroll-away pause, direction, the JSON tree, the tally pivots, drag and drop, and the Audit log. They're listed in design/cycles/c138.developer-mode/live-parity-plan.md.

Visual record

18 captures — heroes, page slices, and full-page renders. Click to view.

Phase 3 · Live Activity: qdash's /live on the current envelope, improved

2026-11-11· 7 captureslatest
Live Activity on a synthetic coding session: the state line (rate, the last event's age, kept and MiB), the harnesses row (qdash's presence, ported), the rail (severity, harnesses, families with counts), the firehose newest first.
Live Activity on a synthetic coding session: the state line (rate, the last event's age, kept and MiB), the harnesses row (qdash's presence, ported), the rail (severity, harnesses, families with counts), the firehose newest first.
A tool call opened: chips that filter to its process, session or type; the preview; the record as JSON with its arguments masked everywhere (summary, preview, JSON, copy) until revealed.
A tool call opened: chips that filter to its process, session or type; the preview; the record as JSON with its arguments masked everywhere (summary, preview, JSON, copy) until revealed.
Search reaches the payload (the web searches twelve projected fields): every record that names payments/client.rs.
Search reaches the payload (the web searches twelve projected fields): every record that names payments/client.rs.
Filtered to nothing is its own state: how many are kept, and how to see them.
Filtered to nothing is its own state: how many are kept, and how to see them.
The tally by process (also by session, by type, and qdash's own by run, which on today's stream is one Unattributed row).
The tally by process (also by session, by type, and qdash's own by run, which on today's stream is one Unattributed row).
No sensor (the condition 'not running' on Dev › Source): says where it looked and what to do.
No sensor (the condition 'not running' on Dev › Source): says where it looked and what to do.
Refused: qdash's own 503 sentence for a static qdash serve, drawn through the real pipes by the 'unavailable' condition.
Refused: qdash's own 503 sentence for a static qdash serve, drawn through the real pipes by the 'unavailable' condition.

Phase 2 · Dev › Source: where the data comes from, and the ladder

2026-11-11· 6 captures
Dev › Source, synthetic: a world, a condition (healthy, not running, slow, no live stream, a shape that does not match, lag then cut), a stream and its rate. A choice re-opens the data for every page at once and is saved.
Dev › Source, synthetic: a world, a condition (healthy, not running, slow, no live stream, a shape that does not match, lag then cut), a stream and its rate. A choice re-opens the data for every page at once and is saved.
This machine, Not found: no lock file in qcontrol's data directory. The ladder says what it checked and what to do.
This machine, Not found: no lock file in qcontrol's data directory. The ladder says what it checked and what to do.
Found: the lock file names a running pid, but nothing answers at its URL (the dashboard is off).
Found: the lock file names a running pid, but nothing answers at its URL (the dashboard is off).
Streaming: lock file, pid, /api/stats and the event stream all check out (the version is not served yet: c131 D17).
Streaming: lock file, pid, /api/stats and the event stream all check out (the version is not served yet: c131 D17).
Refused: a static `qdash serve` answers REST but has no live hub; the 503 sentence is qdash's own, as the doctor read it from the seed backend.
Refused: a static `qdash serve` answers REST but has no live hub; the 503 sentence is qdash's own, as the doctor read it from the seed backend.
An address: the near end of an SSH tunnel, never an opened port. Here an opened port is refused by qdash's host guard (403).
An address: the near end of an SSH tunnel, never an opened port. Here an opened port is refused by qdash's host guard (403).

Phase 0 · developer mode behind the seam, lean by default

2026-11-11· 5 captures
The lean build, the default: no Dev group, no tray item. Developer mode is not in the binary (scripts/check-lean.sh proves it: the dependency graph and eight strings).
The lean build, the default: no Dev group, no tray item. Developer mode is not in the binary (scripts/check-lean.sh proves it: the dependency graph and eight strings).
The full build with developer mode turned off in the tray: byte-identical to the lean window (SHA-256 of the two PNGs).
The full build with developer mode turned off in the tray: byte-identical to the lean window (SHA-256 of the two PNGs).
Dev › Tools, now a page of the devmode extension (crates/devmode), routed by its path through Route::Ext and built only while it is open.
Dev › Tools, now a page of the devmode extension (crates/devmode), routed by its path through Route::Ext and built only while it is open.
Dev › Components through the seam. Showing it reads 65.0 and 65.3 MB against 61.5 MB with nothing open: looking costs about 4 MB, carrying the code nothing measurable.
Dev › Components through the seam. Showing it reads 65.0 and 65.3 MB against 61.5 MB with nothing open: looking costs about 4 MB, carrying the code nothing measurable.
A Dev page asked for while developer mode is off lands on the overview; the group is gone.
A Dev page asked for while developer mode is off lands on the overview; the group is gone.

Artifacts

/cycles/qnative/c3/doc/review-0 (local dev)/cycles/qnative/c3/doc/guide/02-run-it (local dev)/cycles/qnative/c3/doc/guide/03-live-activity (local dev)

Qpoint Brand Style Guide