Content
# oddyssey
https://github.com/user-attachments/assets/a8dbc6e1-115e-4f33-83d1-b488bb1d9c51
**A CLI toolbox for Observability-Driven Development (ODD).**
[](https://github.com/using-system/oddyssey/actions/workflows/ci-mcp-server.yml)
[](https://pypi.org/project/oddyssey-mcp/)
[](LICENSE)
## Install
### With APM (every CLI)
With [APM](https://microsoft.github.io/apm/), to install the latest release, for Claude Code:
```bash
uvx --from 'apm-cli==0.31.0' apm install --global --target claude 'using-system/oddyssey#>=1.0.0'
```
Same command for every other supported CLI agent — swap the target:
`--target opencode`, `copilot`, `kiro`, `cursor`, `codex`, `gemini`,
`windsurf`. Drop `--global` to install into the current repository
only.
To update an existing install to the latest release:
```bash
uvx --from 'apm-cli==0.31.0' apm update --global --target claude using-system/oddyssey
```
It shows the update plan and asks for confirmation (`--yes` to skip,
`--dry-run` to only look); `uvx --from 'apm-cli==0.31.0' apm outdated`
tells you whether an update is worth running.
### From the native marketplaces (no APM)
**Claude Code**
```
/plugin marketplace add using-system/oddyssey
/plugin install oddyssey@oddyssey-plugin
```
**GitHub Copilot CLI**
```
copilot plugin marketplace add using-system/oddyssey
copilot plugin install oddyssey@oddyssey-plugin
```
**Kimi Code**
```
/plugin marketplace add using-system/oddyssey
/plugin install oddyssey@oddyssey-plugin
```
The native artifacts are generated from the APM package on every
release (`marketplace/`, built by `scripts/build-marketplace.sh`) and
carry the same agents, commands, skills, and pinned MCP server. The
other CLIs (Codex, opencode, Cursor, Windsurf, Kiro, Gemini) install
via APM above. opencode and Cursor install into the current
repository only — leave `--global` out.
### With GitHub Actions
In a workflow, a setup action installs the CLI and the package on the
runner, and the other actions run the loop's commands headless:
```yaml
- uses: using-system/oddyssey-actions/setup-copilot@v1
- uses: using-system/oddyssey-actions/odd-status@v1
with:
fail-on: error
```
There is a setup action per supported CLI and an action per oddyssey
capability; the catalog, with what each one takes and returns, lives
in [oddyssey-actions](https://github.com/using-system/oddyssey-actions).
## Which model/CLI to use?
A ranking of models and coding-agent CLIs on the loop's own work is kept
in [`.llms-benchmark/`](.llms-benchmark/README.md): each model observes
the same running stack through the same replayed traffic, driven through
one CLI (opencode, Claude Code or Copilot CLI), and its report is graded
on evidence — of what it claimed, how much held up. One model on one CLI
is one row, so the same model under two CLIs compares directly. The
table carries the findings, the cost and the duration of every run, so
you can pick on the axis that matters to you.
## The idea
ODD complements Spec-Driven Development: observe a running service — local
or remote — through its telemetry, turn what you see into the next SDD wave
(spec, plan, implement), then observe again. A continuous improvement loop,
indefinitely.
Everything is built on OpenTelemetry. For **local** observation, the MCP
server pilots a complete Grafana stack (UI, traces, metrics, logs,
profiles) that agents use to observe and fix. For **remote** stacks,
observation works against Grafana or any other OpenTelemetry backend
(Datadog, Dynatrace, Azure Monitor, CloudWatch, your own, ...).
oddyssey provides:
- **an OpenTelemetry expert** ([`otel-instrumentation-expert`](.apm/agents/otel-instrumentation-expert.agent.md))
that investigates your stack and hands your CLI agent everything needed
to integrate OpenTelemetry and deploy collectors;
- **a run investigation agent** ([`observe-run`](.apm/agents/observe-run.agent.md)),
local or remote, that delivers a complete observation report your CLI
agent turns into a spec-driven plan of fixes and improvements;
- **a complete local observability stack** based on Grafana, piloted by
the oddyssey MCP server ([what it runs and on which
ports](docs/guide/plugin.md#the-mcp-server));
- **an ODD memory carried by the repo itself** — every observation and
instrumentation report lands in `.odd/`, committed and versioned with
the code, shared with the whole team, and recalled as the baseline of
the next run: the loop accumulates knowledge instead of starting
blind.
Everything is packaged for any coding agent
([APM](https://microsoft.github.io/apm/): Claude Code, Copilot, Cursor,
Codex, Gemini, and friends).
## How to
The loop in three prompts — every example below links to a real artifact
from this repository: oddyssey instrumented, observed, and verified its
own MCP server.
**Step 1 — Instrument OpenTelemetry.**
```text
/odd-instrument-otel add OpenTelemetry to my project XXX
```
The `otel-instrumentation-expert` agent investigates the codebase,
stores its report in `.odd/otel-instrumentation-reports/` (committed —
the next investigation starts from it), and hands back everything a
spec-driven wave needs. Real output of that wave on this repo: the
[design spec](docs/superpowers/specs/2026-08-22-mcp-otel-instrumentation-design.md)
and the
[implementation plan](docs/superpowers/plans/2026-08-22-mcp-otel-instrumentation.md)
generated with [superpowers](https://github.com/obra/superpowers) from
the agent's investigation report.
**Step 2 — Observe a local run.**
```text
/odd-observe check that my project XXX starts and answers requests on the /user endpoint
```
The `observe-run` agent drives the run, queries the telemetry, and
stores its report in `.odd/observe-run-reports/` — findings, evidence,
and the replay protocol the verification will consume. Real example: the
[first observation report](.odd/observe-run-reports/2026-08-22-2154-mcp-otel-instrumentation-verification.md)
(4 confirmed findings) and the
[fix-wave plan](docs/superpowers/plans/2026-08-23-mcp-otel-fix-wave.md)
the next SDD wave built from it.
**Step 3 — Verify the fixes the SDD wave delivered.**
```text
/odd-verify check that report XXX from .odd has been fixed
```
The same agent replays the stored report's protocol and rules on every
recorded item — before-value, after-value, pass criterion. Real example:
the
[verification report](.odd/observe-run-reports/2026-08-22-2227-verify-mcp-otel-instrumentation-verification.md)
— 9/9 checks pass, all 4 findings fixed, measured not assumed.
**Step 4 — Deploy and observe remotely.**
Let the deployment run for a while first — a remote observation needs
real traffic history to read, not a freshly booted service. Then point
the missions at the remote stack: **its CLI must be installed and
configured beforehand** (gcx for a Grafana stack), and `/odd-config`
is the guided way to switch and prove the connection before any
mission runs. A backend the package does not ship is one
`/odd-instrument-stack create a stack <name>` away: the stack it
writes — a guide and its query scripts, verified live — lives in your
repository, and your runs' reports say where it rubs.
```text
/odd-config switch to grafana
/odd-observe what did my service XXX do over the last 24 hours?
```
Or in a single prompt — naming the stack in the mission switches the
configuration too:
```text
/odd-observe what did my service XXX do over the last 24 hours on my stack grafana?
```
The backends oddyssey manages, their associated CLI, and their switch
prompt are documented in
[docs/guide/backends.md](docs/guide/backends.md).
And the loop starts again: an SDD wave from the remote observation, a
local observe, a verify — and on it goes. Every step left a committed
report in `.odd/`; their formats — frontmatter fields, allowed values,
body structure — are documented in
[docs/guide/reports.md](docs/guide/reports.md).
### Miscellaneous prompts
#### /odd-status
```text
/odd-status
/odd-status full picture, every finding and every trend
/odd-status where is the loop for my service XXX
/odd-status what was observed on prod for my service XXX
/odd-status where is the loop for XXX, cloned at ../XXX
/odd-status wontfix F4 of my last XXX report - port-move is rare, 14.5s accepted
/odd-status .apm is non-runtime - it is the package's prompts, never on the request path
```
Answers "where is the loop?" for this repository — one screen, per
service and stack: the last report, how many findings are open, fixed,
or declined, and what to do next — from the committed `.odd/` history and
git alone: no backend query, nothing written. Ask for the full picture,
or scope it to a service, a stack, or an environment, to get every
finding, trend, and gap. It also records the rulings only you can
make, each with a one-sentence reason: a finding no fix will ever
address lands in `.odd/decisions.md` and stops counting as open, a
top-level directory or file that cannot change the service's runtime lands in
`.odd/entry-classifications.md` and stops being asked about — two
committed ledgers next to the reports.
#### /odd-instrument-bench
```text
/odd-instrument-bench author a load benchmark for my service XXX, p95 under 300ms
/odd-instrument-bench stress test XXX against http://localhost:8080, error rate must stay under 1%
/odd-instrument-bench update the XXX-read-heavy benchmark - the cart endpoints moved
```
Writes a k6 load-test benchmark for a service — a script plus a
manifest — under `.odd/benchmarks/<name>/`, as code you review and
commit like any other. It asks you only what a human must decide (test
type, thresholds, target, new benchmark or an update) and proposes the
load shape; a threshold the service can structurally never meet comes
back to you with the evidence instead of being persisted. It validates
the script without ever running the benchmark — running it is
`/odd-observe`'s job: `/odd-observe run .odd/benchmarks/<name>/`. The
lifecycle is in [docs/guide/benchmarks.md](docs/guide/benchmarks.md).
#### /odd-instrument-stack
```text
/odd-instrument-stack create a stack seq from https://datalust.co/docs/command-line-client
/odd-instrument-stack for stack seq: the traces endpoint is /api/traces
/odd-instrument-stack from report .odd/observe-run-reports/<the report>.md
```
Writes a custom observability stack for a backend the package does
not ship — a guide with the same sections as a built-in reference,
and the query scripts it names — under `.odd/observability-stacks/<name>/`,
verified live against the backend, as code you review like any other.
An observe run against it never edits it: its report records the
friction, and `from report` turns that into the fix. How, in
[docs/guide/custom-backends.md](docs/guide/custom-backends.md).
More invocation examples for every prompt live in
[docs/guide/prompts.md](docs/guide/prompts.md).
## Every backend
The missions query the backend your telemetry lands in, through its
own CLI: the local stack (gcx), a remote Grafana (gcx), Azure Monitor
(az), AWS CloudWatch and X-Ray (aws), Datadog (Pup) and Dynatrace
(dtctl). Per backend — the CLI and how to install it, how
to connect, what must already exist before there is anything to
query, the switch prompt and what gets persisted — see
[docs/guide/backends.md](docs/guide/backends.md).
You can add your own observability backend. A backend the package
does not ship becomes a directory in your repository — a guide with
the same sections as a built-in one, and the query scripts it names —
that `/odd-instrument-stack` writes from the documentation you point
at, your instructions, or its own research, verified live against
the backend, and fixes from what your runs' reports record as
friction — or a pointer to a stack another repository already
carries. How, in
[docs/guide/custom-backends.md](docs/guide/custom-backends.md).
## Multi-repo strategies
When the system you observe spans several repositories — a
microservice architecture, a product shipped as several deliverables —
and you want the reports to accumulate in one place, drive oddyssey
from a dedicated observability repository rather than from each
service's own. Its `.odd/` becomes the loop's memory for the whole
system — the memory lives with the system rather than with one
service's code — and a single `/odd-status` renders the system rather
than one service's slice.
Give that repository an `AGENTS.md` that lists the repositories and
their role — where each one lives, what it is, the service names its
telemetry carries. That list is the context your coding agent starts
every mission with, so it knows what the system is made of and where
to fetch the code a mission needs to read.
```text
## Repositories
- `github.com/example-org/checkout-api` — the checkout API (`checkout`)
- `github.com/example-org/payment-worker` — the queue consumer (`payment`)
- `github.com/example-org/shared-telemetry` — the shared OTel helpers
```
## The ODD principles
- **The system must be observable locally.** Prefer a docker-compose
that starts your whole stack, and mocks for the remote systems it
queries — the oddyssey MCP server provides the local observability
backend the telemetry lands in.
- **Instrument with the expert.** Bring OpenTelemetry into your
services through the `otel-instrumentation-expert` agent rather than
by hand.
- **One design loop, always the same.** Every feature follows: SDD to
develop it → observe a local run → fix and improve → repeat those
last two steps until satisfied. Then deploy to the target
environments. After some time, run a remote observation on the
deployed environment's stack to seed the next SDD wave — and the
loop starts again from the local run.
- **Maturity spaces observation out.** The time between remote
observations grows as the service matures: a young service gets
observed often, a stable one only when something is worth learning.
- **Evidence over impressions.** Every claim about a service comes from
a query and its result — numbers, trace IDs, log lines — never "it
seems faster".
- **Cross-confirm before concluding.** Never conclude from one signal
what two could confirm (traces, metrics, logs, profiles); a
single-signal anomaly is always labeled as such.
- **The memory lives with the code.** Observation reports are stored in
the observed repo under `.odd/` — version that directory (do not add
it to `.gitignore`): the reports get reviewed in PRs, shared by the
whole team, and the git history reads observed → fixed → verified.
- **Verify by replaying, not by re-measuring differently.** A fix is
proven by replaying the recorded scenario identically; one changed
variable invalidates the before/after comparison.
- **What's missing is a finding too.** Telemetry gaps — absent spans,
logs without trace IDs, missing histograms — are deliverables of the
observation and feed the next instrumentation wave.
- **One telemetry, two consumers.** The metrics, traces, and logs do not
serve ODD alone: the same data feeds classic runtime observability —
dashboards, alerting, incident investigation. Instrument once, and the
development loop and the operation of the system read from the same
source of truth.
- **Agents observe, they never fix.** The investigation agents only
observe and report — they never modify the code directly. Their report
is a universal input: feed it to any spec-driven framework for the
spec-and-implement wave, turn it into JIRA tickets, or hand it to a
human — what happens next stays your call.
## Prerequisites
- **[Docker](https://docs.docker.com/get-docker/)** — runs the local
observability stack (the MCP server drives it directly).
- **[gcx](https://github.com/grafana/gcx)** — required only when observing
a **Grafana** backend (the local stack, self-hosted, or Grafana Cloud):
`brew install gcx`, or
`curl -fsSL https://raw.githubusercontent.com/grafana/gcx/main/scripts/install.sh | sh`.
- **Other backends** need their own CLI, each covered by the
[`observability-cli-guides`](.apm/skills/observability-cli-guides/SKILL.md)
skill: Datadog ([Pup](https://github.com/DataDog/pup)), Dynatrace
([dtctl](https://github.com/dynatrace-oss/dtctl)), Azure Monitor
(`az`), AWS CloudWatch/X-Ray (`aws`).
- **`python3`** — the package's skills run their scripts with it.
- **[k6](https://grafana.com/docs/k6/latest/set-up/install-k6/)** —
needed to author a benchmark and to run one: `/odd-instrument-bench`
validates the script it writes with `k6 inspect` and one smoke
iteration (never a benchmark run), and `/odd-observe` and
`/odd-verify` run it. The three prompts' preflights install it on
the spot when it is missing and Homebrew is available
(`brew install k6`, no confirmation — k6 needs no account and no
configuration); without Homebrew they follow the platform's
non-interactive path when one exists, otherwise hand you the
official steps. To install it yourself: `brew install k6` on macOS,
the official APT/YUM repositories or a release binary on Linux, the
MSI installer or a package manager on Windows — a `k6` on your path,
which the `grafana/k6` Docker image does not provide.
## The oddyssey plugin package
The loop: **investigate** (agents) → **spec & implement** (the main
agent's spec-driven workflow) → **observe again** — telemetry on both
ends. Each observation report is stored in the observed repo
(`.odd/observe-run-reports/`), versioned by git and shared with the whole
team, and becomes the baseline the next run diffs against — the loop
accumulates knowledge instead of starting blind.
Every prompt, agent, skill and hook of the package — its role, and who
invokes what across them and the MCP tools — is listed in
[docs/guide/plugin.md](docs/guide/plugin.md).
## Development
The exact build, test, and lint commands live in
[CONTRIBUTING.md](CONTRIBUTING.md) — single source, matching what CI
enforces. In short: the project under `src/` is a self-contained uv
project (own `pyproject.toml`); `tests/` mirrors `src/`.
## Contributing
Issues, docs fixes, and code are welcome — see
[CONTRIBUTING.md](CONTRIBUTING.md) for the layout, the exact build/test
commands, and the PR conventions (squash titles drive the released
version). Questions and ideas belong in
[Discussions](https://github.com/using-system/oddyssey/discussions);
[good first issues](https://github.com/using-system/oddyssey/issues?q=is%3Aopen+label%3A%22good+first+issue%22)
are waiting.
## License
[MIT](LICENSE)
Connection Info
You Might Also Like
No recommendations available