|
1 | 1 | --- |
2 | 2 | name: ctvs-gascity |
3 | | -description: Query the gascity event log and session-reconciler segments registered by `ctvs init gascity` in this workspace. Use when the user asks about gc agents, beads, orders, mail, sessions, or session-reconciler decisions. |
| 3 | +description: Query the gascity event log, session-reconciler segments, and `gascity_messages` agent transcripts. Use when the user asks about gc agents, beads, orders, mail, sessions, session-reconciler decisions, agent tool calls, or LLM token usage by rig/template. |
4 | 4 | --- |
5 | 5 |
|
6 | 6 | # Gascity Query |
7 | 7 |
|
8 | | -This workspace has been registered with the `ctvs query` cache via `ctvs init gascity`. Two collection tables are available alongside the built-in datasets: |
| 8 | +This workspace has been registered with the `ctvs query` cache via `ctvs init gascity`. Three tables are available alongside the global built-in datasets: |
9 | 9 |
|
10 | | -- **`events`** — one row per gascity event from `.gc/events.jsonl` (bead lifecycle, order execution, mail, sessions, controller events). Single source file. |
11 | | -- **`session_segments`** — one row per tracepoint from `.gc/runtime/session-reconciler-trace/segments/**/*.jsonl` (baseline, decision, mutation, operation records per session reconciler cycle). Glob-backed; many source files, one cache partition each. |
| 10 | +- **`events`** — one row per gascity event from `.gc/events.jsonl` (bead lifecycle, order execution, mail, sessions, controller events). Single source file, registered as a collection. |
| 11 | +- **`session_segments`** — one row per tracepoint from `.gc/runtime/session-reconciler-trace/segments/**/*.jsonl` (baseline, decision, mutation, operation records per session reconciler cycle). Glob-backed collection; many source files, one cache partition each. |
| 12 | +- **`gascity_messages`** — one row per content block from gascity-captured agent sessions (text, thinking, tool_use, tool_result, attachment). Captured by the `ctvs gascity` source from the supervisor REST API; provider-native frames preserved verbatim in `raw_frame`. Built-in dataset (no `ctvs init gascity` needed) — partitioned at `~/.collectivus/sink/gascity_messages/date=<YYYY-MM-DD>/city=<name>/`. |
12 | 13 |
|
13 | | -Refer to the global [`collectivus-query`](../collectivus-query/SKILL.md) skill for cache freshness rules, `--format` options, and built-in datasets like `proxy_messages`. |
| 14 | +Refer to the global [`collectivus-query`](../collectivus-query/SKILL.md) skill for cache freshness rules, `--format` options, and the wire-level `proxy_messages` dataset. |
14 | 15 |
|
15 | 16 | ## Agent targeting |
16 | 17 |
|
17 | | -There is no `cwd` column on these tables. Agent identity lives in different columns per table: |
| 18 | +Each table identifies the originating agent through a different column. There is no `cwd` on `events` or `session_segments`; on `gascity_messages` the `cwd` is the agent's working directory at frame time. |
18 | 19 |
|
19 | 20 | - `events.actor` — e.g. `hypcity-overrides.mayor`, `hypcity-overrides.refinery`, `hypcity-overrides.deacon`. |
20 | 21 | - `session_segments.template` — e.g. `hypcity-overrides.mayor` (city-scoped) or `collectivus/hypcity-overrides.polecat` (rig-scoped: `<rig>/<pack>.<agent>`). |
| 22 | +- `gascity_messages.gascity_template` — same shape as `session_segments.template`. Pair with `gascity_rig` and `gascity_alias` for finer cuts. The provider-side session id is `gascity_session_id` / `provider_session_id`. |
21 | 23 |
|
22 | 24 | Matching patterns: |
23 | 25 |
|
24 | 26 | ```sql |
25 | 27 | -- a specific agent (city-scoped) |
26 | 28 | where actor = 'hypcity-overrides.mayor' |
| 29 | +where gascity_template = 'hypcity-overrides.mayor' |
27 | 30 |
|
28 | 31 | -- any rig's refinery |
29 | 32 | where template LIKE '%/hypcity-overrides.refinery' |
| 33 | +where gascity_template LIKE '%/hypcity-overrides.refinery' |
30 | 34 |
|
31 | 35 | -- everything in the collectivus rig |
32 | 36 | where template LIKE 'collectivus/%' |
| 37 | +where gascity_rig = 'collectivus' |
33 | 38 | ``` |
34 | 39 |
|
35 | | -To cross-reference an agent's actions with its proxied LLM calls, join `template` (this skill) against `cwd` (global `proxy_messages` table) via the workspace path embedded in the rig prefix. |
| 40 | +To cross-reference an agent's actions with its proxied LLM calls, join `template` against `cwd` (`proxy_messages`) via the workspace path embedded in the rig prefix, or join `gascity_messages.provider_session_id` to `proxy_messages.conversation_id` when both sources captured the same Claude Code session. |
| 41 | + |
| 42 | +## `gascity_messages` schema highlights |
| 43 | + |
| 44 | +Grain is one row per content block (text, thinking, tool_use, tool_result, attachment). Run `ctvs query schema gascity_messages --format markdown` for the authoritative column list — there are 40+ columns; the highlights below are the ones you reach for first. |
| 45 | + |
| 46 | +- **Identity:** `gascity_template`, `gascity_rig`, `gascity_alias`, `gascity_session_id`, `provider`, `provider_session_id`, `provider_uuid`, `gateway_id` (constant `gascity-scribe` so cross-source unions can tag the source). |
| 47 | +- **Frame metadata:** `part_type` (`text` / `thinking` / `tool_use` / `tool_result` / `attachment`), `part_index`, `message_id`, `message_created_at`, `conversation_started_at`, `cwd`, `git_branch`, `permission_mode`, `is_sidechain`, `parent_uuid`, `prompt_id`, `request_id`. |
| 48 | +- **Assistant-only hoist (null elsewhere):** `model`, `stop_reason`, `stop_details`, `input_tokens`, `output_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens`, `ephemeral_1h_input_tokens`, `ephemeral_5m_input_tokens`, `service_tier`, `inference_geo`, `speed`. |
| 49 | +- **Content-block specific:** `content_text`, `thinking_signature`, `tool_name`, `tool_call_id`, `tool_args` (JSON), `caller_type`, `tool_result_for`, `is_error`, `attachment_type`, `hook_event`. |
| 50 | +- **Overflow:** `attributes` (unmapped fields, JSON) and `raw_frame` (verbatim original envelope, JSON) keep nothing the supervisor sent from being lost. |
| 51 | + |
| 52 | +> Note: `gascity_messages` does not carry a `role` column. Use `part_type` to split assistant-side frames (`text` / `thinking` / `tool_use`) from user/tool-side frames (`tool_result`). |
36 | 53 |
|
37 | 54 | ## Common queries |
38 | 55 |
|
@@ -74,10 +91,80 @@ ORDER BY ts; |
74 | 91 |
|
75 | 92 | Use `_ctvs_source_path` to see which segment file a row came from, and `_ctvs_line_number` for the line inside that file. |
76 | 93 |
|
| 94 | +## `gascity_messages` queries |
| 95 | + |
| 96 | +```sql |
| 97 | +-- All tool calls by the mayor today |
| 98 | +SELECT message_created_at, tool_name, JSON_VALUE(tool_args, '$.command') AS command |
| 99 | +FROM gascity_messages |
| 100 | +WHERE gascity_template = 'hypcity-overrides.mayor' |
| 101 | + AND part_type = 'tool_use' |
| 102 | + AND date = strftime(NOW(), '%Y-%m-%d') |
| 103 | +ORDER BY message_created_at DESC |
| 104 | +LIMIT 50; |
| 105 | + |
| 106 | +-- Token usage by rig over the last 24h. `input_tokens` is set only on |
| 107 | +-- assistant frames, so summing across the whole table picks up exactly the |
| 108 | +-- assistant-side cost (NULL inputs from tool_use / tool_result / text are |
| 109 | +-- skipped by SUM). |
| 110 | +SELECT gascity_rig, |
| 111 | + SUM(input_tokens) AS input, |
| 112 | + SUM(output_tokens) AS output, |
| 113 | + SUM(cache_read_input_tokens) AS cache_hits, |
| 114 | + SUM(cache_creation_input_tokens) AS cache_creates |
| 115 | +FROM gascity_messages |
| 116 | +WHERE message_created_at >= NOW() - INTERVAL '1 day' |
| 117 | + AND input_tokens IS NOT NULL |
| 118 | +GROUP BY gascity_rig |
| 119 | +ORDER BY input DESC; |
| 120 | + |
| 121 | +-- Tool result that came back as an error, last hour, any agent |
| 122 | +SELECT gascity_template, tool_name, content_text |
| 123 | +FROM gascity_messages |
| 124 | +WHERE part_type = 'tool_result' |
| 125 | + AND is_error = true |
| 126 | + AND message_created_at >= NOW() - INTERVAL '1 hour' |
| 127 | +ORDER BY message_created_at DESC; |
| 128 | + |
| 129 | +-- Sanity-check: for a Claude Code session captured by both sources, |
| 130 | +-- compare token counts. `proxy_messages.conversation_id` is the Claude |
| 131 | +-- Code session id, which gascity records as `provider_session_id`. |
| 132 | +SELECT |
| 133 | + COALESCE(p.conversation_id, g.provider_session_id) AS session, |
| 134 | + SUM(CAST(JSON_VALUE(p.attributes, '$.usage.input_tokens') AS BIGINT)) AS proxy_input, |
| 135 | + SUM(g.input_tokens) AS gascity_input |
| 136 | +FROM proxy_messages p |
| 137 | +FULL OUTER JOIN gascity_messages g |
| 138 | + ON p.conversation_id = g.provider_session_id |
| 139 | +WHERE p.role = 'assistant' |
| 140 | + OR g.input_tokens IS NOT NULL |
| 141 | +GROUP BY session |
| 142 | +ORDER BY proxy_input DESC NULLS LAST |
| 143 | +LIMIT 20; |
| 144 | + |
| 145 | +-- Cross-source UNION: text content from both sources for one Claude Code session |
| 146 | +SELECT 'proxy' AS source, message_index AS idx, content_text |
| 147 | +FROM proxy_messages |
| 148 | +WHERE conversation_id = '<session-id>' AND role = 'assistant' AND part_type = 'text' |
| 149 | +UNION ALL |
| 150 | +SELECT 'gascity' AS source, part_index AS idx, content_text |
| 151 | +FROM gascity_messages |
| 152 | +WHERE provider_session_id = '<session-id>' AND part_type = 'text' |
| 153 | +ORDER BY source, idx; |
| 154 | +``` |
| 155 | + |
| 156 | +## When to use which source |
| 157 | + |
| 158 | +- Use **`gascity_messages`** when you want: agent identity (`gascity_template` / `gascity_rig`), structured content blocks, tool calls + arguments + results in one table, per-frame token usage with cache breakdown, no need for HTTP wire detail. |
| 159 | +- Use **`proxy_messages`** when you want: HTTP retry visibility, request timing, response status codes, end-user attribution via the Anthropic `user_id`, conversation-grain dedup of replayed history. |
| 160 | +- Use **both** (UNION ALL or FULL OUTER JOIN) for cross-source aggregations, sanity checks, or to recover content that one source missed (e.g., gascity captured an in-process supervisor frame the proxy never saw). |
| 161 | + |
77 | 162 | ## Freshness |
78 | 163 |
|
79 | 164 | `session_segments` is glob-backed: new segment files only appear in the cache after a refresh. Run `ctvs query refresh session_segments` to pick up new segments, or use `--refresh always` on any query. Deleted segment files are pruned from the cache on the next refresh. |
80 | 165 |
|
81 | 166 | `events` is append-only single-file; mtime/size changes trigger re-materialization on refresh. |
82 | 167 |
|
83 | | -Full schemas: `ctvs query schema events --format markdown`, `ctvs query schema session_segments --format markdown`. Catalog: `ctvs query catalog --format markdown`. |
| 168 | +`gascity_messages` is **always fresh** — the daemon writes Parquet directly into the sink (no JSONL stage, no `.meta.json` sidecar), so query-time discovery picks up every part-file the writer has flushed. `ctvs query refresh gascity_messages` is a documented no-op (it lists existing partitions as already-fresh). To pull in newly-flushed rows simply rerun the query. |
| 169 | + |
| 170 | +Full schemas: `ctvs query schema events --format markdown`, `ctvs query schema session_segments --format markdown`, `ctvs query schema gascity_messages --format markdown`. Catalog: `ctvs query catalog --format markdown`. |
0 commit comments