Skip to content

Plan and apply timings, tracing and reuse reasons

reflow plan inspects the selected flows and their current inputs without running them or sending target-readiness probes. Timing collection is automatic. Use the standard debug mode when investigating slow planning or preparing feedback:

Terminal window
reflow plan smoke checkout --log-level debug
reflow plan smoke checkout --json --log-level debug

Debug diagnostics go to stderr. Each timing event is one JSON line with level: "debug", event: "plan_timings" and a timings report. Normal terminal output and JSON stdout keep their usual plan data; diagnostics do not change reuse decisions or the reviewed plan fingerprint. There is no timing-specific flag. REFLOW_LOG_LEVEL=debug enables the same debug mode; an explicit CLI --log-level takes precedence.

For local MCP, set REFLOW_LOG_LEVEL=debug in the Reflow process environment and read its stderr through the agent host. A host may need to reconnect MCP after changing that environment. Local MCP plan diagnostics measure the daemon handler, excluding the rest of the agent host’s tool-call latency. CLI reports also measure process startup, discovery and the local round trip.

Durations use milliseconds. The breakdown includes CLI setup, daemon discovery, the daemon round trip and queue, source inspection, flow inventory and source fetching, plugin integrity checks, configuration and baseline reads, shared execution history, and remaining planning work. Shared-history timing includes the full client-observed request and response processing; it does not separate the server’s database and evidence-storage operations.

Parent durations include their children. Do not add every displayed row together: concurrent work overlaps, and the other/exclusive values account for those overlaps. The CLI total ends when the timing report is prepared; shell launcher time and the final output flush are outside that measurement. An external wall clock includes those additional costs; report it alongside the CLI total.

The diagnostic phase names are:

PhaseWhat it measures
cli_totalCLI process start to report preparation
cli_setupStartup and local setup before daemon discovery
daemon_discoveryFinding or starting the selected daemon
daemon_retireStopping an idle daemon from another release (once after an upgrade)
daemon_startStarting the daemon and waiting until it answers
daemon_round_tripThe complete local request and response
daemon_requestThe daemon handler’s part of that round trip
daemon_queueWaiting behind earlier daemon operations
daemon_initializationPreparing the controller when needed
planningPreparing the plan and reading current ownership
local_configurationReading the local target, bindings and inputs
identity, test_data, configurationShared identity, test-data state and configuration reads
source_scanInspecting repository source files
storage_context, inventoryReading the application and flow inventory
source_resolution, source_fetchResolving selected flow sources and fetching their contents
plugin_integrityChecking the selected application lock and installed providers
baselinesReading visual baselines
shared_historyFetching shared execution outcomes and evidence availability
history_projectionTurning history into the latest step outcomes
ownerReading the group’s current owner

Only phases reached by that request appear. A phase’s count records how many measured calls it combines. other/exclusive is work inside that phase not accounted for by its measured children.

After an upgrade, the first command that reaches the device service retires the idle service from the previous release and starts the selected one; when a command starts the service, reflow writes a short Reflow: ... progress note to stderr with the elapsed time (--log-level error hides it). That request also rebuilds the new service’s in-memory caches, so its plugin_integrity and source_fetch take longer than later warm plans. Compare warm runs, not the first plan after a start.

With an older daemon, the CLI still measures discovery and the round trip. In the debug report, an absent daemon_request child under daemon_round_trip means daemon detail was not available; it does not mean daemon work took zero time. Upgrade to obtain it.

Handled planning errors emit available partial timings on the debug channel. Generic planning failures retain the existing stderr error and nonzero exit; --json does not add a stdout error object. Argument errors, import failures or abrupt process termination can prevent a timing report entirely.

For a useful comparison, keep the flow selection, target and application lock unchanged and collect several warm runs. Report the CLI version, timing output, external wall time, and counts to run and reuse. Include the relevant sanitized plan_timings event in feedback, not a full unreviewed log. Never repeat an uncertain apply or another mutation solely to collect diagnostics. A faster source scan alone does not establish that the whole plan is faster.

reflow apply --json adds a timings object to the reflow.apply.result v1 result. It is additive diagnostics: existing fields are unchanged. Values are client-observed milliseconds; a phase that was not reached is null.

FieldWhat it measures
total_msCLI process start to report preparation
phases.startup_msProcess start to the plan request (loading, setup)
phases.plan_msThe plan request through the local device service
phases.admission_msSubmission until the admission response
phases.reused_reads_msReading retained steps of reused flows
phases.execution_msWaiting for submitted runs to become terminal
phases.cleanup_msWaiting for owned process cleanup confirmation
requestsRun reads, device status reads, time slept between polls, and the count and time of settlement waits (the device service answers as soon as a run’s worker exits or its cleanup ends; the shared run and owner reads still decide the outcome)

Each runs[].steps[] entry also carries started_at, finished_at and duration_ms when the run recorded them. Steps of a reused flow keep the interval of the run that produced their evidence and are marked reused: true. A step reused inside a new run reports the time taken to adopt the retained result.

Tracing is off by default and costs nothing measurable when off. Enable it per command with any of these environment variables:

VariableEffect
REFLOW_TRACE_FILE=/path/trace.jsonlAppend one span per line (OTLP/JSON span shape)
OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINTPost spans to an OTLP/HTTP collector (JSON) when the command ends
OTEL_EXPORTER_OTLP_HEADERSCollector headers, for example authorization
REFLOW_DEBUG=perfPrint a span tree and the largest self-time contributors to stderr; plan --json then also includes its timings report

OTEL_SDK_DISABLED=true turns all of these off; OTEL_TRACES_EXPORTER=none disables only the collector export.

One trace covers the CLI, the local device service and the execution workers it starts: the CLI sends a W3C traceparent header to the device service, which records that request’s spans under the profile’s traces/ directory and passes the trace position to its workers. The CLI collects those spans when the command ends. Spans name phases, shared reads, flows, steps, provider launches and calls, browser launch and every Reflow API request (method, route template with identifiers replaced by {id}, status and size). They never record tokens, request or response bodies, query values or error text.

Any OpenTelemetry backend can display the exported spans. A trace file is plain JSON lines; when sharing one in feedback, review it first like any other log.

Reuse requires matching target state, execution inputs, dependencies and retained evidence. After reflow init --upgrade changes a used provider’s pins, affected steps can need another run even when the repository is unchanged. Their previous evidence describes the earlier inputs; reuse can resume once the affected steps satisfy the current reuse requirements.

When the latest matching run retains a plugin lock for the same application, target and unchanged flow source, the plan names changed pins of providers used by that flow, with abbreviated old and new identities. This identifies a known change, not necessarily the only change. Missing historical metadata, edited flow source or changes to unused providers keep the generic explanation.

A plan can also require work because the flow, covered source, bindings, supplied inputs, configuration or visual baselines changed. An input-change explanation should not be read as permission to retry a step whose completion is unknown; inspect that outcome before explicitly tainting it.