Skip to content

Commit 7296ec7

Browse files
committed
Add log-driven development skill bundle
1 parent ca5a355 commit 7296ec7

12 files changed

Lines changed: 538 additions & 15 deletions

File tree

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -362,15 +362,17 @@ their cross-source joins with `proxy_messages`.
362362

363363
### LLM skill
364364

365-
Install the bundled `collectivus-query` skill so Claude Code and Codex know how
366-
to inspect local recordings with `ctvs query`:
365+
Install the bundled Collectivus skills so Claude Code and Codex know how to
366+
inspect local recordings with `ctvs query` and build log-driven development
367+
loops:
367368

368369
```bash
369370
ctvs skills install --client all
370371
```
371372

372-
The skill assumes the default `~/.hyp/collectivus.json` config unless the agent
373-
discovers a non-default service config from `ctvs status` or the service unit.
373+
The query skill assumes the default `~/.hyp/collectivus.json` config unless the
374+
agent discovers a non-default service config from `ctvs status` or the service
375+
unit.
374376

375377
## OTLP receiver
376378

@@ -627,7 +629,7 @@ supported in this build.
627629
| `ctvs export --config <path> [...]` | Convert recorded JSONL to local Parquet without invoking the upload scheduler |
628630
| `ctvs query <command> [...]` | Query local recordings through the explicit query cache |
629631
| `ctvs collect <file.jsonl>\|--glob <pattern> --name <name>` | Register external JSONL as a dynamic query table |
630-
| `ctvs skills install [--client claude\|codex\|all]` | Install the bundled Collectivus query LLM skill |
632+
| `ctvs skills install [--client claude\|codex\|all]` | Install the bundled Collectivus LLM skills |
631633
632634
If stdin is not a TTY, `install` refuses to guess: pass `--yes` to attach
633635
Claude Code unattended, or `--no` to skip the attach step.

ctvs-vs-claude-code-logs.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# ctvs proxy logs vs Claude Code logs
2+
3+
**Date:** 2026-05-18
4+
**Author:** investigation captured by Claude (Opus 4.7) working with phil
5+
**Context:** Investigating whether the Gas City supervisor can surface reasoning tokens from Claude sessions. The investigation expanded into a comparison of three vantage points: Claude Code's on-disk JSONL, the Gas City supervisor's HTTP API, and the ctvs HTTPS proxy capture.
6+
7+
---
8+
9+
## TL;DR
10+
11+
ctvs captures the full HTTPS traffic between Claude Code and Anthropic's API. Claude Code's JSONL captures the post-processed conversation it chose to persist locally. **ctvs is essentially a superset on the request side**, plus it captures wire metadata Claude Code never sees. Claude Code adds a few client-side artifacts ctvs doesn't have (hook output, file snapshots, DAG structure).
12+
13+
Crucially, ctvs is the only layer with visibility into **what Claude Code told Anthropic about your project** — system prompt, tool schemas, request parameters, beta headers, context-management strategies.
14+
15+
---
16+
17+
## What ctvs has that Claude Code JSONL does not
18+
19+
### 1. The exact system prompt as sent to the API
20+
21+
Every API call carries a system prompt that Claude Code constructs at request time:
22+
23+
- Claude Code identity + safety rules
24+
- Tool-use guidance and conventions
25+
- Your project's `CLAUDE.md` / `AGENTS.md` content
26+
- Auto-memory blob from `~/.claude/projects/.../memory/`
27+
- Git status snapshot at conversation start
28+
- Environment block (platform, shell, working directory, model ID)
29+
30+
ctvs's `proxy_messages.system_text` column has the full ~30 KB blob per request, prefixed with a billing header:
31+
```
32+
x-anthropic-billing-header: cc_version=2.1.143.e33; cc_entrypoint=cli; cch=fe500;
33+
34+
You are Claude Code, Anthropic's official CLI for Claude.
35+
...
36+
```
37+
38+
The Claude Code JSONL has none of this. Your project's user instructions ARE visible inside the system prompt (so the prompt content is reconstructible), but the framing/guardrail prose around them is not in the JSONL anywhere.
39+
40+
### 2. Tool definitions as sent
41+
42+
ctvs has the full tool JSON Schema list per request: every tool's `name`, `description`, parameter schemas, examples — the exact contract Claude Code declared to the API.
43+
44+
Claude Code logs only show tool *invocations* (`tool_use` blocks with `{name, input}`), not the schemas. If you want to know which subagent types were available, which Bash flags were enabled, what each tool's description was — that's only in ctvs.
45+
46+
### 3. Request envelope and parameters
47+
48+
Captured under `proxy_messages.attributes.request`:
49+
50+
- `model` — exact model ID used
51+
- `max_tokens` (64,000 in current captures)
52+
- `thinking.type``adaptive` / `enabled` / `disabled`
53+
- `output_config.effort``low` / `medium` / `high` / `xhigh` / `max` / `auto`
54+
- `context_management.edits` — including the `clear_thinking_20251015` strategy responsible for thinking redaction on Opus 4.7
55+
- `stream: true`
56+
- Beta headers requested (e.g. `context-management-2025-06-27`)
57+
- `metadata.user_id``{device_id, account_uuid, session_id}` (your Anthropic account identifiers)
58+
59+
None of this exists in the Claude Code JSONL. If you want to know **why** thinking is empty in your logs, the answer is in `attributes.request.context_management` — and only ctvs has it.
60+
61+
### 4. Wire-level metadata
62+
63+
- `latency_ms` per request (4,867 ms on a sample)
64+
- `provider_raw` — Anthropic API response headers / envelope
65+
- HTTP status, retries (via `ctvs query proxy events`)
66+
- OTLP traces and metrics (separate datasets in ctvs)
67+
68+
### 5. Per-message billing detail
69+
70+
Both layers have token counts. ctvs reflects what was *actually billed* server-side, including server-tool calls (`web_search_requests`, `web_fetch_requests`, etc.).
71+
72+
### 6. Cross-session conversation continuity
73+
74+
ctvs has a `conversation_id` that can span multiple Claude Code session resumes. Claude Code's JSONL splits on its own session ID; `claude --resume` produces a new file. ctvs stitches them by API request lineage, so you can reconstruct a full multi-day conversation as a single thread.
75+
76+
### 7. Plaintext reasoning, when the API returns it
77+
78+
Both layers happen to have plaintext when the model produces it (e.g., 163/163 Opus 4.6 thinking blocks had plaintext in both Claude Code JSONL and ctvs proxy capture). But ctvs is independent of Claude Code's choice to persist. If a future Claude Code version starts stripping thinking from JSONL even when the API returns it — or if you swap Claude Code for a custom client that doesn't write JSONL at all — ctvs would still capture it.
79+
80+
---
81+
82+
## What Claude Code JSONL has that ctvs does not
83+
84+
The relationship isn't strictly "ctvs is a superset." Claude Code adds client-side artifacts ctvs has no visibility into:
85+
86+
| Field | What it carries |
87+
|---|---|
88+
| `uuid` / `parentUuid` | Conversation DAG — branching, sidechains, retries |
89+
| `compactMetadata` | When and how context was auto-compacted |
90+
| `attachment.hookName` | `SessionStart:startup` hook stdout/stderr |
91+
| `file-history-snapshot` | Claude Code's internal snapshots of file state |
92+
| `ai-title` | Model-generated session title |
93+
| `permission-mode` | When permission state flipped during the session |
94+
| `cwd` / `gitBranch` per entry | Local state at each message timestamp |
95+
| Uploaded attachments | Files the user dropped into the session UI |
96+
97+
Claude Code also has DAG semantics — it lets you reconstruct which response branched from which user message, including ones that were retried or abandoned. ctvs sees a flat sequence of API calls.
98+
99+
---
100+
101+
## Side-by-side: where each layer lives
102+
103+
```
104+
┌─────────────────────────────────────────────────────────────────┐
105+
│ User │
106+
│ ↓ │
107+
│ Claude Code CLI (2.1.143) │
108+
│ ↓ writes → ~/.claude/projects/{slug}/{id}.jsonl │
109+
│ ↓ │
110+
│ HTTPS → ← CAPTURED BY ctvs PROXY (127.0.0.1:8787) │
111+
│ ↓ ↓ │
112+
│ Anthropic API /Users/phil/.hyp/collectivus/ │
113+
│ phil/proxy/YYYY-MM-DD.jsonl │
114+
│ │
115+
│ Separately, Gas City supervisor (127.0.0.1:8372) serves the │
116+
│ Claude Code JSONL files via │
117+
│ GET /v0/city/{name}/session/{id}/transcript[?format=raw] │
118+
│ It reads the same files Claude Code wrote — no extra data. │
119+
└─────────────────────────────────────────────────────────────────┘
120+
```
121+
122+
| Layer | What it stores | Reasoning visible? |
123+
|---|---|---|
124+
| **Anthropic API** | Server-side state | Only Anthropic sees raw reasoning |
125+
| **ctvs proxy capture** | Full request/response over the wire | When API returns plaintext (model-dependent) |
126+
| **Claude Code JSONL** | Conversation turns + client artifacts | When Claude Code chose to persist it (model + version-dependent) |
127+
| **Gas City supervisor** | Pass-through of Claude Code JSONL | Same as Claude Code JSONL |
128+
129+
---
130+
131+
## Why this distinction matters
132+
133+
If you want to know **what Claude said** → either layer works (when reasoning is present at all).
134+
135+
If you want to know **what Claude Code told Anthropic about your project** → only ctvs has it. That includes:
136+
- The full system prompt with your `CLAUDE.md` injected
137+
- Every tool schema Claude Code advertised
138+
- The `clear_thinking_20251015` context-management strategy that strips thinking
139+
- Beta API flags Claude Code opted into
140+
- Your Anthropic account identifiers
141+
- Wire timings and retries
142+
143+
If you want **client-side state** (file snapshots, hook output, permission changes, DAG branching) → only Claude Code JSONL has it.
144+
145+
If you want **cross-session conversation continuity** → only ctvs's `conversation_id` stitches multi-resume sessions together.
146+
147+
---
148+
149+
## Concrete columns reference
150+
151+
### ctvs `proxy_messages` schema (28 columns)
152+
153+
```
154+
gateway_id, schema_version, conversation_id, user_id, provider, model,
155+
system_text, tools, conversation_started_at, conversation_source,
156+
cwd, git_branch, message_id, previous_message_id, message_index,
157+
message_created_at, role, part_id, part_index, part_type,
158+
content_text, tool_name, tool_call_id, tool_args, thinking_signature,
159+
status, attributes, date
160+
```
161+
162+
### Claude Code JSONL top-level fields (representative)
163+
164+
```
165+
uuid, parentUuid, type, subtype, message{role, content[], model, usage},
166+
toolUseID, logicalParentUuid, compactMetadata, isCompactSummary,
167+
timestamp, sessionId, cwd, gitBranch, version, requestId, userType,
168+
entrypoint, attachment{hookName, content, stdout, stderr, ...}
169+
```
170+
171+
The overlap (model, role, message content, timestamps, tool calls, tool results) is what most users see. The non-overlap is where ctvs's value lives.
172+
173+
---
174+
175+
## Footnote: how this report was produced
176+
177+
Live `testcity` supervisor at `127.0.0.1:8372`. ctvs daemon at `127.0.0.1:8787` with proxy recordings under `~/.hyp/collectivus/phil/proxy/`. Counts and samples queried via `ctvs query sql` against the materialized parquet cache. Cross-checked against raw JSONL at `~/.claude/projects/-Users-phil-workspace-gascity/eaec666c-b3ce-46d7-b2c9-66e9f67d65e5.jsonl` (the file backing this very conversation).
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
name: log-driven-development
3+
description: Instrument applications and embedded LLM-agent workflows with Collectivus OTEL and local JSONL so Claude Code or Codex can self-test, query logs/traces/metrics, and iteratively fix issues. Use when building or modifying apps, adding smoke tests, debugging development loops, adding OpenTelemetry, capturing agent events, or using ctvs query/ctvs collect to close a log-driven feedback loop.
4+
---
5+
6+
# Log Driven Development
7+
8+
## Overview
9+
10+
Treat telemetry as part of the development harness. Add instrumentation early, run a narrow smoke path with a unique run id, inspect Collectivus recordings, then fix or improve the app based on observed behavior.
11+
12+
## Core Loop
13+
14+
1. Pick a stable `service.name` and a per-run id such as `DEV_RUN_ID=smoke-<utc>-<pid>`.
15+
2. Confirm the Collectivus OTLP endpoint before wiring code. Default local OTLP/HTTP is `http://127.0.0.1:4318`; signal endpoints are `POST /v1/traces`, `POST /v1/metrics`, and `POST /v1/logs`.
16+
3. Instrument liberally around entrypoints, state transitions, branches, external calls, background jobs, tool calls, retries, validation checks, and error paths.
17+
4. Add or update a smoke command that exercises the smallest complete workflow and emits telemetry for each step and assertion.
18+
5. Run the smoke command, query the resulting `logs`, `traces`, `metrics`, and collected JSONL tables, then make the next code change from evidence rather than guesses.
19+
6. Repeat until the smoke path succeeds and the telemetry shows the expected internal sequence, not just a passing exit code.
20+
21+
## Instrumentation Bias
22+
23+
Prefer broad, cheap, local-first instrumentation over sparse "production-quality" telemetry while developing. It is acceptable for dev telemetry to be chatty when it is guarded by environment flags, avoids secrets, and can be disabled or sampled before production.
24+
25+
Use structured attributes that are easy to query: `dev_run_id`, `smoke_name`, `smoke_step`, `component`, `operation`, `status`, `error_kind`, `tool_name`, `agent_id`, and domain ids. Prefer snake_case for custom attributes so SQL JSON paths stay simple. Keep high-cardinality or large values in local JSONL or short hashes/excerpts, not every OTEL span.
26+
27+
Do not record secrets, credentials, raw customer data, or private prompts by default. For LLM agents, record decisions, tool calls, model settings, token counts, latency, validation outcomes, content hashes, and short redacted excerpts; do not record hidden chain-of-thought.
28+
29+
## Reference Loading
30+
31+
- Read [references/collectivus-otel.md](references/collectivus-otel.md) when adding OpenTelemetry libraries, manual OTLP emitters, or Collectivus-specific endpoint configuration.
32+
- Read [references/agent-jsonl.md](references/agent-jsonl.md) when the application contains LLM agents, planners, routers, tool callers, evaluators, or other model-driven flows that should write local JSONL for `ctvs collect`.
33+
- Read [references/self-test-loop.md](references/self-test-loop.md) when adding the smoke command or querying the captured data after a run.
34+
35+
If the `collectivus-query` skill is available, use it for detailed `ctvs query` cache freshness and SQL rules. Otherwise run `ctvs query status`, prefer `--format json` for analysis, and refresh the exact source file when the CLI reports a missing cache partition.
36+
37+
## Done Criteria
38+
39+
- The app can run with telemetry enabled using documented env vars or dev config.
40+
- Each smoke run has a queryable `dev_run_id` across logs/spans/metrics and any local agent JSONL table.
41+
- The smoke test checks both external behavior and captured internal signals.
42+
- Failures produce enough telemetry to identify which step, component, or agent decision failed.
43+
- The final response mentions the smoke command and the key `ctvs query` or `ctvs collect` command used for verification.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
interface:
2+
display_name: "Log Driven Development"
3+
short_description: "Instrument apps and close feedback loops"
4+
default_prompt: "Use $log-driven-development to instrument this app with Collectivus OTEL and JSONL feedback loops, then smoke test by querying the captured logs."
5+
policy:
6+
allow_implicit_invocation: true
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Local JSONL For LLM Agents
2+
3+
Applications that embed LLM agents should write a local append-only JSONL event stream in addition to OTEL. OTEL gives causality and latency; JSONL preserves agent-specific event shape that is easier to query with `ctvs collect`.
4+
5+
## File Layout
6+
7+
Use a local, gitignored path:
8+
9+
```text
10+
.collectivus/agents/<agent-id>/<YYYY-MM-DD>.jsonl
11+
```
12+
13+
If the repo uses another runtime/log directory, follow that convention, but keep files local, append-only, and easy to glob. Add the path to `.gitignore` unless the repo already ignores the parent directory.
14+
15+
Register the stream:
16+
17+
```bash
18+
ctvs collect --glob '.collectivus/agents/**/*.jsonl' \
19+
--name app-agent-events \
20+
--timestamp-column timestamp \
21+
--replace
22+
ctvs query schema app_agent_events --format markdown
23+
```
24+
25+
`ctvs collect` infers top-level fields as SQL columns. Keep common fields top-level and put rare or nested details in `metadata`.
26+
27+
## Event Shape
28+
29+
Use one JSON object per line. Keep field names stable.
30+
31+
```jsonl
32+
{"timestamp":"2026-05-18T19:00:00.000Z","dev_run_id":"smoke-20260518T190000Z-123","agent_id":"planner","conversation_id":"conv-abc","turn_id":"turn-001","event_type":"tool_call","phase":"act","status":"started","model":"claude-opus-4-7","provider":"anthropic","tool_name":"search_inventory","tool_call_id":"toolu_123","latency_ms":null,"input_sha256":"a1b2","output_sha256":null,"content_excerpt":"search inventory for sku prefix","metadata":{"route":"inventory.lookup"}}
33+
```
34+
35+
Recommended top-level fields:
36+
37+
- `timestamp`: ISO-8601 UTC time. Use this with `--timestamp-column timestamp`.
38+
- `dev_run_id`: matches the smoke/test run id used in OTEL.
39+
- `agent_id`: stable logical agent name.
40+
- `conversation_id`, `turn_id`, `parent_turn_id`: correlate multi-turn flows.
41+
- `event_type`: `run_start`, `prompt_built`, `model_request`, `model_response`, `tool_call`, `tool_result`, `decision`, `validation`, `run_end`, `error`.
42+
- `phase`: `plan`, `act`, `observe`, `reflect`, `evaluate`, or a domain-specific phase.
43+
- `status`: `started`, `ok`, `error`, `skipped`, `retrying`.
44+
- `model`, `provider`: model metadata when applicable.
45+
- `tool_name`, `tool_call_id`: tool correlation when applicable.
46+
- `latency_ms`, `prompt_tokens`, `completion_tokens`, `total_tokens`: numeric cost/performance fields.
47+
- `input_sha256`, `output_sha256`: stable hashes for large or sensitive content.
48+
- `content_excerpt`: short redacted excerpt for local debugging.
49+
- `error_kind`, `error_message`: bounded error detail.
50+
- `metadata`: nested JSON for rare details.
51+
52+
## What To Log
53+
54+
Log:
55+
56+
- Agent run start/end with inputs summarized by id/hash.
57+
- Prompt/template version, selected tools, model, temperature, max tokens, and safety settings.
58+
- Retrieval/search query summaries, hit counts, chosen ids, and score ranges.
59+
- Tool call arguments after redaction, tool results summarized by ids/counts/status, and latency.
60+
- Model response metadata, finish reason, token usage, and structured output validation results.
61+
- Decisions and routing choices as explicit data: `decision`, `alternatives`, `reason_code`, `confidence`.
62+
- Evaluator or smoke-test assertions: expected, actual summary, pass/fail, and linked domain ids.
63+
- Errors, retries, cancellations, and fallbacks.
64+
65+
Do not log hidden chain-of-thought. When reasoning is useful, log a brief decision rationale or machine-readable reason code instead.
66+
67+
## Writer Requirements
68+
69+
The writer should:
70+
71+
- Create parent directories.
72+
- Append exactly one compact JSON object plus `\n` per event.
73+
- Avoid partial lines where possible.
74+
- Never throw in the main application path because logging failed.
75+
- Redact secrets before writing.
76+
- Include `dev_run_id` on every event when available.
77+
78+
In tests and local smokes, assert that at least one event was written for each expected agent phase. Then use `ctvs collect` and SQL to inspect the same file from the outside.
79+
80+
## Query Examples
81+
82+
```bash
83+
ctvs query sql "select event_type, status, count(*) as events from app_agent_events group by event_type, status order by events desc" --format markdown
84+
```
85+
86+
```bash
87+
ctvs query sql "select timestamp, agent_id, phase, event_type, status, tool_name, error_message from app_agent_events where dev_run_id = '<run-id>' order by timestamp" --refresh always --format json
88+
```
89+
90+
```bash
91+
ctvs query sql "select agent_id, avg(latency_ms) as avg_latency_ms, count(*) as calls from app_agent_events where event_type in ('model_response', 'tool_result') and latency_ms is not null group by agent_id order by avg_latency_ms desc" --format markdown
92+
```

0 commit comments

Comments
 (0)