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

PriorityExisting substrateRequired delivery evidence
P0.1Real SDK, completed port spans, transport and shared observabilitySame real graph operation locally and remotely with exact artifact/source identity; serializer preserves events/links; failures/loss visible
P0.2Required-measurement manifestRelease-owned probes/units/targets/revisions; no collector and measurement failure block rather than pass
P0.3Native device vitals and Crashlytics adapters; legacy debug Measure integration pending removalNamed build/SDK/device qualification, release symbols, graph join keys and journal transport per Crashlytics acceptance; separate timing/resource and leak/retention evidence
P0.4Port interception, typed attachments, telemetry plan teardownMeasured overhead and boundary coverage, reference-escape disclosure, cancellation/expiry cleanup
P0.5Keploy + R2 portabilityExact candidate/runner pin, isolated pre-state/effects and the send/retry/reconnect assertion
P0.6Selected shared WAE + Iceberg directionPaired observation and durable journal/capsule before hot/archive expansion; ingestion/query visibility and reconciliation
P0.7Graph/port hooks, Java-agent configuration, Keploy and platform/instance evidenceExtend 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.

#ItemNotes
A1Attachment, fact vocabulary, Fidelity rung enumcommonMain, no I/O. Settles the ToolingTarget (buildable) vs rgp Target (running) name collision — recommend Attachment locally, Target on the wire.
A2Probe interface over three shapes: bounded command, stream, socketDeviceTools becomes the first probe family; its argv grammar and SafetyClass are kept unchanged.
A3Adapter interfaceDeviceInspection.viewTree becomes the first registered adapter.
A4AttachmentRegistryMerges facts from N probes onto one identity; derives TargetCapability from which probes attached rather than from a declaration.
A5Port-forwarding probesadb forward tcp:<port> localabstract:reaktor-rgp, idb forwarding. This is the entire device-reachability story; write no custom USB transport.
A6L3 stream probeslogcat follow, log stream, wrangler tail, kubectl logs as Flow<Fact> over the existing streaming SupervisedProcessExecutor.
A7Profile probessimpleperf record, xctrace, async-profiler agent, JFR. Pairs with C2.
A8In-process agentSmall artifact the app links; sits on addPortEventListener and the lifecycle/backStack flows, opens the carrier socket. L4 and L5.
A9Correlation headerStamp outgoing requests in reaktor-io's http client; emit a local WireObservation. The join that makes proxy bytes attributable.
A10HTTP proxy probeLocal mitm, device proxy settings, CA install. Revert on detach with a receipt; refused at prepare on a stable channel.
A11Debugger probes under PerturbingJDWP first — one protocol covering Android and JVM — then lldb. Attach/detach, threads, frames, breakpoints, bounded variable reads under the Values grant.
A12Unify the write pathTaskPlanningPort.plan() ≡ EditDomain.prepare() → one Plan; add WriteTarget.Device; RunLedger becomes the single receipt store for device commands and graph patches alike.
A13On-device stores join Database modeThe run-as ls databases path already exists; join it rather than building a second browser.
A14Device and app pickers as permanent chromePromote 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.

#ItemNotes
B1Frame codec over FlexBuffersCommands, replies, events, cursors, credits. Everything is blocked on this. Specify Edit's payloads here even though it ships at B9.
B2WebSocket carrierreaktor-io's WebSocket / PartySocket. Behind a carrier interface so the Switchboard slots in later without a rewrite.
B3Flow control in the carrierPer-subscription buffer policy — conflate for state, drop-oldest for events — and droppedSinceCursor on every subscription. The producer never blocks on a subscriber.
B4Mesh domainDiscovery, attach/detach, capability negotiation, HELLO descriptors.
B5Graph read pathDescribe, handles, resolve; bounded by depth and a node cap, with truncation reported in Coverage.
B6Graph event pathCursors, producer-side selectors, credits — sourced from addPortEventListener and the lifecycle/backStack flows.
B7Revision domainContract 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.
B8Wire domainLive cross-runtime traffic; CreditState.stalledForMs is the point — the failure no log line shows.
B9Edit domainOver the existing GraphEditorSession / ChangeProtocol. Prepare, execute, cancel, conflict on preconditions.
B10Value domainLast, and gated on the redaction contract (D12), which does not exist yet.
B11Durable Object room for Worker targetsworkerd isolates are per-request; a switch in a stateless handler dies before anyone attaches.
B12MCP surface extensionElement, 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.

#ItemNotes
C1Promote 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.
C2ProfileProbe per platformBound to A7; output into ReaktorProfileCapture carrying its rung.
C3Perfetto protos for capturesReaktorFlamegraphFrame stays the normalised CI summary. Two artifacts: one gates the build, one you open when the gate fails.
C4Mobile frame vitalsJankStats, CADisplayLink → recordFrame(). (Phase zero.)
C5Memory vitals per platformAndroid Debug.MemoryInfo, iOS task_vm_info, JVM MemoryMXBean → recordMemory().
C6Budget catalog + CI gate(Phase zero.) Error blocks, Warning annotates, Info dashboards.
C7Size budgetsBundle / APK / IPA / JAR per build via the existing size-report.mjs, diffed against the previous release.
C8Benchmarking as a framework servicePromote the jvmBenchmark + kotlinx-benchmark/JMH pattern out of flexbuffer into a convention plugin any module can apply.
C9Cross-runtime benchmark reportOne benchmark, five runtimes, one comparison artifact. Nothing in the industry does this and the harness already exists.
C10Driver runners for the remaining enum membersRunner 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.
C11Recomposition and skip countsNeeds a verified counting adapter for this Compose version. Gate it; a composition snapshot does not establish it.
C12App-size treemap with build-over-build diffFlutter 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 plannedNow
Five port shapes as a kernel changeOptional sugar in a higher module, or an attached semantics tag. ConsumerPort<Anything> is instrumentable as it stands
TelemetryFacet as a new kernel conceptAn attachment
PortInvocationEvent as a new kernel typeWhat 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 ExitScope scopes 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.

#ItemNotes
D1Shared signal destinationsReuse existing OTel/Grafana infrastructure and ClickHouse consumers. Proposed Shadow hot/archive paths follow the paired-record contract; no migration or per-workspace stack.
D2SDK/export fidelity and distributed contextSDK and transport exist. Qualify events/links, bounded backoff, delivery health and W3C propagation across actual boundaries.
D3Destination decisionClickHouse as the store, Grafana as a reader — not a second store.
D4reaktor.* semantic conventiongraph.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.
D5TelemetryFacet + inheritance Graph → Module → Node → Port/EdgePolicy only, never data. payloadPolicy defaults to None. Rides K3.
D6TelemetryVisitor → TelemetryPlanDrops onto the existing PortGraphVisitor (Selector/Traverser/ExitScope). Cheapest item in the layer. The plan is itself a fact the workbench can render.
D7Shape-aware instrumentationRequestPort 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.
D8Three modes: Minimal / Standard / DetailedStandard is the default; Detailed is a temporary DevTools capture, never a deployed setting.
D9Transport instrumentation + span links for asyncQueues, 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.
D10DB query spansSqlAdapter.execute and Memgraph Cypher. Parameterised SQL only in db.statement, enforced by test.
D11TelemetryValidator — cardinality caught at authoring timenode.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.
D12Redaction from schema classification@Telemetry.PII / @Telemetry.Safe compiled into redaction rules by D6. Also gates rgp Value (B10).
D13Baggage allowlist, empty by defaultBaggage crosses service boundaries and can reach third parties; W3C forbids PII in tracestate. Strictest handling in the layer.
D14Two paths: development vs productionFull-fidelity ring buffer over the local carrier for Desktop; sampled, batched, redacted OTLP for production. One socket — the tooling layer's L4 carrier.
D15CaptureTelemetry / CaptureTrace as graph commandsBounded duration, automatic revert, receipt in the ledger via A12. Right-click a slow node: next invocation · next 10 · errors only · slower than 500ms.
D16No self-instrumentationThe 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.
D17Release correlation on every signalDeploy SHA, app build, remote-config version, experiment cohort.
D18Telemetry self-monitoringDropped spans, queue depth, ingest latency, exporter health. No data is an alertable state.
D19Crashlytics ↔ trace-id correlationCrashes are currently uncorrelated with any span.
D20Work metricsProposed 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.
D21M(g, Δt) overlay serviceThe 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.
D22Unblock the 17 signal subpanes9 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

#ItemNotes
E0.1InvestigationContextA five-field extension of the shipped WorkbenchSelection — add targets (plural), revisionVector, time, evidence, layers. Subject, related, version, resolution and history already exist.
E0.2TimeContext as the second universal dimensionBrushing 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.3Six surfaces + universal drawerInspect · Traffic · Timeline · State · Memory · Recorder; drawer carries Console · Logs · Source · Findings · Problems · Tasks over any surface.
E0.4Permanent header chromeApp, environment, target mesh, revisions, live/recording, capture, interact/inspect, overrides, agent. Devices stops being a subpane.
E0.5reaktor.pen + pen-truth.json migrationpanes.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.6Typed Finding + evidenceNo 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.7Live vs historical is visually unmistakablePinned evidence freezes revision, activation, capture time, sampling and coverage. Refreshing the live target never silently retargets a pinned trace.
E0.8Extension SDKDevToolsViewProvider, InspectorProvider, TimelineTrackProvider, TrafficAdapter, FindingProvider, OverlayProvider, ConsoleCommandProvider, RecorderExporter. Named in the master plan, in no source file.

E1–E6 — the surfaces

#SurfaceItemsDepends on
E1InspectIDE 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 findingsB5, B7, E0.6
E2Trafficevery 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, SourceA9, B8
E3Timelinesynchronised 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.2C3, D2, E0.6
E4Stateactors · 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 isolationK1, K2, B10
E5Memoryheap · allocations · ownership · snapshot diff. A graph dependency cycle is not a heap retain cycle and must never imply oneC5 — zero foundation today, sequence last or descope to snapshot-diff
E6Recordersemantic actions not coordinates · element id → intent → expected effects · assertions · replay · export to Maestro, Playwright, scenario, JSONE0.1, C10

E7 — cross-cutting

#ItemNotes
E7.1Graph breakpoints, two classesObservation 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.2Typed Console$0 over the graph subject; routes, trace, watch, graph. Not an eval.
E7.3Fault injectiondelay, fail, drop, rewrite in/out, bandwidth, disconnect after N frames — on any port, not only HTTP.
E7.4Environment overridestheme, locale, text size, motion, network, CPU, location, permissions, applied live with a conspicuous badge.
E7.5Everything mutating goes through one pathE7.2–E7.4 all route prepare → validate → authority → approve → execute → receipt. No debugger back door.
E7.6.reaktortrace evidence bundlemanifest, 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.7Agent handoff carries the investigationsubject, route, revisions, interval, spans, logs, source, findings, and coverage — what evidence is missing. Not a screenshot.
E7.8Explain this, everywherescope, 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.

CapabilityWho has itClosed by
Live UI tree, click-to-selectallshipped✅
Provenance: declared vs recorded vs remotenobodyshipped✅
Agent/MCP read surfaceChrome (thin)shipped, extended by B12✅
Attach to a program elsewhereallA1–A5, B1–B5
Source jumpallE1
Log streamall but React DTA6
Network capturemostA9, A10, B8, E2
Console / expression evalmostB10, E7.2
Performance timeline / flameall but React DTC1–C3, E3
Memory / heapmostC5, E5
Re-render attributionReact DT, AS, Xcode, FlutterC11
Breakpointsall but React DTA11, E7.1
Props/state editableReact DT, Unreal, FlutterB9, B10, E4
On-device stores editableFlipper, AS, UnrealA13, A12
Environment overrides liveXcodeE7.4
Portable trace artifactPlaywrightE7.6
SQL over the tracePerfettoC3, E3
Time travelUnreal, ReduxE4 — gated
Cross-system route UI → service → DBnobodyE1★
Field telemetry attributed to a graph nodenobodyD4, D17★
Port-shape-aware metrics (mailbox depth, subscriber lag)nobodyK1, D7★
Telemetry projected onto the architecture graphnobodyD21★
Time as a second universal selection dimensionUnreal (within one tool)E0.2★
One investigation context shared by every surfaceChrome (DOM only)E0.1★
Non-halting graph breakpoints safe in productionnobodyE7.1★
One benchmark, five runtimes, one reportnobodyC9★

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.

ItemAdoptNotes
D2, D11 SDK, OTLP, W3C, conventions, cardinalityopentelemetry-javaViews, 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 spansopentelemetry-java-instrumentationJDBC instrumentation covers the server side.
D3 ClickHouse clientclickhouse-javaOfficial, maintained.
C1, C2 JVM profilingasync-profiler Java API (one.profiler.AsyncProfiler)In-process, so the JVM flamechart script becomes a library call.
C2 JVM recordingJFR, jdk.jfr + jdk.jfr.consumerIn the JDK. Zero dependency.
C3 Perfetto emissionprotobuf-java + published protosNo writer library exists. The trace processor has an HTTP API for reading.
C7, C12 artifact size and diffdiffuseStable with low commit activity — vendor rather than depend. apkanalyzer is the heavier alternative.
A5, A6 Android device, logcat, forwardingddmlib (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 AndroidJDI (com.sun.jdi)In the JDK. Attach, breakpoints, frames, variables, for two of the targets, with no dependency.
A10 proxy and CABrowserUp Proxy or LittleProxy, + BouncyCastleSmaller projects with patchier maintenance than the rest of this list. Pin and vendor.
E5 heap, retained size, ownership, diffshark (Square, under LeakCanary) + HotSpotDiagnosticMXBean.dumpHeapKotlin, maintained, runs on JVM and Android. Eclipse MAT has a headless API if dominator analysis is wanted.
E1 source index read sideLSP4JAlready a dependency at 0.23.1.
B1–B3 protocol plumbingorg.eclipse.lsp4j.debugThe 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 controlnothingsimctl 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.

PhaseItemsOutcome
0 — week 1E1, C6, C4, D1The docs stop overstating the app. A developer sees real vitals and a real source jump.
1 — weeks 2–5A1–A4, B1–B3, K1, K2The model and the wire exist, and a port invocation becomes an event. Nothing user-visible; everything else depends on it.
2A5, 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.
3A8, A9, A14, B7, B8, B11, B12, C5, C7–C10, D7–D10, D14, E0.2, E0.4, E0.7, E2, E3, E7.7Attribution works end to end: a pixel to a port to a service to a row, with measured traffic behind it.
4A10–A13, B9, B10, D11–D13, D15–D17, E4, E6, E7.1–E7.6The tool mutates as well as reads: console, breakpoints, overrides, fault injection, portable traces.
5C11, C12, D18–D22, E0.3, E0.8, E7.8, E5 on remaining targetsThe 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 -n fails on flamechart/generate-all.sh at line 83, so it cannot run. profile-ios-sim.sh runs macOS sample; its xctrace line is printed as a suggestion. generate-native.sh targets an iOS simulator binary, so no generic-native target is separately qualified. generate-android.sh runs adb shell rm .../perf.data and then adb 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. GraphTelemetry calls startSpan and addEvent with no .end() anywhere, so no processor will ever export it, and HttpAnalyticsSink is app-owned code in bestbuds/modules/app/ carrying a product-event schema. See P0.1.

Contract corrections

WasNow
B7 used a HELLO-derived digest to identify a revisionA 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 rungRungs 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-safeNon-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 gateThe 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 dimensionsStability 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 containmentIt 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 parityCapture 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 filesA .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 qualifiedThey 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 ExitScope scopes traversal; K1 scopes the runtime call.
DocRelationship
DevToolsLayer E's design and its milestone definitions.
Tooling LayerLayer A's design; §11 covers profilers as probes.
Performance and ObservabilityLayers C and D in detail; §26 is the verified starting state.
Graph ProtocolLayer B's specification.
Analytics and TelemetryLayer D's design; §17 is the graph-native model and §17.13 the kernel prerequisites behind Layer K.
Graph refinement planLayer K's port shapes (§L1).
UI/UX auditWhere the missing observation plane was first recorded.