Goal
Define a standard, language-neutral environment contract so a CLI invocation can be correlated with
the process that launched it — including when either side is not a base-cli application.
Background
The data model for parent/child correlation already exists in the Context and in run.json:
RuntimeBinding.inherited_path, RuntimeBinding.history_parent_run_id,
Context.history_scope (primary / internal), and Context.history_parent_run_id. A child
invocation that inherits a run root is deliberately excluded from owning run metadata
(owns_run_metadata = inherited_path is None and ..., lib/python/base_cli/_app_core.py:1175).
What is missing is the transport. Those values reach the framework only from a
consumer-supplied RuntimeResolver. base-cli defines no environment variable for them, so every
consumer must invent its own convention, and two base-cli CLIs from different teams cannot correlate
at all. The framework's own environment surface confirms the gap:
$ grep -rhoE "BASE_CLI_[A-Z_]+" lib/python/base_cli/ docs/*.md | sort -u
BASE_CLI_BENCHMARK_PLATFORM
BASE_CLI_CACHE_DIR
BASE_CLI_COLOR
BASE_CLI_CONFIG_DIR
BASE_CLI_DISPLAY_COMMAND
BASE_CLI_VALIDATION_RESULT
Nothing for run identity. For the ops/platform/SRE audience this is a core capability, not a nicety:
CLIs calling CLIs, wrapped by shell scripts, driven by CI, is the normal topology, and "which run
produced this artifact" is the first question during an incident.
This is also the cheapest possible increment toward the language-neutral platform in #274-#277: an
environment-variable contract needs no SDK in any language. A Bash wrapper, a Go binary, or a
Makefile can participate by reading and forwarding variables.
Scope
- Define and document the variables: run ID, parent run ID, run root, and history scope — for
example BASE_CLI_RUN_ID, BASE_CLI_PARENT_RUN_ID, BASE_CLI_RUN_ROOT, BASE_CLI_RUN_SCOPE.
- Define W3C trace-context interop: read an inbound
traceparent/tracestate and export them for
children, so the OpenTelemetry integration produces connected traces across process boundaries
instead of isolated per-process spans.
- Make the generic runtime resolver honour the inbound variables by default, producing an inherited
binding rather than a new owning run.
- Export the variables into the child environment for subprocesses the command launches, with a
documented opt-out.
- Specify precedence against an explicit
RuntimeResolver, and validation rules (a malformed or
hostile inbound run ID must not become a path component — runtime_run_directory_name() and the
cleanup guards depend on run IDs being single safe components).
- Define the trust boundary: inbound values come from the environment and must be validated, never
trusted as paths.
Acceptance criteria
- Two base-cli CLIs, one invoking the other with no custom profile, produce correlated records: the
child's run.json carries the parent run ID and scope: internal, and only the parent owns the bundle.
- A shell script or non-Python process can set the documented variables and achieve the same
correlation with no Python involvement.
- An inbound run ID that is not a single safe path component is rejected with an actionable error and
cannot influence any filesystem path.
- With telemetry enabled, a child span is a child of the parent span via inbound
traceparent.
- The variables, precedence, validation, opt-out, and trust boundary are documented in
docs/integrations.md and docs/security-threat-model.md.
Validation
End-to-end test with a base-cli parent invoking a base-cli child, and a second test with a shell
wrapper setting the variables directly. Negative tests for ../, absolute paths, and NUL in the
inbound run ID.
Non-goals
- Do not add a daemon or shared state beyond the environment and the existing run bundle.
- Do not make OpenTelemetry a required dependency.
- Do not change the existing
RuntimeResolver protocol shape.
Goal
Define a standard, language-neutral environment contract so a CLI invocation can be correlated with
the process that launched it — including when either side is not a base-cli application.
Background
The data model for parent/child correlation already exists in the
Contextand inrun.json:RuntimeBinding.inherited_path,RuntimeBinding.history_parent_run_id,Context.history_scope(primary/internal), andContext.history_parent_run_id. A childinvocation that inherits a run root is deliberately excluded from owning run metadata
(
owns_run_metadata = inherited_path is None and ...,lib/python/base_cli/_app_core.py:1175).What is missing is the transport. Those values reach the framework only from a
consumer-supplied
RuntimeResolver. base-cli defines no environment variable for them, so everyconsumer must invent its own convention, and two base-cli CLIs from different teams cannot correlate
at all. The framework's own environment surface confirms the gap:
Nothing for run identity. For the ops/platform/SRE audience this is a core capability, not a nicety:
CLIs calling CLIs, wrapped by shell scripts, driven by CI, is the normal topology, and "which run
produced this artifact" is the first question during an incident.
This is also the cheapest possible increment toward the language-neutral platform in #274-#277: an
environment-variable contract needs no SDK in any language. A Bash wrapper, a Go binary, or a
Makefile can participate by reading and forwarding variables.
Scope
example
BASE_CLI_RUN_ID,BASE_CLI_PARENT_RUN_ID,BASE_CLI_RUN_ROOT,BASE_CLI_RUN_SCOPE.traceparent/tracestateand export them forchildren, so the OpenTelemetry integration produces connected traces across process boundaries
instead of isolated per-process spans.
binding rather than a new owning run.
documented opt-out.
RuntimeResolver, and validation rules (a malformed orhostile inbound run ID must not become a path component —
runtime_run_directory_name()and thecleanup guards depend on run IDs being single safe components).
trusted as paths.
Acceptance criteria
child's
run.jsoncarries the parent run ID andscope: internal, and only the parent owns the bundle.correlation with no Python involvement.
cannot influence any filesystem path.
traceparent.docs/integrations.mdanddocs/security-threat-model.md.Validation
End-to-end test with a base-cli parent invoking a base-cli child, and a second test with a shell
wrapper setting the variables directly. Negative tests for
../, absolute paths, and NUL in theinbound run ID.
Non-goals
RuntimeResolverprotocol shape.