Reaktor Instrumentation Plan
One plan across six layers. It exists because the same missing pieces block DevTools, profiling and telemetry, and doing them per-area produces three incompatible models of the same running system.
Status: reconciled 1 October 2026; capture scope extended 3 October. This page retains the September numbered dependency register; its dated capability comparisons and estimates are historical design, not a current implementation inventory or calendar commitment. The current gates below supersede the original phase-zero and schedule assumptions. Operations owns the source-grounded system map.
0. Current gates — complete the existing substrate
| Priority | Existing substrate | Required delivery evidence |
|---|---|---|
| P0.1 | Real SDK, completed port spans, transport and shared observability | Same real graph operation locally and remotely with exact artifact/source identity; serializer preserves events/links; failures/loss visible |
| P0.2 | Required-measurement manifest | Release-owned probes/units/targets/revisions; no collector and measurement failure block rather than pass |
| P0.3 | Native device vitals and Crashlytics adapters; legacy debug Measure integration pending removal | Named build/SDK/device qualification, release symbols, graph join keys and journal transport per Crashlytics acceptance; separate timing/resource and leak/retention evidence |
| P0.4 | Port interception, typed attachments, telemetry plan teardown | Measured overhead and boundary coverage, reference-escape disclosure, cancellation/expiry cleanup |
| P0.5 | Keploy + R2 portability | Exact candidate/runner pin, isolated pre-state/effects and the send/retry/reconnect assertion |
| P0.6 | Selected shared WAE + Iceberg direction | Paired observation and durable journal/capsule before hot/archive expansion; ingestion/query visibility and reconciliation |
| P0.7 | Graph/port hooks, Java-agent configuration, Keploy and platform/instance evidence | Extend toward all supported JVM, kernel/eBPF, JavaScript, Worker, Kubernetes, state and client capture; retain per-collector identity, original artifacts, coverage/loss/overhead and qualified graph joins |
ClickHouse remains in its existing analytics and service-evidence roles. Do not build a new telemetry stack per Shadow workspace. The implementation order and runbooks are in Delivery gates; diagnostics are in Instance health, Profiling and Load & resilience.
The Capture fabric owns the expanded Shadow acquisition target. OTel spans and Keploy network recordings are foundations; deep JVM method/value/state capture, stronger JavaScript instrumentation and host/platform collectors need additional qualification and implementation. Reuse the plan/attachment/journal/receipt model while extending those capabilities. Recorded observations, replay controls and diagnostic artifacts remain distinct evidence uses.
September design register
Item IDs below remain useful for dependency tracking. Their original notes describe the 13 September proposal unless explicitly reconciled. They must be qualified against today's source before work is selected; the latest DevTools also supersedes the old pane-layout assumptions.
1. Layer A — reaktor-tooling
The model. Detailed design in Tooling Layer.
| # | Item | Notes |
|---|---|---|
| A1 | Attachment, fact vocabulary, Fidelity rung enum | commonMain, no I/O. Settles the ToolingTarget (buildable) vs rgp Target (running) name collision — recommend Attachment locally, Target on the wire. |
| A2 | Probe interface over three shapes: bounded command, stream, socket | DeviceTools becomes the first probe family; its argv grammar and SafetyClass are kept unchanged. |
| A3 | Adapter interface | DeviceInspection.viewTree becomes the first registered adapter. |
| A4 | AttachmentRegistry | Merges facts from N probes onto one identity; derives TargetCapability from which probes attached rather than from a declaration. |
| A5 | Port-forwarding probes | adb forward tcp:<port> localabstract:reaktor-rgp, idb forwarding. This is the entire device-reachability story; write no custom USB transport. |
| A6 | L3 stream probes | logcat follow, log stream, wrangler tail, kubectl logs as Flow<Fact> over the existing streaming SupervisedProcessExecutor. |
| A7 | Profile probes | simpleperf record, xctrace, async-profiler agent, JFR. Pairs with C2. |
| A8 | In-process agent | Small artifact the app links; sits on addPortEventListener and the lifecycle/backStack flows, opens the carrier socket. L4 and L5. |
| A9 | Correlation header | Stamp outgoing requests in reaktor-io's http client; emit a local WireObservation. The join that makes proxy bytes attributable. |
| A10 | HTTP proxy probe | Local mitm, device proxy settings, CA install. Revert on detach with a receipt; refused at prepare on a stable channel. |
| A11 | Debugger probes under Perturbing | JDWP first — one protocol covering Android and JVM — then lldb. Attach/detach, threads, frames, breakpoints, bounded variable reads under the Values grant. |
| A12 | Unify the write path | TaskPlanningPort.plan() ≡ EditDomain.prepare() → one Plan; add WriteTarget.Device; RunLedger becomes the single receipt store for device commands and graph patches alike. |
| A13 | On-device stores join Database mode | The run-as ls databases path already exists; join it rather than building a second browser. |
| A14 | Device and app pickers as permanent chrome | Promote out of the Devices subpane into the mode header. |
2. Layer B — the rgp/1 spine
The wire. Pulled ahead of the device work because the desktop reads through it even locally.
| # | Item | Notes |
|---|---|---|
| B1 | Frame codec over FlexBuffers | Commands, replies, events, cursors, credits. Everything is blocked on this. Specify Edit's payloads here even though it ships at B9. |
| B2 | WebSocket carrier | reaktor-io's WebSocket / PartySocket. Behind a carrier interface so the Switchboard slots in later without a rewrite. |
| B3 | Flow control in the carrier | Per-subscription buffer policy — conflate for state, drop-oldest for events — and droppedSinceCursor on every subscription. The producer never blocks on a subscriber. |
| B4 | Mesh domain | Discovery, attach/detach, capability negotiation, HELLO descriptors. |
| B5 | Graph read path | Describe, handles, resolve; bounded by depth and a node cap, with truncation reported in Coverage. |
| B6 | Graph event path | Cursors, producer-side selectors, credits — sourced from addPortEventListener and the lifecycle/backStack flows. |
| B7 | Revision domain | Contract fingerprint from HELLO descriptors, plus separate artifact, configuration and activation identities — the digest alone cannot identify an implementation (protocol §3). Unknown build identity stays unknown. This is what kills DevTools' revision: unknown. |
| B8 | Wire domain | Live cross-runtime traffic; CreditState.stalledForMs is the point — the failure no log line shows. |
| B9 | Edit domain | Over the existing GraphEditorSession / ChangeProtocol. Prepare, execute, cancel, conflict on preconditions. |
| B10 | Value domain | Last, and gated on the redaction contract (D12), which does not exist yet. |
| B11 | Durable Object room for Worker targets | workerd isolates are per-request; a switch in a stateless handler dies before anyone attaches. |
| B12 | MCP surface extension | Element, route and trace reads alongside the shipped workbench_selection. |
3. Layer C — reaktor-performance
Reports and several adapters now exist, including required measurements, Crashlytics and legacy Measure. This register names the broader target; current per-platform coverage belongs to Performance overview.
| # | Item | Notes |
|---|---|---|
| C1 | Promote reaktor-flexbuffer/flamechart/ | Repair and qualify each script before promotion; the September review found orchestration/capture defects. Require original artifacts and a per-platform status matrix. |
| C2 | ProfileProbe per platform | Bound to A7; output into ReaktorProfileCapture carrying its rung. |
| C3 | Perfetto protos for captures | ReaktorFlamegraphFrame stays the normalised CI summary. Two artifacts: one gates the build, one you open when the gate fails. |
| C4 | Mobile frame vitals | JankStats, CADisplayLink → recordFrame(). (Phase zero.) |
| C5 | Memory vitals per platform | Android Debug.MemoryInfo, iOS task_vm_info, JVM MemoryMXBean → recordMemory(). |
| C6 | Budget catalog + CI gate | (Phase zero.) Error blocks, Warning annotates, Info dashboards. |
| C7 | Size budgets | Bundle / APK / IPA / JAR per build via the existing size-report.mjs, diffed against the previous release. |
| C8 | Benchmarking as a framework service | Promote the jvmBenchmark + kotlinx-benchmark/JMH pattern out of flexbuffer into a convention plugin any module can apply. |
| C9 | Cross-runtime benchmark report | One benchmark, five runtimes, one comparison artifact. Nothing in the industry does this and the harness already exists. |
| C10 | Driver runners for the remaining enum members | Runner inventory must use current source: Maestro/k6 and Keploy scripts now exist. Presence of a script does not prove backend qualification. Normalise all into ReaktorPerformanceToolRun. |
| C11 | Recomposition and skip counts | Needs a verified counting adapter for this Compose version. Gate it; a composition snapshot does not establish it. |
| C12 | App-size treemap with build-over-build diff | Flutter parity; builds on C7. |
4. Layer K — two kernel primitives
Revised 13 September 2026. The earlier version of this layer asked for five port shapes and a
facet system. That was wrong: ConsumerPort<Functionality: Any> already holds anything a shape
could express, so shapes add no expressive power. The real gap is narrower, and two general
primitives close it. Both generalise a pattern already present in the kernel, and both mean
later subsystems extend the graph without amending it.
K1 — implemented port interception
The graph-port interceptor chain now exists for supported synchronous/suspending lambda invocations. Telemetry installs hooks through a plan and owns teardown. The September proposed source edit and 154/76 inventory are no longer a pending prerequisite or a current coverage count.
Remaining work: quantify raw-reference escapes, observe a real end-to-end operation, measure overhead and qualify fault/capture behavior. An invocation hook does not automatically serialize function arguments, propagate remote context or provide safe effect substitution.
K2 — implemented typed attachments
Typed attachment storage and port semantics are present in the graph substrate. Telemetry policy and installed interceptors reuse them. Do not introduce a competing facet registry for Shadow. Qualify lifecycle ownership, replacement and uninstall on each participating entity/runtime rather than assuming every design use is implemented.
What this removes
| Previously planned | Now |
|---|---|
| Five port shapes as a kernel change | Optional sugar in a higher module, or an attached semantics tag. ConsumerPort<Anything> is instrumentable as it stands |
TelemetryFacet as a new kernel concept | An attachment |
PortInvocationEvent as a new kernel type | What an attached interceptor emits |
| "Instrument the three shapes" | Every port is instrumented uniformly |
K3 — ExecutionContext
OTel Context plus deadline, cancellation, auth and request. Wider than telemetry — deadline
and cancellation propagation justify it alone. K1 gives it the boundary at which to be read and
written.
What still needs more than K1 and K2
- Queue depth and subscriber lag require the holder to publish a gauge. Under K2 that is an attachment the actor or stream node publishes, read by the telemetry runtime.
- rgp cursors, credits and selectors stay at protocol level (Layer B).
- Policy compilation and hook installation are different jobs. A traversal visitor's
ExitScopescopes the traversal; K1 scopes the runtime call. D6 compiles the plan, K1 installs it.
5. Layer D — reaktor-telemetry
Revised 13 September 2026 against the graph-native model in Analytics and Telemetry §17.
Two decisions frame this layer. Telemetry is a derived overlay — G architecture,
Tᵣ execution, M(g, Δt) aggregate, with φ : Tᵣ → G — and never nodes in the application
graph. And OpenTelemetry serves as the internal model as well as the transport, so there
is no TelemetrySignal, no vendor-neutral internal type set and no separate mapping module. Reaktor's contribution is the
reaktor.* semantic convention, which is the smaller and more durable thing to own.
| # | Item | Notes |
|---|---|---|
| D1 | Shared signal destinations | Reuse existing OTel/Grafana infrastructure and ClickHouse consumers. Proposed Shadow hot/archive paths follow the paired-record contract; no migration or per-workspace stack. |
| D2 | SDK/export fidelity and distributed context | SDK and transport exist. Qualify events/links, bounded backoff, delivery health and W3C propagation across actual boundaries. |
| D3 | Destination decision | ClickHouse as the store, Grafana as a reader — not a second store. |
| D4 | reaktor.* semantic convention | graph.id, node.id, port.id, port.shape, edge.id, contract.id, snapshot.id on every signal; activation.id on spans only. This is the layer's core deliverable. |
| D5 | TelemetryFacet + inheritance Graph → Module → Node → Port/Edge | Policy only, never data. payloadPolicy defaults to None. Rides K3. |
| D6 | TelemetryVisitor → TelemetryPlan | Drops onto the existing PortGraphVisitor (Selector/Traverser/ExitScope). Cheapest item in the layer. The plan is itself a fact the workbench can render. |
| D7 | Shape-aware instrumentation | RequestPort duration/outcome/retries; MailboxPort depth and queueing-vs-processing split; StreamPort lag, drops, backpressure; StatePort mutation and propagation. ValuePort is never traced — it is a reference handoff. Needs K1 + K2. |
| D8 | Three modes: Minimal / Standard / Detailed | Standard is the default; Detailed is a temporary DevTools capture, never a deployed setting. |
| D9 | Transport instrumentation + span links for async | Queues, pub/sub and MailboxPort use links, not parent-child. A parent-child span would assert B's work happened inside A's, which is false and makes every actor trace lie about duration. |
| D10 | DB query spans | SqlAdapter.execute and Memgraph Cypher. Parameterised SQL only in db.statement, enforced by test. |
| D11 | TelemetryValidator — cardinality caught at authoring time | node.id and snapshot.id are safe dimensions because they are stable architectural ids; activation.id, user_id, trace_id are rejected by name. OTel's default limit is 2,000 combinations per stream. |
| D12 | Redaction from schema classification | @Telemetry.PII / @Telemetry.Safe compiled into redaction rules by D6. Also gates rgp Value (B10). |
| D13 | Baggage allowlist, empty by default | Baggage crosses service boundaries and can reach third parties; W3C forbids PII in tracestate. Strictest handling in the layer. |
| D14 | Two paths: development vs production | Full-fidelity ring buffer over the local carrier for Desktop; sampled, batched, redacted OTLP for production. One socket — the tooling layer's L4 carrier. |
| D15 | CaptureTelemetry / CaptureTrace as graph commands | Bounded duration, automatic revert, receipt in the ledger via A12. Right-click a slow node: next invocation · next 10 · errors only · slower than 500ms. |
| D16 | No self-instrumentation | The telemetry plane gets a kernel-level internal channel that bypasses graph instrumentation — not a marker someone can forget to apply. Distinct from self-monitoring, which is D18. |
| D17 | Release correlation on every signal | Deploy SHA, app build, remote-config version, experiment cohort. |
| D18 | Telemetry self-monitoring | Dropped spans, queue depth, ingest latency, exporter health. No data is an alertable state. |
| D19 | Crashlytics ↔ trace-id correlation | Crashes are currently uncorrelated with any span. |
| D20 | Work metrics | Proposed WorkStore admission/intent age, WorkRuntime attempt duration/retry/exhaustion, WorkScheduler opportunity/defer state, and broker/Workflow backlog and handoff receipts. Join operation, attempt/fence and provider identity; scheduling success is not effect completion. |
| D21 | M(g, Δt) overlay service | The aggregate query behind every Desktop overlay — latency, errors, traffic, saturation, cost. Five panes currently want their own metrics model; they should all read this one. |
| D22 | Unblock the 17 signal subpanes | 9 telemetry + 8 analytics, in declared-blocker order. telemetry/sources needs "nothing" — first. |
Scope note. This layer fixes on OTel for runtime and performance telemetry only. Product analytics keeps its sink-flexible design; the four signal classes stay four classes.
6. Layer E — DevTools and the workbench
Revised 13 September 2026 to the investigation model in DevTools §12. DevTools becomes an investigation workspace: one shared subject, target set, time interval and evidence context, read by six surfaces and a universal drawer. The nine subpanes it replaces mixed objects, lifecycle stages and diagnostic tools in one list.
E0 — the shell
| # | Item | Notes |
|---|---|---|
| E0.1 | InvestigationContext | A five-field extension of the shipped WorkbenchSelection — add targets (plural), revisionVector, time, evidence, layers. Subject, related, version, resolution and history already exist. |
| E0.2 | TimeContext as the second universal dimension | Brushing an interval filters Traffic, Logs, Graph, State, Findings and Memory at once. Needs the same monotonic-version and late-answer discipline as subject selection. The strongest single idea in the revision. |
| E0.3 | Six surfaces + universal drawer | Inspect · Traffic · Timeline · State · Memory · Recorder; drawer carries Console · Logs · Source · Findings · Problems · Tasks over any surface. |
| E0.4 | Permanent header chrome | App, environment, target mesh, revisions, live/recording, capture, interact/inspect, overrides, agent. Devices stops being a subpane. |
| E0.5 | reaktor.pen + pen-truth.json migration | panes.families.devtools.subpanes pins the nine names and MachineSignalPaneCoverageTest asserts it every build. The design file is the pane spec — six boards move first, code second. |
| E0.6 | Typed Finding + evidence | No Finding type exists in either repo today (only a private FindingRow in an auth pane). Prerequisite for Timeline findings, Explain, the agent handoff and .reaktortrace. Small, and blocks four things. |
| E0.7 | Live vs historical is visually unmistakable | Pinned evidence freezes revision, activation, capture time, sampling and coverage. Refreshing the live target never silently retargets a pinned trace. |
| E0.8 | Extension SDK | DevToolsViewProvider, InspectorProvider, TimelineTrackProvider, TrafficAdapter, FindingProvider, OverlayProvider, ConsoleCommandProvider, RecorderExporter. Named in the master plan, in no source file. |
E1–E6 — the surfaces
| # | Surface | Items | Depends on |
|---|---|---|---|
| E1 | Inspect | IDE handoff · source resolution for arbitrary composables · revision adapter · routes pin-to-pin through folded scopes · reverse impact · Ports tab (system-wide, not ancestor-limited) · Style provenance token → theme → recipe → component → local · a11y findings | B5, B7, E0.6 |
| E2 | Traffic | every boundary crossing — HTTP, WebSocket, rgp, FFI, JNI, actor, queue, bus, worker, DB, GraphQL, storage · Cause (initiator chain across process and language) · per-row jump to Graph, Timeline, Source | A9, B8 |
| E3 | Timeline | synchronised tracks (Unreal Insights) × three readings — flame, call tree, bottom-up (Chrome) · OTel span ↔ graph entity ↔ source ↔ activation join · Findings attached to measured events · time brushing drives E0.2 | C3, D2, E0.6 |
| E4 | State | actors · machines · state ports · flows · stores · preferences · SQLite · projections · journal and diff first · live machine with event log · jump-to-state gated on determinism and effect isolation | K1, K2, B10 |
| E5 | Memory | heap · allocations · ownership · snapshot diff. A graph dependency cycle is not a heap retain cycle and must never imply one | C5 — zero foundation today, sequence last or descope to snapshot-diff |
| E6 | Recorder | semantic actions not coordinates · element id → intent → expected effects · assertions · replay · export to Maestro, Playwright, scenario, JSON | E0.1, C10 |
E7 — cross-cutting
| # | Item | Notes |
|---|---|---|
| E7.1 | Graph breakpoints, two classes | Observation does not halt — capture, notify, mark the timeline; production-safe and the default. Pause may suspend and is local/debug only, mapping onto the Perturbing capability. Conditions are graph-shaped: port invoked, state enters Error, effect kind, route entered, latency > N, outcome Error, queue credit zero, revision mismatch, named Finding. |
| E7.2 | Typed Console | $0 over the graph subject; routes, trace, watch, graph. Not an eval. |
| E7.3 | Fault injection | delay, fail, drop, rewrite in/out, bandwidth, disconnect after N frames — on any port, not only HTTP. |
| E7.4 | Environment overrides | theme, locale, text size, motion, network, CPU, location, permissions, applied live with a conspicuous badge. |
| E7.5 | Everything mutating goes through one path | E7.2–E7.4 all route prepare → validate → authority → approve → execute → receipt. No debugger back door. |
| E7.6 | .reaktortrace evidence bundle | manifest, graph snapshot, targets, OTel, port journal, state events, screenshots, element trees, Perfetto capture, source maps, findings, redaction, coverage. An evidence bundle — it does not claim deterministic replay. |
| E7.7 | Agent handoff carries the investigation | subject, route, revisions, interval, spans, logs, source, findings, and coverage — what evidence is missing. Not a screenshot. |
| E7.8 | Explain this, everywhere | scope, relations, expansion, filter, coverage, sources on every derived view. Matters more once agents generate the queries. |
Sequencing note. E0 is the contract and comes before any surface work beyond E1's already-shipped core. E5 has no adapter on any platform and should be last. E0.5 and E0.6 are small and block disproportionately.
7. Industry parity map
Every capability from the DevTools comparison, and what closes it.
| Capability | Who has it | Closed by | |
|---|---|---|---|
| Live UI tree, click-to-select | all | shipped | ✅ |
| Provenance: declared vs recorded vs remote | nobody | shipped | ✅ |
| Agent/MCP read surface | Chrome (thin) | shipped, extended by B12 | ✅ |
| Attach to a program elsewhere | all | A1–A5, B1–B5 | |
| Source jump | all | E1 | |
| Log stream | all but React DT | A6 | |
| Network capture | most | A9, A10, B8, E2 | |
| Console / expression eval | most | B10, E7.2 | |
| Performance timeline / flame | all but React DT | C1–C3, E3 | |
| Memory / heap | most | C5, E5 | |
| Re-render attribution | React DT, AS, Xcode, Flutter | C11 | |
| Breakpoints | all but React DT | A11, E7.1 | |
| Props/state editable | React DT, Unreal, Flutter | B9, B10, E4 | |
| On-device stores editable | Flipper, AS, Unreal | A13, A12 | |
| Environment overrides live | Xcode | E7.4 | |
| Portable trace artifact | Playwright | E7.6 | |
| SQL over the trace | Perfetto | C3, E3 | |
| Time travel | Unreal, Redux | E4 — gated | |
| Cross-system route UI → service → DB | nobody | E1 | ★ |
| Field telemetry attributed to a graph node | nobody | D4, D17 | ★ |
| Port-shape-aware metrics (mailbox depth, subscriber lag) | nobody | K1, D7 | ★ |
| Telemetry projected onto the architecture graph | nobody | D21 | ★ |
| Time as a second universal selection dimension | Unreal (within one tool) | E0.2 | ★ |
| One investigation context shared by every surface | Chrome (DOM only) | E0.1 | ★ |
| Non-halting graph breakpoints safe in production | nobody | E7.1 | ★ |
| One benchmark, five runtimes, one report | nobody | C9 | ★ |
Deliberately declined
- 3D exploded hierarchy (Xcode, Android Studio) — low value for a Compose tree; the Layout tab and overlays answer the same questions.
- Retain-cycle analysis (Xcode's Memory Graph) — a cycle in a dependency or actor-message graph is not proof of a heap retention cycle. Needs runtime ownership and reference evidence. Declined until an adapter supplies it.
- eBPF (
telemetry/ebpf) — needs a collector on target hosts. Not in this plan. - Deterministic replay — needs captured state, version-compatible reducers, scheduling control and isolation from external effects. E4 ships the journal and the diff; rewind stays behind its own gate.
- Our own profiler. We wrap simpleperf, async-profiler, Instruments, Lighthouse, k6. The existing principle holds.
8. Build versus adopt
Java libraries cover the JVM and Android. They cover nothing on iOS, Native, browser JS or
Workers. Since the desktop workbench and the server are JVM, and Android is close enough, a
large fraction of the tooling side is available off the shelf. The project already leans this
way — Pulumi, io.kubernetes:client-java, google-cloud-pubsub, JNA, commons-compress, H2,
LSP4J and the Postgres and Neo4j drivers are on the classpath today.
| Item | Adopt | Notes |
|---|---|---|
| D2, D11 SDK, OTLP, W3C, conventions, cardinality | opentelemetry-java | Views, aggregation and cardinality limits with overflow counters are already in the SDK, so D11 becomes configuring a view. Use it directly on JVM and Android; the experimental io.opentelemetry.kotlin path then only has to carry iOS, JS and Workers, where no alternative exists. |
| D10 DB query spans | opentelemetry-java-instrumentation | JDBC instrumentation covers the server side. |
| D3 ClickHouse client | clickhouse-java | Official, maintained. |
| C1, C2 JVM profiling | async-profiler Java API (one.profiler.AsyncProfiler) | In-process, so the JVM flamechart script becomes a library call. |
| C2 JVM recording | JFR, jdk.jfr + jdk.jfr.consumer | In the JDK. Zero dependency. |
| C3 Perfetto emission | protobuf-java + published protos | No writer library exists. The trace processor has an HTTP API for reading. |
| C7, C12 artifact size and diff | diffuse | Stable with low commit activity — vendor rather than depend. apkanalyzer is the heavier alternative. |
| A5, A6 Android device, logcat, forwarding | ddmlib (com.android.tools.ddms:ddmlib) | Google's own, what Android Studio uses. Covers discovery, logcat streaming, shell, file transfer and port forwarding. |
| A11 debugger, JVM and Android | JDI (com.sun.jdi) | In the JDK. Attach, breakpoints, frames, variables, for two of the targets, with no dependency. |
| A10 proxy and CA | BrowserUp Proxy or LittleProxy, + BouncyCastle | Smaller projects with patchier maintenance than the rest of this list. Pin and vendor. |
| E5 heap, retained size, ownership, diff | shark (Square, under LeakCanary) + HotSpotDiagnosticMXBean.dumpHeap | Kotlin, maintained, runs on JVM and Android. Eclipse MAT has a headless API if dominator analysis is wanted. |
| E1 source index read side | LSP4J | Already a dependency at 0.23.1. |
| B1–B3 protocol plumbing | org.eclipse.lsp4j.debug | The same Eclipse project ships a full DAP implementation — JSON-RPC framing, launchers, async request/response. If the carrier accepts JSON-RPC beside the FlexBuffers codec, the desktop half of the protocol is largely written. |
| Apple device control | nothing | simctl stays a CLI. idb is Python with a gRPC surface, so generating a client from its protos is the only leverage available. |
Two consequences for the schedule. Use opentelemetry-java directly on JVM and Android.
And move E5 Memory forward for JVM and Android — it was scheduled last for having no
foundation, and shark plus a heap dump delivers most of the surface in days. It stays
unavailable on iOS, JS and Workers, stated plainly.
No library answer, correctly: K1's interceptor chain and K2's attachment capability (both small), the element-to-graph join, the investigation context, rgp's domain semantics, and every runtime that is not a JVM. The libraries cover the parts that are not the differentiator.
9. Historical sequencing register
The table below preserves the September proposal and dependency discussion. It is not the current delivery calendar: K1/K2, SDK/export, required measurements and native device adapters now exist. Use §0 and Delivery gates to select remaining work.
| Phase | Items | Outcome |
|---|---|---|
| 0 — week 1 | E1, C6, C4, D1 | The docs stop overstating the app. A developer sees real vitals and a real source jump. |
| 1 — weeks 2–5 | A1–A4, B1–B3, K1, K2 | The model and the wire exist, and a port invocation becomes an event. Nothing user-visible; everything else depends on it. |
| 2 | A5, A6, A7, B4–B6, C1–C3, K3, D2–D6, E0.1, E0.5, E0.6, E1, E5 (JVM/Android, via shark) | Devices attach. Logs stream. Profiles capture on five platforms. Spans reach a store. The inspector resolves real source. |
| 3 | A8, A9, A14, B7, B8, B11, B12, C5, C7–C10, D7–D10, D14, E0.2, E0.4, E0.7, E2, E3, E7.7 | Attribution works end to end: a pixel to a port to a service to a row, with measured traffic behind it. |
| 4 | A10–A13, B9, B10, D11–D13, D15–D17, E4, E6, E7.1–E7.6 | The tool mutates as well as reads: console, breakpoints, overrides, fault injection, portable traces. |
| 5 | C11, C12, D18–D22, E0.3, E0.8, E7.8, E5 on remaining targets | The long tail — parity items and the 17 signal subpanes. |
What K1 actually gates. Shape-specific instrumentation (D7), fault injection (E7.3) and breakpoints (E7.1) need K1. SDK installation, semantic conventions, bounded export, transport and DB instrumentation, redaction and self-monitoring do not. Schedule Layer D's export half in parallel with the kernel work.
The two gates worth defending. Phase 1 produces nothing a user can see and everything after
it depends on it; resist reordering around visible wins. And no item in phases 3–5 may present
a declared fact as an observed one — the fidelity ladder
and provesExecution = false are what keep this tool honest as it grows.
10. Review corrections, 13 September 2026
An independent review of the first version of this plan found eleven defects. All were accepted; four were verified directly against the source before acceptance. The corrections are applied in place above; this section records what was wrong so the same claims are not reintroduced.
Verified by running the code
- The five-platform profiling harness does not work as claimed.
bash -nfails onflamechart/generate-all.shat line 83, so it cannot run.profile-ios-sim.shruns macOSsample; its xctrace line is printed as a suggestion.generate-native.shtargets an iOS simulator binary, so no generic-native target is separately qualified.generate-android.shrunsadb shell rm .../perf.dataand thenadb shell simpleperf report -i .../perf.data, deleting the capture before reporting from it, with the error suppressed. C1 is a repair and qualification job, and the earlier "mostly a move" claim came from reading filenames rather than running scripts. - Phase zero's span pipeline did not exist.
GraphTelemetrycallsstartSpanandaddEventwith no.end()anywhere, so no processor will ever export it, andHttpAnalyticsSinkis app-owned code inbestbuds/modules/app/carrying a product-event schema. See P0.1.
Contract corrections
| Was | Now |
|---|---|
| B7 used a HELLO-derived digest to identify a revision | A bug fix changes handler behaviour without changing cell names, method IDs or schema fingerprints, so that digest cannot identify an implementation. Keep separate identities for semantic definition, contract fingerprint, built artifact, configuration and activation. The contract digest supports compatibility; artifact identity supports implementation comparison. Unknown build identity stays unknown. |
| A1/A4 fidelity ladder as a total order, capability derived from rung | Rungs conflate independent things. A read-only interactive target supports neither Edit nor Values; an attributed snapshot is one-shot; a streamed log can stay unattributed. Use independent dimensions — acquisition, freshness, attribution, coverage and loss, perturbation, supported operation, effective grant — with capability derived from the probe contract and then restricted by target support and session authority. |
| E7.1 observation breakpoints called production-safe | Non-halting does not bound overhead. A predicate can be expensive and serialisation allocates on a hot path. Require a bounded predicate budget, event and byte rates, ring-buffer cap, capture duration, payload policy and drop accounting, with overhead measured on a representative hot path and expiry independent of the desktop connection. |
| C6 budgets alone as a CI gate | The checker skips a budget with no matching metric, so thresholds without probes go green. Required-measurement manifest, per P0.2. |
| D4/D11 node and snapshot IDs as safe metric dimensions | Stability bounds churn, never count. Node × port × contract × snapshot × status can exceed a stream's limit, and snapshots accumulate across releases. Specify allowed dimensions and projected cardinality per instrument; use views, aggregation and overflow counters; keep high-detail identity on spans. |
| D9 claimed parent-child asserts temporal containment | It does not. OTel permits a message creation context as the parent for single-message processing. Links remain the default because OTel recommends them for messaging; the earlier justification was wrong. Test batching, fan-out, retries and delayed consumption, and allow an execution to cross trace IDs. |
| A10 "works on any build" | A user CA does not make modern Android apps trust it, pinning defeats interception, and the proxy policy already excludes production targets. Publish a platform and build capability matrix; treat TLS-unavailable flows as metadata-only. Persist original proxy and trust state before mutation, preserve pre-existing entries on cleanup, handle shared probe users, and record recovery pending when a device is unreachable. Unconditional rollback after a client crash cannot be guaranteed, so the recovery contract lands before the invasive probes. |
| B11 "workerd isolates are per-request" | Isolates serve multiple concurrent requests. A Durable Object can own the coordination room; that does not make every Worker request a persistently introspectable process. |
| C9 five-runtime benchmark parity | Capture identity alone does not establish comparability. Retain workload, build mode, tool version, device and runtime, warmup and repetitions, and publish a per-platform status matrix with real capture evidence first. |
| C3 Perfetto by moving files | A .cpuprofile, a sample tree or an Instruments artifact does not become a Perfetto trace through relocation. Keep original captures and add explicit conversion and coverage contracts. |
| §26.4 web vitals called qualified | They are approximations. CLS sums shifts across the page lifetime where the metric uses the largest session window; INP takes an unconditional maximum and folds in a first-input processing interval, where the metric groups interactions and discards one per fifty. Either qualify against the maintained web-vitals implementation or label the values as approximations. |
Sequencing changes
- Move shared
Finding/evidence and the agent investigation handoff earlier. E0.6 is in phase 2 and E7.7 in phase 3. Reuse the Agent Workspace schema rather than creating a second diagnostic finding model. - The first protocol slice ends in a visible bounded graph read plus one real observation over rgp loopback, without requiring every domain first.
- Policy compilation and hook installation are separate jobs. A traversal visitor's
ExitScopescopes traversal; K1 scopes the runtime call.
Related docs
- Operations & Reliability — current evidence, textbook and diagnostics.
| Doc | Relationship |
|---|---|
| DevTools | Layer E's design and its milestone definitions. |
| Tooling Layer | Layer A's design; §11 covers profilers as probes. |
| Performance and Observability | Layers C and D in detail; §26 is the verified starting state. |
| Graph Protocol | Layer B's specification. |
| Analytics and Telemetry | Layer D's design; §17 is the graph-native model and §17.13 the kernel prerequisites behind Layer K. |
| Graph refinement plan | Layer K's port shapes (§L1). |
| UI/UX audit | Where the missing observation plane was first recorded. |