Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ user opts in with `/plugin enable`; an existing install is never flipped by cata

### Autonomy

- [`autonomy`](plugins/autonomy) — Governed autonomous agent operation: role-topology, binding-seam, and wiring-vs-advisor contracts for climbing the AI-adoption ladder, plus a guided-setup skill that discovers an adopting org's state and writes its schema-versioned binding.
- [`autonomy`](plugins/autonomy) — Governed autonomous agent operation: role-topology, binding-seam, wiring-vs-advisor, and telemetry contracts for climbing the AI-adoption ladder, plus a guided-setup skill that discovers an adopting org's state, writes its schema-versioned binding, and wires standards-pinned OTLP emission with a zero-cost file-artifact default.

### Security

Expand Down
8 changes: 4 additions & 4 deletions docs/topics/ai-ladder-wp2-telemetry/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Prerequisite: WP1 implementation merged (`plugins/autonomy/` exists with `refere
cut after that merge; WP3's PR follows this package's PR (its convention doc cites this
contract — dead-link avoidance, same ordering rule as WP1's standards PR).

### Phase 1: Telemetry contract doc [TODO]
### Phase 1: Telemetry contract doc [DONE]

| File | Action | What changes |
|---|---|---|
Expand All @@ -114,7 +114,7 @@ contract — dead-link avoidance, same ordering rule as WP1's standards PR).
- `grep -c 'no trailing slash' plugins/autonomy/reference/telemetry.md` ≥ 1 (URL normalization rule present)
- `node scripts/validate-plugin-contracts.mjs` exit 0; lychee lane passes

### Phase 2: Guided-setup telemetry slice [TODO]
### Phase 2: Guided-setup telemetry slice [DONE]

Extends the WP1 `setup` skill (discovery-first per D6/WP1 D7); all wiring lands as reviewable
changes; paid sinks advisory + explicit opt-in with cost surfaced. First work item — fresh-docs
Expand All @@ -138,7 +138,7 @@ editing SKILL.md or citing vendor env vars.
- `grep -c 'OTEL_RESOURCE_ATTRIBUTES' plugins/autonomy/skills/setup/SKILL.md` ≥ 1
- Fleet-name sweep (`validate-plugin-contracts.mjs`) exit 0

### Phase 3: Conforming-path demonstration [TODO]
### Phase 3: Conforming-path demonstration [DONE]

Acceptance-criterion probe: one path demonstrates a single trace tree trigger → CI → agent
session on the free default, zero paid dependencies. Scratch consumer repo (NOT this repo),
Expand All @@ -164,7 +164,7 @@ plugins; no standing capability is created (near-duplicate audit rationale).
- The committed fixture exists and the same DuckDB assertions pass against it
- Demo transcript + query output attached to the PR body; no paid service touched

### Phase 4: Gates [TODO]
### Phase 4: Gates [DONE]

Full in-repo gate run (same roster as WP1 Phase 5): `scripts/validate-plugins.sh`,
`scripts/run-plugin-tests.sh`, `node scripts/validate-plugin-contracts.mjs`,
Expand Down
4 changes: 2 additions & 2 deletions plugins/autonomy/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "autonomy",
"version": "0.1.0",
"description": "Governed autonomous agent operation: role-topology, binding-seam, and wiring-vs-advisor contracts for climbing the AI-adoption ladder, plus a guided-setup skill that discovers an adopting org's state and writes its schema-versioned binding.",
"version": "0.2.0",
"description": "Governed autonomous agent operation: role-topology, binding-seam, wiring-vs-advisor, and telemetry contracts for climbing the AI-adoption ladder, plus a guided-setup skill that discovers an adopting org's state, writes its schema-versioned binding, and wires standards-pinned OTLP emission with a zero-cost file-artifact default.",
"author": {
"name": "Melodic Software",
"email": "info@melodicsoftware.com"
Expand Down
7 changes: 5 additions & 2 deletions plugins/autonomy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ AI-adoption-ladder contract set: it ships the tool-agnostic contracts an adoptin
its own repositories, tools, and policies, plus a guided-setup skill that discovers the org's
state and records that binding.

## Shipped capability (0.1.0)
## Shipped capability (0.2.0)

- **Topology contracts** (`reference/`): role topology for the repositories an adoption spans,
the binding-seam shape that maps contract roles to an org's real instances, and the
wiring-vs-advisor principle governing how setup lands changes.
- **Telemetry contract** (`reference/telemetry.md`): standards-pinned OTLP from every execution
context, the `autonomy.work_item.url` join attribute, one causal tree by context propagation,
sink classes with a zero-cost file-artifact default — plus the setup telemetry slice, its
snippet templates, and the emission-conformance check.
- **Guided setup** (`/autonomy:setup`): discovery-first interview of the adopting org's state —
role homes, substrate availability, budget posture — writing a schema-versioned binding under
`.claude/autonomy/` as reviewable changes. Never assumes any particular org or repo shape.
Expand All @@ -21,7 +25,6 @@ locked (no step-skipping — trust before scale).

| Capability | Trigger |
|---|---|
| Telemetry contract + setup slice | Telemetry work package build lands. |
| Return-accounting convention + capture slice | Return-accounting work package build lands. |
| Trigger/dispatch adapters | Trigger-layer work package build lands. |
| Guardrail matrix + sandbox-ladder binding | Guardrails work package build lands. |
Expand Down
97 changes: 97 additions & 0 deletions plugins/autonomy/reference/telemetry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Telemetry

Normative contract for autonomy telemetry: every execution context an adoption runs — the
interactive session, the CI pipeline, the autonomous runner — emits standard OpenTelemetry
(OTLP), pinned to the upstream semantic conventions, carrying one contract-owned join
attribute, joined into one causal tree by standard context propagation. The sink is
deliberately out of contract.

## Pillar 1 — standard OTLP, pinned semantic conventions

Every execution context emits OTLP pinned to the OpenTelemetry CI/CD and VCS semantic
conventions, release **v1.43.0**. Every CONTRACT-AUTHORED emission (the writers and adapters
an adoption wires) declares `schema_url: https://opentelemetry.io/schemas/1.43.0`; a native
tool's own emission is consumed as-is — its schema declaration is whatever the tool emits
(empirically, native agent-session output declares none), and the native-surface principle
forbids rewriting it. A declared schema URL anywhere in a conforming output set MUST match
the pin. Attribute vocabulary is cited by that registry reference, never copied into this
contract or any conforming document — the registry owns the names (illustrative citation
only: `cicd.pipeline.run.id` and its sibling pipeline/task attributes, the `vcs.*` change
and revision attributes).

Those conventions are Release Candidate: upstream renames still happen. The pin is exact;
adopting a newer release (including the graduation-to-Stable rename wave) is a reviewed
contract migration owned by the contract home — never a silent upgrade. The migration
trigger is recorded in the capability home's trigger register. Never invent a parallel
schema for a concept the upstream conventions already name.

## Pillar 2 — the work-item join attribute

One custom attribute joins machine telemetry to the work item that caused it:

- **Name:** `autonomy.work_item.url`
- **Value:** the work item's canonical web URL in normalized form — https scheme,
no trailing slash, no query string, no fragment. String equality is the join operation,
so this normalization rule is normative.
- **Selection:** the key is always the WORK ITEM's URL, never a change/PR URL. A change that
closes N items yields N per-item associations. An agent session keys on the single item it
was dispatched to work.
- **Scope:** RESOURCE-scope on agent-session emission, so session cost and token metrics and
session spans all carry it; span-scope on CI pipeline and task spans.

Granularity guarantee: conforming autonomous dispatch runs ONE leased work item per emitting
session/process — the trigger layer's lease contract is the guarantor. A multi-item batch
session gets session-granular cost only; that limitation is stated, never silently
misattributed.

Known join-epoch limitations: a repository rename or transfer, or a tracker migration,
changes the canonical URL. The join is query-time, so a sink MAY remap historical values
across such an epoch. A secondary immutable-ID attribute is deferred with a trigger: rename
churn proving material in practice.

Confidentiality: the attribute value inherits the confidentiality class of the repository or
tracker it references. Any sink, artifact, or export carrying it must enforce access controls
at least as strict as the item's home.

Namespace governance: this contract defines no other custom attribute. A sibling capability
contract in this home MAY define its own additions under the same `autonomy.*` prefix and
governance — reviewed contract changes, no parallel schema for upstream-named concepts.
Minimality binds this contract; it does not forbid governed extension. If the upstream
conventions ever ship a work-item/tracker namespace, this attribute migrates to it under the
same reviewed-migration rule.

## Pillar 3 — one causal tree

W3C `traceparent` context propagates trigger → CI → agent session, forming one causal tree
per triggered chain. This is a headless/CI/runner property: conforming headless agent
surfaces read inbound trace context from their environment; interactive contexts are
explicitly excluded — the contract does not promise inbound trace joining for an interactive
session, which deliberately ignores ambient context.

## Sink binding — out of contract

Where telemetry lands is deployment-owned. The contract names sink CLASSES only:

1. **Existing observability stack** — the org already runs one; emission points at it.
2. **File-artifact free default** — no stack exists: emissions land as OTLP JSON-lines
artifacts (the OTLP file-exporter encoding), queried on read. Zero standing
infrastructure, zero cost by default.
3. **Opt-in network backend** — self-hosted or paid; always explicit opt-in with cost
surfaced first, never a default.

An adapter for any class MUST preserve the emitted signals unmodified (schema, attributes,
`schema_url`); class choice, endpoints, and storage are the adopting deployment's. No vendor
is named or privileged by this contract.

## Native-surface principle

Prefer each tool's native telemetry export over reimplementing it. A capability that
re-derives what a native surface already emits is non-conforming; wrap, configure, or
transport native output instead.

## Telemetry is not return

Usage measures activity, not return. Nothing in this contract's data answers whether work
was worth doing; the return-accounting convention owns that question, joining its
human-attested records to this telemetry by the Pillar 2 attribute at query time. Telemetry
alone is never presented as return.
60 changes: 51 additions & 9 deletions plugins/autonomy/skills/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,60 @@ ask and offer to persist; otherwise → safe free-tier default).
- `substrate` — an object with kebab-case surface keys (`local-machine`, `ci-runners`,
`self-run-infrastructure`), boolean values.

Capability slices add their own sections under their slice name. The same file name is the
shape at EVERY layer: the user-global layer is `~/.claude/autonomy/binding.json`, the project
layer `.claude/autonomy/binding.json`, and each layer's personal overlay
`binding.local.json` beside it. The project file is tracked (team-shared); recommend the
consumer `.gitignore` line: `.claude/autonomy/**/*.local.*`. Layers resolve per the
binding-seam ladder — user-global → org binding (when pointed) → project → local overlay —
additively.
The same file name is the shape at EVERY layer: the user-global layer is
`~/.claude/autonomy/binding.json`, the project layer `.claude/autonomy/binding.json`, and
each layer's personal overlay `binding.local.json` beside it. The project file is tracked
(team-shared); recommend the consumer `.gitignore` line: `.claude/autonomy/**/*.local.*`.
Layers resolve per the binding-seam ladder — user-global → org binding (when pointed) →
project → local overlay — additively. Capability slices (like telemetry below) add their
sections ADDITIVELY under their slice name: a binding without a slice's section is valid
(absent-section tolerance) and no schema major bump is needed for an additive section.

## Telemetry slice

Wires the emitting state of
[`${CLAUDE_PLUGIN_ROOT}/reference/telemetry.md`](${CLAUDE_PLUGIN_ROOT}/reference/telemetry.md)
for all three execution contexts, discovery-first. Everything lands as reviewable changes;
paid sinks are advisory + explicit opt-in with cost surfaced first.

1. **Detect an existing observability stack** — interview + repo/env inspection (`OTEL_*`
endpoints in settings/env blocks, collector configs, known backend config files). Found →
wire emission toward it: agent-session env block (settings `env`) and a CI emission snippet
pointing at the org's endpoint. Paid/hosted stack → advisory with cost surfaced before any
opt-in.
2. **No stack → the file-artifact free default** (zero paid dependencies):
- CI pipeline spans via the OTLP JSON-lines writer snippet in
[`templates/ci-otlp-artifact.md`](templates/ci-otlp-artifact.md), uploading the artifact
directory per run;
- agent-session signals via the ephemeral per-job collector in the same template (single
static OSS collector binary + file-exporter config writing JSON-lines into the same
artifact directory — per-job, no standing infrastructure);
- interactive sessions get the same coverage: env block toward the discovered stack when
one exists, else a local collector instance (same binary + config template) exporting
into a local query-on-read store directory.
- Cost caveat surfaced on private repos: artifact storage and per-job collector runtime
draw from metered pools.
3. **Agent-session wiring (Claude Code specifics)** — `CLAUDE_CODE_ENABLE_TELEMETRY=1`,
per-signal `OTEL_*_EXPORTER` values, and for work-item-dispatched sessions
`OTEL_RESOURCE_ATTRIBUTES` carrying `autonomy.work_item.url=<canonical item URL>` (the
vendor attaches resource attributes to every metric datapoint and event — verified against
the official monitoring doc). Headless `-p` sessions inherit `TRACEPARENT`/`TRACESTATE`
from the environment; interactive sessions deliberately ignore inbound trace context.
Traces stay beta behind `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1`; the slice treats spans as
optional and never depends on beta span shapes.
4. **Record the binding** — sink class, endpoint or artifact path, and the semconv pin land
as the `telemetry` section of the schema-versioned binding.
5. **Conformance** — run
[`scripts/check-emission-conformance.mjs`](scripts/check-emission-conformance.mjs) against
produced OTLP JSON-lines to verify the pinned `schemaUrl` and the join attribute before
declaring the emitting state reached.

## What this skill does NOT do

- Wire any capability slice (telemetry, capture, adapters) — those land with their own work
packages and extend this skill when they ship.
- Wire capability slices that have not shipped yet (capture, adapters) — each lands with its
own work package and extends this skill.
- Mutate platform settings, user settings, or `pluginConfigs`.
- Assume the shape of any particular org or fleet — a run against an unknown repo asks or
defaults; it never guesses silently.
- Recommend or privilege any observability vendor — sink classes only; the deployment picks
instances.
39 changes: 39 additions & 0 deletions plugins/autonomy/skills/setup/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,45 @@
"Grounds every bound value in an inspected signal or an explicit user answer",
"Never treats any particular org/fleet layout as 'the usual place'"
]
},
{
"id": 6,
"name": "telemetry-existing-stack-wire-path",
"prompt": "/autonomy:setup apply\n\nWe already run an observability stack; its OTLP endpoint is otlp.internal.example:4318.",
"expected_output": "Wires emission toward the declared existing stack (session env block + CI snippet pointing at the org endpoint) as reviewable changes, records sink class existing-stack in the binding's telemetry section, and does not stand up the file-artifact default alongside it. No vendor is recommended; the org's own endpoint is used as given.",
"files": [],
"expectations": [
"Session env block and CI snippet target the declared endpoint",
"Binding telemetry section records sink class existing-stack",
"File-artifact default not wired when a stack exists",
"No observability vendor recommended or privileged"
]
},
{
"id": 7,
"name": "telemetry-free-default-when-no-stack",
"prompt": "/autonomy:setup apply --org-policy-home none --budget-posture free\n\nWire telemetry too; we have no observability stack.",
"expected_output": "Applies the file-artifact free default: CI pipeline-span JSON-lines writer plus per-job collector capture into one artifact directory, interactive coverage via a local collector store, OTEL_RESOURCE_ATTRIBUTES carrying autonomy.work_item.url for dispatched sessions, the conformance check named as the verification step, and the private-repo metered-cost caveat surfaced. Zero paid dependencies introduced.",
"files": [],
"expectations": [
"One artifact directory receives both pipeline and session JSON-lines",
"OTEL_RESOURCE_ATTRIBUTES carries autonomy.work_item.url for dispatched sessions",
"check-emission-conformance named as the verification step",
"Metered private-repo cost caveat surfaced; no paid dependency added"
]
},
{
"id": 8,
"name": "telemetry-paid-sink-advisory-not-default",
"prompt": "/autonomy:setup apply\n\nJust send everything to a hosted observability SaaS, whatever you think is best.",
"expected_output": "Refuses to default to a paid sink: surfaces that a hosted backend is a paid opt-in, presents the cost posture explicitly, names the free file-artifact default as the no-cost alternative, and picks no vendor on the user's behalf.",
"files": [],
"expectations": [
"Paid/hosted sink treated as advisory + explicit opt-in, never default",
"Cost surfaced before any opt-in question",
"Free file-artifact default offered as the alternative",
"No specific vendor chosen for the user"
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"resourceSpans":[{"schemaUrl":"https://opentelemetry.io/schemas/1.43.0","resource":{"attributes":[]},"scopeSpans":[{"scope":{"name":"demo-ci"},"spans":[{"traceId":"2aa971adccd8ab35cfa405854628db2f","spanId":"d79ce0092ae71d97","parentSpanId":"e83d2fc23d34ff22","name":"demo-pipeline","kind":2,"startTimeUnixNano":"1784386857774921700","endTimeUnixNano":"1784386857896765600","attributes":[{"key":"cicd.pipeline.run.id","value":{"stringValue":"demo-run-1"}},{"key":"autonomy.work_item.url","value":{"stringValue":"https://tracker.example/demo-org/demo-repo/items/101"}}]}]}]}]}
Loading
Loading