Skill 07 · Agent Observability Replay Trace
Subchapter 7.2
references/local-setup.mdMarkdown6 KBView on GitHub
Read this only when the app can’t be invoked locally with a JSON input — a deployed-only service, an
HTTP/gRPC handler entrypoint, no local __main__/CLI, deps not installed, or logic coupled to live infra.
Replay re-runs the entrypoint locally, so without a local run path there's nothing to replay. This procedure
makes the app locally runnable, then hands back to the normal instrument + runner steps.
Shape: detect → propose (one approval) → build automatically. You do as much as possible on your own, but two things are mandatory handbacks to the user; see below.
The real question isn’t the binary “is the app runnable?” — it’s what is the innermost callable seam corresponding to the trace’s root span, and can it be called directly with a JSON input? A deployed-only HTTP/gRPC service frequently still exposes a plain callable underneath its handler (the ports-and-adapters / hexagonal case); when it does, extract/call that seam — full local-setup would be overkill. Only run this procedure when no seam is directly callable.
Signals the seam isn’t directly callable as-is:
if __name__ == "__main__" / CLI, or no way to import + call the core logic directly.If the seam is already an importable function you can call with JSON, skip this whole procedure and return to step 4. If the logic is a plain callable buried inside a handler, extracting that seam is a small edit (see §2) — still lighter than treating the whole app as non-runnable.
First, look for an existing dry-run affordance before proposing any stubs. Many apps already ship a
dry-run / no-op / sandbox mode or a nil-adapter branch built for exactly this situation. Using the app’s own
affordance is safer and higher fidelity than a mock — it exercises the real code path right up to the
side effect, and it’s code the owners already trust. Check for it (flags, env vars, a --dry-run, a
nil/fake adapter) and prefer it. Only fall back to a stub — a fake/no-op stand-in for a dependency
that either isn’t reachable locally or whose real call would re-trigger a side-effecting write — where no
such affordance exists; the per-dependency stub-vs-real decision is §4’s second handback.
Read the code around the entrypoint and produce a concrete, reviewable plan covering:
.env), and tracing enabled under <ml_app>-local
(LLMObs.enable(ml_app="<ml_app>-local")) — local test traces must not pollute the production ml_app.Present this as a single plan and get the user’s approval (accept / adjust / “just scaffold, I’ll do X”). This is their one control point — don’t build before it.
Do everything you can without further prompting:
.env skeleton, enable LLMObs under <ml_app>-local.Then hand off to step 4 (instrument + runner) as normal — the runner’s entrypoint becomes this local entry point.
These you cannot (and must not silently) do for the user:
.env with the
required keys empty and ask the user to fill them; never invent or guess values.Everything outside these two is automatic.