@aaac/observability 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/generated/program.ts","../../src/generated/contract.ts","../../src/cli/record.ts","../../src/cli/record-hook.ts","../../src/cli/query.ts","../../src/cli/backfill.ts","../../src/cli/handlers.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * aaac-observ CLI entry point.\n *\n * Reads the version from package.json, creates the generated Commander\n * program with hand-written handlers, and executes it.\n */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createProgram } from \"../generated/program.js\";\nimport { handlers } from \"./handlers.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(\n readFileSync(resolve(__dirname, \"../../package.json\"), \"utf8\"),\n) as { version: string };\n\nconst program = createProgram(handlers, pkg.version);\nawait program.parseAsync();\n","// Auto-generated by cli-contracts. Do not edit.\nimport { Command } from \"commander\";\nimport { CONTRACT_YAML, CONTRACT_JSON_STR } from \"./contract.js\";\n\nexport interface CommandHandlers {\n record: (options: { eventType?: string; lifecycle?: string; spanId?: string; parentSpanId?: string; sessionId?: string; traceId?: string; source?: string; attr?: string; link?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n recordHook: (hookName: string | undefined, options: { mappingConfig?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n backfill: (options: { db?: string; endpoint?: string; serviceName?: string; from?: string; to?: string; eventType?: string; dryRun?: boolean; flushTimeoutMs?: number }, parentOpts: Record<string, unknown>) => Promise<void>;\n query: (options: { kind?: string; traceId?: string; spanId?: string; eventType?: string; taskId?: string; from?: string; to?: string; direction?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n}\n\nexport function createProgram(\n handlers: CommandHandlers,\n version: string,\n): Command {\n const program = new Command();\n program\n .name(\"aaac-observ\")\n .version(version, \"-V, --version\")\n .description(\"aaac-observ — register external events and query the observability store\");\n\n\n program\n .command(\"record\")\n .description(\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\")\n .option(\"-t, --event-type <value>\", \"Event type (e.g. promotion.commit, process.edit, agent.session)\")\n .option(\"-l, --lifecycle <value>\", \"Event lifecycle phase: open | close | event | instant\")\n .option(\"-s, --span-id <value>\", \"Span ID (auto-generated if omitted)\")\n .option(\"--parent-span-id <value>\", \"Parent span ID for hierarchy\")\n .option(\"--session-id <value>\", \"Session ID to associate with the event\")\n .option(\"--trace-id <value>\", \"Trace ID for distributed tracing\")\n .option(\"--source <value>\", \"Event source identifier (default: external)\")\n .option(\"--attr <value>\", \"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\")\n .option(\"--link <value>\", \"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.record(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"record-hook\")\n .description(\"Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\")\n .argument(\"<hook-name>\", \"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\")\n .option(\"--mapping-config <value>\", \"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (hookName, opts, cmd) => {\n await handlers.recordHook(hookName, opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"backfill\")\n .description(\"Re-emit historical spans from SQLite to an OTLP backend\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .option(\"--endpoint <value>\", \"OTLP HTTP endpoint URL (fallback: OTEL_EXPORTER_OTLP_ENDPOINT)\")\n .option(\"--service-name <value>\", \"OTEL service name reported to backend (default: @aaac/observability)\")\n .option(\"--from <value>\", \"Backfill spans starting at or after this ISO 8601 time\")\n .option(\"--to <value>\", \"Backfill spans starting at or before this ISO 8601 time\")\n .option(\"--event-type <value>\", \"Filter by event type (e.g. agent.session)\")\n .option(\"--dry-run\", \"Print span count without emitting to OTLP backend\")\n .option(\"--flush-timeout-ms <ms>\", \"Maximum ms to wait for OTLP flush before exit (default: 2000; use ≥30000 for >5k spans)\", (v) => parseInt(v, 10))\n .action(async (opts, cmd) => {\n await handlers.backfill(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"query\")\n .description(\"Query the observability store via QueryAdapter\")\n .option(\"-k, --kind <value>\", \"Query kind: trace | span | search | links\")\n .option(\"--trace-id <value>\", \"Trace ID for trace queries (--kind trace)\")\n .option(\"-s, --span-id <value>\", \"Span ID for span/links queries (--kind span or --kind links)\")\n .option(\"--event-type <value>\", \"Event type filter for search queries (--kind search)\")\n .option(\"--task-id <value>\", \"Task ID filter for search queries (--kind search)\")\n .option(\"--from <value>\", \"Start time in ISO 8601 format for search queries (--kind search)\")\n .option(\"--to <value>\", \"End time in ISO 8601 format for search queries (--kind search)\")\n .option(\"-d, --direction <value>\", \"Link traversal direction for links queries (default: both)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.query(opts, cmd.optsWithGlobals());\n });\n\n\n // Built-in extract command (auto-injected by cli-contracts)\n program\n .command(\"extract\")\n .description(\"Extract contract specification for this CLI tool.\")\n .argument(\"[commands...]\", \"Command IDs to extract. Use dot notation.\")\n .option(\"-a, --all\", \"Extract all commands.\", false)\n .option(\"--include-meta\", \"Include extraction metadata.\", true)\n .option(\"-F, --format <format>\", \"Output format (yaml or json).\", \"yaml\")\n .action(async (commands: string[], opts: { all?: boolean; includeMeta?: boolean; format?: string }, cmd: Command) => {\n if (commands.length === 0 && !opts.all) {\n process.stderr.write(JSON.stringify({ code: \"INVALID_ARGS\", message: \"Specify command IDs or use --all\" }) + \"\\n\");\n process.exit(2);\n }\n\n const format = opts.format || \"yaml\";\n const doc = JSON.parse(CONTRACT_JSON_STR);\n const cmdIds = opts.all ? [] : commands;\n\n if (cmdIds.length === 0) {\n // --all: output full contract\n if (format === \"json\") {\n const out: Record<string, unknown> = {};\n if (opts.includeMeta) {\n out._meta = {\n source: \"embedded\",\n type: \"cli-contracts/extract\",\n extractedAt: new Date().toISOString(),\n specVersion: doc.cli_contracts ?? \"0.1.0\",\n commands: [\"aaac-observ.record\",\"aaac-observ.record-hook\",\"aaac-observ.backfill\",\"aaac-observ.query\"],\n };\n }\n Object.assign(out, doc);\n process.stdout.write(JSON.stringify(out, null, 2) + \"\\n\");\n } else {\n // YAML output\n const yamlLines: string[] = [];\n yamlLines.push(\"# aaac-observ extract\");\n yamlLines.push(\"# source: embedded\");\n yamlLines.push(\"# type: cli-contracts/command-extract\");\n if (opts.includeMeta) {\n yamlLines.push(\"---\");\n yamlLines.push(\"source: embedded\");\n yamlLines.push(\"type: cli-contracts/command-extract\");\n yamlLines.push(\"extractedAt: \" + new Date().toISOString());\n yamlLines.push(\"spec_version: \" + (doc.cli_contracts ?? \"0.1.0\"));\n yamlLines.push(\"commands:\");\n for (const id of [\"aaac-observ.record\",\"aaac-observ.record-hook\",\"aaac-observ.backfill\",\"aaac-observ.query\"]) {\n yamlLines.push(\" - \" + id);\n }\n }\n yamlLines.push(\"---\");\n yamlLines.push(CONTRACT_YAML);\n process.stdout.write(yamlLines.join(\"\\n\") + \"\\n\");\n }\n } else {\n // Filter specific commands\n const filtered: Record<string, unknown> = {\n cli_contracts: doc.cli_contracts,\n info: doc.info,\n command_sets: {},\n };\n const fcs = filtered.command_sets as Record<string, Record<string, unknown>>;\n for (const [setId, cs] of Object.entries(doc.command_sets ?? {})) {\n const cmds = (cs as Record<string, unknown>).commands as Record<string, unknown> | undefined;\n if (!cmds) continue;\n const matched: Record<string, unknown> = {};\n for (const [cmdId, cmdDef] of Object.entries(cmds)) {\n const fullId = setId + \".\" + cmdId;\n if (cmdIds.some((id) => id === cmdId || id === fullId || cmdId.startsWith(id + \".\"))) {\n matched[cmdId] = cmdDef;\n }\n }\n if (Object.keys(matched).length > 0) {\n const setCopy = { ...(cs as Record<string, unknown>) };\n setCopy.commands = matched;\n fcs[setId] = setCopy;\n }\n }\n if (doc.components) filtered.components = doc.components;\n process.stdout.write(JSON.stringify(filtered, null, 2) + \"\\n\");\n }\n process.exit(0);\n });\n return program;\n}\n","// Auto-generated by cli-contracts. Do not edit.\n// Embedded contract for the extract subcommand.\n\nexport const CONTRACT_YAML: string = \"# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json\\ncli_contracts: 0.1.0\\n\\ninfo:\\n title: AaaC Observability CLI\\n version: 0.1.0\\n description: aaac-observ — external event registration and query for @aaac/observability\\n\\ncommand_sets:\\n aaac-observ:\\n summary: aaac-observ — register external events and query the observability store\\n executable: aaac-observ\\n commands:\\n record:\\n summary: Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\n options:\\n - name: event-type\\n aliases: [t]\\n schema: { type: string }\\n description: \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n - name: lifecycle\\n aliases: [l]\\n schema:\\n type: string\\n enum: [open, close, event, instant]\\n description: \\\"Event lifecycle phase: open | close | event | instant\\\"\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID (auto-generated if omitted)\\n - name: parent-span-id\\n schema: { type: string }\\n description: Parent span ID for hierarchy\\n - name: session-id\\n schema: { type: string }\\n description: Session ID to associate with the event\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for distributed tracing\\n - name: source\\n schema: { type: string }\\n description: \\\"Event source identifier (default: external)\\\"\\n - name: attr\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n - name: link\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Event registered successfully\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n eventId:\\n type: string\\n spanId:\\n type: string\\n '1':\\n description: Registration failed (validation error or write error)\\n stderr:\\n format: text\\n\\n record-hook:\\n summary: Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\\n arguments:\\n - name: hook-name\\n index: 0\\n required: true\\n schema: { type: string }\\n description: \\\"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\\\"\\n options:\\n - name: mapping-config\\n schema: { type: string }\\n description: \\\"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Hook recorded (or no-op when stdin/mapping unavailable) — always fail-open\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n recorded:\\n type: integer\\n fallback:\\n type: boolean\\n\\n backfill:\\n summary: Re-emit historical spans from SQLite to an OTLP backend\\n description: |\\n Reads closed/instant spans from the local SQLite database and emits them\\n via OtelEmitter to an OTLP-compatible backend.\\n\\n IMPORTANT — idempotency: The OTel SDK does not support specifying custom\\n spanIds, so each backfill invocation creates NEW spans in the backend.\\n Repeated invocations will produce DUPLICATE data. Use --from/--to to\\n control scope. See #125 §7 for full idempotency guidance.\\n options:\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n - name: endpoint\\n schema: { type: string }\\n description: \\\"OTLP HTTP endpoint URL (fallback: OTEL_EXPORTER_OTLP_ENDPOINT)\\\"\\n - name: service-name\\n schema: { type: string }\\n description: \\\"OTEL service name reported to backend (default: @aaac/observability)\\\"\\n - name: from\\n schema: { type: string }\\n description: Backfill spans starting at or after this ISO 8601 time\\n - name: to\\n schema: { type: string }\\n description: Backfill spans starting at or before this ISO 8601 time\\n - name: event-type\\n schema: { type: string }\\n description: \\\"Filter by event type (e.g. agent.session)\\\"\\n - name: dry-run\\n schema: { type: boolean }\\n description: Print span count without emitting to OTLP backend\\n exits:\\n '0':\\n description: Backfill completed\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n spansFound:\\n type: integer\\n spansEmitted:\\n type: integer\\n dryRun:\\n type: boolean\\n '1':\\n description: Backfill failed (missing endpoint, DB error, etc.)\\n stderr:\\n format: text\\n\\n query:\\n summary: Query the observability store via QueryAdapter\\n options:\\n - name: kind\\n aliases: [k]\\n schema:\\n type: string\\n enum: [trace, span, search, links]\\n description: \\\"Query kind: trace | span | search | links\\\"\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for trace queries (--kind trace)\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID for span/links queries (--kind span or --kind links)\\n - name: event-type\\n schema: { type: string }\\n description: Event type filter for search queries (--kind search)\\n - name: task-id\\n schema: { type: string }\\n description: Task ID filter for search queries (--kind search)\\n - name: from\\n schema: { type: string }\\n description: Start time in ISO 8601 format for search queries (--kind search)\\n - name: to\\n schema: { type: string }\\n description: End time in ISO 8601 format for search queries (--kind search)\\n - name: direction\\n aliases: [d]\\n schema:\\n type: string\\n enum: [forward, reverse, both]\\n description: \\\"Link traversal direction for links queries (default: both)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Query succeeded — results as JSON array\\n stdout:\\n format: json\\n schema:\\n type: object\\n '1':\\n description: Query failed\\n stderr:\\n format: text\\n\";\n\nexport const CONTRACT_JSON_STR: string = \"{\\n \\\"cli_contracts\\\": \\\"0.1.0\\\",\\n \\\"info\\\": {\\n \\\"title\\\": \\\"AaaC Observability CLI\\\",\\n \\\"version\\\": \\\"0.1.0\\\",\\n \\\"description\\\": \\\"aaac-observ — external event registration and query for @aaac/observability\\\"\\n },\\n \\\"command_sets\\\": {\\n \\\"aaac-observ\\\": {\\n \\\"summary\\\": \\\"aaac-observ — register external events and query the observability store\\\",\\n \\\"executable\\\": \\\"aaac-observ\\\",\\n \\\"commands\\\": {\\n \\\"record\\\": {\\n \\\"summary\\\": \\\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"aliases\\\": [\\n \\\"t\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n },\\n {\\n \\\"name\\\": \\\"lifecycle\\\",\\n \\\"aliases\\\": [\\n \\\"l\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"open\\\",\\n \\\"close\\\",\\n \\\"event\\\",\\n \\\"instant\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Event lifecycle phase: open | close | event | instant\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID (auto-generated if omitted)\\\"\\n },\\n {\\n \\\"name\\\": \\\"parent-span-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Parent span ID for hierarchy\\\"\\n },\\n {\\n \\\"name\\\": \\\"session-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Session ID to associate with the event\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for distributed tracing\\\"\\n },\\n {\\n \\\"name\\\": \\\"source\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event source identifier (default: external)\\\"\\n },\\n {\\n \\\"name\\\": \\\"attr\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n },\\n {\\n \\\"name\\\": \\\"link\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Event registered successfully\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"eventId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"spanId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n }\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Registration failed (validation error or write error)\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"record-hook\\\": {\\n \\\"summary\\\": \\\"Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\\\",\\n \\\"arguments\\\": [\\n {\\n \\\"name\\\": \\\"hook-name\\\",\\n \\\"index\\\": 0,\\n \\\"required\\\": true,\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\\\"\\n }\\n ],\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"mapping-config\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Hook recorded (or no-op when stdin/mapping unavailable) — always fail-open\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"recorded\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"fallback\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n },\\n \\\"backfill\\\": {\\n \\\"summary\\\": \\\"Re-emit historical spans from SQLite to an OTLP backend\\\",\\n \\\"description\\\": \\\"Reads closed/instant spans from the local SQLite database and emits them\\\\nvia OtelEmitter to an OTLP-compatible backend.\\\\n\\\\nIMPORTANT — idempotency: The OTel SDK does not support specifying custom\\\\nspanIds, so each backfill invocation creates NEW spans in the backend.\\\\nRepeated invocations will produce DUPLICATE data. Use --from/--to to\\\\ncontrol scope. See #125 §7 for full idempotency guidance.\\\\n\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n },\\n {\\n \\\"name\\\": \\\"endpoint\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"OTLP HTTP endpoint URL (fallback: OTEL_EXPORTER_OTLP_ENDPOINT)\\\"\\n },\\n {\\n \\\"name\\\": \\\"service-name\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"OTEL service name reported to backend (default: @aaac/observability)\\\"\\n },\\n {\\n \\\"name\\\": \\\"from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Backfill spans starting at or after this ISO 8601 time\\\"\\n },\\n {\\n \\\"name\\\": \\\"to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Backfill spans starting at or before this ISO 8601 time\\\"\\n },\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Filter by event type (e.g. agent.session)\\\"\\n },\\n {\\n \\\"name\\\": \\\"dry-run\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n },\\n \\\"description\\\": \\\"Print span count without emitting to OTLP backend\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Backfill completed\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"spansFound\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"spansEmitted\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"dryRun\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n }\\n }\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Backfill failed (missing endpoint, DB error, etc.)\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"query\\\": {\\n \\\"summary\\\": \\\"Query the observability store via QueryAdapter\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"kind\\\",\\n \\\"aliases\\\": [\\n \\\"k\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"trace\\\",\\n \\\"span\\\",\\n \\\"search\\\",\\n \\\"links\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Query kind: trace | span | search | links\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for trace queries (--kind trace)\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID for span/links queries (--kind span or --kind links)\\\"\\n },\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"task-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Task ID filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Start time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"End time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"direction\\\",\\n \\\"aliases\\\": [\\n \\\"d\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"forward\\\",\\n \\\"reverse\\\",\\n \\\"both\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Link traversal direction for links queries (default: both)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Query succeeded — results as JSON array\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\"\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Query failed\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n}\";\n","/**\n * record handler — thin entrypoint for EventCollector.registerExternalEvent.\n *\n * Wires the CLI options to the write pipeline:\n * options → EventCollector.registerExternalEvent\n * → Normalizer → Correlator → Enricher → SqliteSink + OtelEmitter\n *\n * architecture.md §9 / OBSERVABILITY_TASKS.md Phase 3-2\n * shift-left-event-mapping.md: aaac-observ record is the thin entrypoint\n */\nimport { createPipeline, generateId, DEFAULT_DB_PATH } from \"../index.js\";\nimport type { CanonicalLink, AttrValue, Lifecycle } from \"../types/canonical-event.js\";\n\n// ── Parsers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse comma-separated key=value attribute string into a Record.\n * Also accepts a JSON object string.\n *\n * Examples:\n * \"git.commit=abc123,git.branch=main\"\n * '{\"git.commit\":\"abc123\",\"git.branch\":\"main\"}'\n */\nexport function parseAttr(attrStr: string | undefined): Record<string, AttrValue> {\n if (!attrStr) return {};\n const trimmed = attrStr.trim();\n if (trimmed.startsWith(\"{\")) {\n try {\n return JSON.parse(trimmed) as Record<string, AttrValue>;\n } catch {\n // fall through to key=value parsing\n }\n }\n const result: Record<string, AttrValue> = {};\n for (const pair of trimmed.split(\",\")) {\n const eqIdx = pair.indexOf(\"=\");\n if (eqIdx > 0) {\n const key = pair.slice(0, eqIdx).trim();\n const val = pair.slice(eqIdx + 1).trim();\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * Parse comma-separated link string into a CanonicalLink array.\n * Format per item: targetSpanId:linkType[:targetTraceId]\n *\n * Example: \"spanId123:materializes_as_commit,spanId456:contains_change:traceId789\"\n */\nexport function parseLinks(linkStr: string | undefined): CanonicalLink[] {\n if (!linkStr) return [];\n const links: CanonicalLink[] = [];\n for (const item of linkStr.split(\",\")) {\n const parts = item.trim().split(\":\");\n if (parts.length >= 2) {\n const targetSpanId = parts[0].trim();\n const linkType = parts[1].trim();\n const targetTraceId = parts[2]?.trim() || undefined;\n if (targetSpanId && linkType) {\n links.push({ targetSpanId, linkType, targetTraceId });\n }\n }\n }\n return links;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n * NOTE: attr and link are strings (last value from --option) not arrays,\n * because the generated Commander program does not use a collector.\n * Comma-separate multiple values: --attr \"key1=val1,key2=val2\"\n */\nexport interface RecordHandlerOptions {\n eventType?: string;\n lifecycle?: string;\n spanId?: string;\n parentSpanId?: string;\n sessionId?: string;\n traceId?: string;\n source?: string;\n attr?: string;\n link?: string;\n db?: string;\n}\n\n/**\n * Handle `aaac-observ record`.\n *\n * Required options: --event-type, --lifecycle\n * On success: writes the event through the full pipeline and prints\n * { eventId, spanId } to stdout.\n * On error: writes to stderr and exits with code 1.\n */\nexport async function handleRecord(\n options: RecordHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n // ── Validate required options ──────────────────────────────────────────────\n if (!options.eventType) {\n process.stderr.write(\"Error: --event-type (-t) is required\\n\");\n process.exit(1);\n }\n const validLifecycles: readonly string[] = [\"open\", \"close\", \"event\", \"instant\"];\n if (!options.lifecycle) {\n process.stderr.write(\n `Error: --lifecycle (-l) is required (${validLifecycles.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validLifecycles.includes(options.lifecycle)) {\n process.stderr.write(\n `Error: --lifecycle must be one of: ${validLifecycles.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n // ── Build event payload ────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const spanId = options.spanId ?? generateId();\n const attributes = parseAttr(options.attr);\n const links = parseLinks(options.link);\n\n // Inject well-known scalars into attributes so the Normalizer picks them up\n if (options.sessionId) {\n attributes[\"session_id\"] = options.sessionId;\n }\n // trace_id stored as attribute for user reference; canonical traceId is\n // managed by Normalizer/Correlator (propagated via parentSpanId chain)\n if (options.traceId) {\n attributes[\"trace_id\"] = options.traceId;\n }\n\n // ── Build & execute pipeline ───────────────────────────────────────────────\n let capturedEventId: string | undefined;\n\n const { collector, sink } = createPipeline({\n dbPath,\n // Use afterCorrelate to capture the event id before it is written\n afterCorrelate: (event) => {\n capturedEventId = event.id;\n return event;\n },\n // Enable the default enrichment rules (R1–R5). This is the path the git\n // post-commit hook uses to record promotion.commit (close) with\n // committed_files; R5 (cross-axis) must run here to add the\n // materializes_as_commit link to task spans whose modified files intersect\n // the commit. R3/R4 accumulation persists in the SQLite-backed cache of the\n // same db file, so R5 sees it across the process boundary (#143 / #140).\n // Enricher is fail-open, so a rule error never blocks recording.\n });\n\n try {\n collector.registerExternalEvent({\n source: options.source ?? \"external\",\n eventType: options.eventType,\n lifecycle: options.lifecycle as Lifecycle,\n spanId,\n parentSpanId: options.parentSpanId,\n attributes,\n links,\n });\n } catch (err) {\n process.stderr.write(\n `Error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n sink.close();\n process.exit(1);\n }\n\n sink.close();\n\n const result = { eventId: capturedEventId, spanId };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * record-hook handler — stdin-aware Cursor/git hook recorder.\n *\n * This is the runtime entrypoint for `.cursor/hooks/observ-record.sh`. Unlike the\n * thin `record` command (scalar flags only), `record-hook`:\n *\n * 1. parses the hook JSON payload from stdin (conversation_id + hook-specific fields)\n * 2. loads `.agent-logs/config/event-mapping.json` and resolves the matching\n * mapping rule into 3-axis spans + cross-axis links (evaluateMapping)\n * 3. injects session_id = conversation_id on every emitted event (correlation key\n * with @aaac/runtime in-process events)\n * 4. emits human-interaction events (human.instruction / quality_gate.result /\n * promotion.pr) for the relevant hooks\n * 5. manages the short-lived git context file `.agent-logs/.observ-context.json`\n * (write on beforeShellExecution for git commit/push/merge, delete on\n * afterShellExecution) so git hooks can correlate (read side: #115)\n *\n * It is strictly fail-open: any parse/IO/config error degrades to the generic\n * fallback recording (or a no-op) and never throws — recording must NEVER block\n * the underlying hook operation.\n *\n * Issue #114 / shift-left-event-mapping.md §6 (option B).\n */\nimport { mkdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n createPipeline,\n generateId,\n DEFAULT_DB_PATH,\n loadEventMappingConfig,\n evaluateMapping,\n emitHumanInstruction,\n emitQualityGateResult,\n emitPromotionPr,\n} from \"../index.js\";\nimport type { EventCollector } from \"../collector/event-collector.js\";\nimport type { AttrValue, CanonicalLink } from \"../types/canonical-event.js\";\nimport type { EventMappingConfig, EventMappingRule } from \"../event-mapping/types.js\";\n\n// ── Constants ──────────────────────────────────────────────────────────────────\n\n/** git subcommands that warrant propagating session context to git hooks. */\nconst GIT_CONTEXT_COMMAND_RE = /\\bgit\\s+(?:commit|push|merge)\\b/;\n\n/** Hook names that map to git.* event types in the generic fallback. */\nconst GIT_HOOK_NAMES = new Set([\n \"pre-commit\",\n \"post-commit\",\n \"pre-push\",\n \"post-merge\",\n \"post-checkout\",\n]);\n\n// ── Options ──────────────────────────────────────────────────────────────────\n\nexport interface RecordHookOptions {\n mappingConfig?: string;\n db?: string;\n}\n\n/** Parsed result of running the hook recorder (also written to stdout as JSON). */\nexport interface RecordHookResult {\n /** Number of events emitted into the pipeline. */\n recorded: number;\n /** True when no mapping rule matched and the generic fallback was used. */\n fallback: boolean;\n}\n\n// ── Path helpers ───────────────────────────────────────────────────────────────\n\n/** Default event-mapping config path, derived from the db directory. */\nexport function defaultMappingConfigPath(dbPath: string): string {\n return join(dirname(dbPath), \"config\", \"event-mapping.json\");\n}\n\n/** Short-lived git context file path, derived from the db directory. */\nexport function contextFilePath(dbPath: string): string {\n return join(dirname(dbPath), \".observ-context.json\");\n}\n\n// ── stdin parsing ────────────────────────────────────────────────────────────\n\n/**\n * Read the entire stdin stream as a string.\n * Resolves \"\" when stdin is a TTY (no payload) or on any error — the git-hook\n * path frequently provides no stdin, which must be handled gracefully (AC-1).\n */\nexport async function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n return new Promise<string>((resolve) => {\n let data = \"\";\n let settled = false;\n const finish = (): void => {\n if (settled) return;\n settled = true;\n resolve(data);\n };\n try {\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", finish);\n process.stdin.on(\"error\", finish);\n process.stdin.on(\"close\", finish);\n // Safety net: never hang the hook if stdin is left open.\n const t = setTimeout(finish, 1000);\n if (typeof t.unref === \"function\") t.unref();\n } catch {\n finish();\n }\n });\n}\n\n/** Parse a stdin payload into an object; returns {} for empty/invalid JSON. */\nexport function parseHookInput(raw: string): Record<string, unknown> {\n const trimmed = raw.trim();\n if (!trimmed) return {};\n try {\n const parsed = JSON.parse(trimmed) as unknown;\n return parsed !== null && typeof parsed === \"object\"\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n// ── Field coercion ───────────────────────────────────────────────────────────\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const n = Number(value);\n if (Number.isFinite(n)) return n;\n }\n return undefined;\n}\n\n// ── Emission ─────────────────────────────────────────────────────────────────\n\n/**\n * Emit the 3-axis spans + cross-axis links resolved from a mapping rule.\n * session_id (and parent_conversation_id on subagentStart) are injected on every\n * span so all hook-derived events share the correlation key.\n */\nfunction emitMappedSpans(\n collector: EventCollector,\n hook: string,\n rule: EventMappingRule,\n hookInput: Record<string, unknown>,\n sessionId: string,\n): number {\n const { spans, links } = evaluateMapping(rule, hookInput);\n\n // Group resolved links by their source spanId so they can be attached to the\n // emitting (source) event as CanonicalLink entries.\n const linksBySpan = new Map<string, CanonicalLink[]>();\n for (const link of links) {\n const arr = linksBySpan.get(link.fromSpanId) ?? [];\n arr.push({ targetSpanId: link.toSpanId, linkType: link.linkType });\n linksBySpan.set(link.fromSpanId, arr);\n }\n\n const parentConversationId =\n hook === \"subagentStart\" ? asString(hookInput.parent_conversation_id) : undefined;\n\n for (const span of spans) {\n const attributes: Record<string, AttrValue> = { ...span.attributes };\n if (sessionId) attributes[\"session_id\"] = sessionId;\n if (parentConversationId) {\n attributes[\"parent_conversation_id\"] = parentConversationId;\n }\n collector.emit({\n source: \"cursor-hook\",\n eventType: span.eventType,\n lifecycle: span.lifecycle,\n spanId: span.spanId,\n parentSpanId: span.parentSpanId,\n attributes,\n links: linksBySpan.get(span.spanId) ?? [],\n });\n }\n\n return spans.length;\n}\n\n/**\n * Emit a single generic instant event (current pre-#114 behaviour) when no\n * mapping rule matches — preserves fail-open recording (AC-3).\n */\nfunction emitGenericFallback(\n collector: EventCollector,\n hook: string,\n sessionId: string,\n): number {\n const eventType = (GIT_HOOK_NAMES.has(hook) ? \"git.\" : \"cursor.\") + hook;\n const attributes: Record<string, AttrValue> = { \"hook.name\": hook };\n if (sessionId) attributes[\"session_id\"] = sessionId;\n collector.emit({\n source: \"cursor-hook\",\n eventType,\n lifecycle: \"instant\",\n attributes,\n });\n return 1;\n}\n\n/** Emit human-interaction events for the relevant hooks (AC-5). */\nfunction emitHumanEvents(\n collector: EventCollector,\n hook: string,\n hookInput: Record<string, unknown>,\n sessionId: string,\n): number {\n let count = 0;\n\n if (hook === \"beforeSubmitPrompt\") {\n const prompt = asString(hookInput.prompt);\n if (prompt !== undefined) {\n const attachments = Array.isArray(hookInput.attachments)\n ? (hookInput.attachments as unknown[])\n : [];\n emitHumanInstruction(collector, { sessionId, prompt, attachments });\n count += 1;\n }\n } else if (hook === \"afterShellExecution\") {\n const command = asString(hookInput.command);\n if (command !== undefined) {\n const exitCode = asNumber(hookInput.exit_code) ?? asNumber(hookInput.exitCode) ?? 0;\n const durationMs = asNumber(hookInput.duration);\n if (emitQualityGateResult(collector, { sessionId, command, exitCode, durationMs })) {\n count += 1;\n }\n const output = asString(hookInput.output) ?? \"\";\n if (emitPromotionPr(collector, { sessionId, command, output })) {\n count += 1;\n }\n }\n }\n\n return count;\n}\n\n// ── Context file lifecycle ──────────────────────────────────────────────────\n\n/**\n * Manage the short-lived git context file (AC-4).\n *\n * beforeShellExecution + git commit/push/merge → atomic write\n * afterShellExecution + git commit/push/merge → delete (fail-open)\n *\n * Atomic write uses write-to-temp + rename so a git hook never observes a\n * partially written file. Non-git commands never write the file.\n */\nasync function manageContextFile(\n hook: string,\n hookInput: Record<string, unknown>,\n sessionId: string,\n dbPath: string,\n): Promise<void> {\n const command = asString(hookInput.command) ?? \"\";\n const isGitContextCommand = GIT_CONTEXT_COMMAND_RE.test(command);\n const file = contextFilePath(dbPath);\n\n if (hook === \"beforeShellExecution\" && isGitContextCommand) {\n await mkdir(dirname(file), { recursive: true });\n const payload =\n JSON.stringify({\n session_id: sessionId || null,\n nonce: generateId(),\n created_at_ms: Date.now(),\n }) + \"\\n\";\n const tmp = `${file}.${process.pid}.${generateId()}.tmp`;\n await writeFile(tmp, payload, \"utf8\");\n await rename(tmp, file);\n } else if (hook === \"afterShellExecution\" && isGitContextCommand) {\n await unlink(file).catch(() => {\n // rm -f semantics: missing file is not an error.\n });\n }\n}\n\n// ── Core (testable) ────────────────────────────────────────────────────────────\n\n/**\n * Run the hook recorder against an already-parsed hook input.\n *\n * Separated from {@link handleRecordHook} (which reads stdin) so it can be unit\n * tested without mocking process.stdin.\n */\nexport async function runRecordHook(args: {\n hook: string;\n hookInput: Record<string, unknown>;\n dbPath: string;\n mappingConfigPath: string;\n}): Promise<RecordHookResult> {\n const { hook, hookInput, dbPath, mappingConfigPath } = args;\n const sessionId = asString(hookInput.conversation_id) ?? \"\";\n\n // Context-file lifecycle is independent of recording and must not block it.\n await manageContextFile(hook, hookInput, sessionId, dbPath).catch(() => {\n // fail-open\n });\n\n // Load mapping config; absence/parse-error → generic fallback (AC-3).\n let config: EventMappingConfig | undefined;\n try {\n config = await loadEventMappingConfig(mappingConfigPath);\n } catch {\n config = undefined;\n }\n const rule = config?.mappings?.[hook];\n\n let recorded = 0;\n let fallback = false;\n\n // Enable the default enrichment rules (R1–R5) so hook-derived spans/links are\n // enriched on the same SQLite-backed cache the runtime populates, allowing\n // cross-axis (R5) linking across the process boundary (#143 / #140). The\n // Enricher is fail-open, so a rule error never blocks the underlying hook.\n const { collector, sink } = createPipeline({ dbPath });\n try {\n if (rule) {\n recorded += emitMappedSpans(collector, hook, rule, hookInput, sessionId);\n } else {\n recorded += emitGenericFallback(collector, hook, sessionId);\n fallback = true;\n }\n recorded += emitHumanEvents(collector, hook, hookInput, sessionId);\n } finally {\n sink.close();\n }\n\n return { recorded, fallback };\n}\n\n// ── Handler ─────────────────────────────────────────────────────────────────\n\n/**\n * Handle `aaac-observ record-hook <hook-name>`.\n *\n * Reads the hook JSON from stdin, records the resolved events, and prints\n * `{ recorded, fallback }` to stdout. Strictly fail-open: never throws and\n * always returns; the calling shell wrapper additionally guarantees exit 0.\n */\nexport async function handleRecordHook(\n hookName: string | undefined,\n options: RecordHookOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const hook = hookName ?? \"unknown\";\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const mappingConfigPath = options.mappingConfig ?? defaultMappingConfigPath(dbPath);\n\n let result: RecordHookResult = { recorded: 0, fallback: false };\n try {\n const hookInput = parseHookInput(await readStdin());\n result = await runRecordHook({ hook, hookInput, dbPath, mappingConfigPath });\n } catch (err) {\n // Recorder is fail-open: report to stderr but never block the operation.\n process.stderr.write(\n `record-hook: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * query handler — read path via QueryAdapter.\n *\n * Routes --kind to the appropriate SqliteQueryAdapter method and emits\n * results as JSON to stdout.\n *\n * Supported kinds:\n * trace → QueryAdapter.getTrace(--trace-id)\n * span → QueryAdapter.getSpan(--span-id) [+ QueryAdapter.getSpanEvents]\n * search → QueryAdapter.querySpans({ eventType, taskId, from, to })\n * links → QueryAdapter.getLinks(--span-id, --direction)\n *\n * architecture.md §12 / OBSERVABILITY_TASKS.md Phase 3-2 (record CLI + query)\n */\nimport { SqliteQueryAdapter, DEFAULT_DB_PATH } from \"../index.js\";\nimport { isoToUnixNano } from \"../types/ids.js\";\nimport type { LinkDirection } from \"../query/models.js\";\n\n// ── Handler ───────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n */\nexport interface QueryHandlerOptions {\n kind?: string;\n traceId?: string;\n spanId?: string;\n eventType?: string;\n taskId?: string;\n from?: string;\n to?: string;\n direction?: string;\n db?: string;\n}\n\n/** JSON replacer that serialises BigInt as a string. */\nfunction bigIntReplacer(_key: string, value: unknown): unknown {\n return typeof value === \"bigint\" ? value.toString() : value;\n}\n\n/**\n * Handle `aaac-observ query`.\n *\n * Required option: --kind (trace|span|search|links)\n * On success: writes a JSON array/object to stdout.\n * On error: writes to stderr and exits with code 1.\n *\n * DESIGN NOTE: all required-arg validation happens BEFORE the adapter is\n * created, so that when process.exit is mocked to throw in tests there is no\n * risk of a double-close on the database connection.\n */\nexport async function handleQuery(\n options: QueryHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const validKinds = [\"trace\", \"span\", \"search\", \"links\"] as const;\n type QueryKind = (typeof validKinds)[number];\n\n // ── Validate options (before opening the adapter) ─────────────────────────\n if (!options.kind) {\n process.stderr.write(\n `Error: --kind (-k) is required (${validKinds.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validKinds.includes(options.kind as QueryKind)) {\n process.stderr.write(\n `Error: --kind must be one of: ${validKinds.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n const kind = options.kind as QueryKind;\n\n // Validate sub-command required args (before adapter creation)\n if (kind === \"trace\" && !options.traceId) {\n process.stderr.write(\"Error: --trace-id is required for --kind trace\\n\");\n process.exit(1);\n }\n if (kind === \"span\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind span\\n\");\n process.exit(1);\n }\n if (kind === \"links\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind links\\n\");\n process.exit(1);\n }\n\n // ── Open adapter ──────────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const adapter = new SqliteQueryAdapter(dbPath);\n\n try {\n let result: unknown;\n\n switch (kind) {\n // ── trace: all spans in a trace ─────────────────────────────────────\n case \"trace\": {\n // options.traceId is guaranteed by pre-validation above\n result = adapter.getTrace(options.traceId!);\n break;\n }\n\n // ── span: single span + its events ──────────────────────────────────\n case \"span\": {\n // options.spanId is guaranteed by pre-validation above\n const span = adapter.getSpan(options.spanId!);\n const events = adapter.getSpanEvents(options.spanId!);\n result = span !== undefined ? { span, events } : null;\n break;\n }\n\n // ── search: spans matching event-type / task-id / time range ─────────\n case \"search\": {\n const filter: Parameters<typeof adapter.querySpans>[0] = {};\n if (options.eventType) filter.eventType = options.eventType;\n if (options.taskId) filter.taskId = options.taskId;\n if (options.from) filter.fromTimeUnixNano = isoToUnixNano(options.from);\n if (options.to) filter.toTimeUnixNano = isoToUnixNano(options.to);\n result = adapter.querySpans(filter);\n break;\n }\n\n // ── links: link traversal ─────────────────────────────────────────────\n case \"links\": {\n const validDirections = [\"forward\", \"reverse\", \"both\"] as const;\n type Dir = (typeof validDirections)[number];\n const dir: Dir =\n options.direction !== undefined &&\n validDirections.includes(options.direction as Dir)\n ? (options.direction as Dir)\n : \"both\";\n result = adapter.getLinks(options.spanId!, dir as LinkDirection);\n break;\n }\n }\n\n process.stdout.write(JSON.stringify(result, bigIntReplacer) + \"\\n\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n\n // Graceful handling: an un-initialised (fresh empty) database has no\n // tables yet. Return an empty / null result rather than an error so that\n // `aaac-observ query` on a machine with no recorded events behaves\n // predictably.\n if (msg.includes(\"no such table\") || msg.includes(\"no such column\")) {\n const emptyResult = kind === \"span\" ? null : [];\n process.stdout.write(JSON.stringify(emptyResult, bigIntReplacer) + \"\\n\");\n adapter.close();\n return;\n }\n\n process.stderr.write(`Error: ${msg}\\n`);\n adapter.close();\n process.exit(1);\n }\n\n adapter.close();\n}\n","/**\n * backfill handler — re-emit historical spans from SQLite to an OTLP backend.\n *\n * Reads closed/instant spans from the local SQLite database and emits them\n * via OtelEmitter to an OTLP-compatible backend (e.g. OTel Collector →\n * ClickHouse).\n *\n * ## Idempotency design (important)\n *\n * The OTel SDK does NOT support specifying a custom spanId when creating spans.\n * Therefore, each backfill invocation will create NEW spans in the OTLP backend\n * with different spanIds than the originals recorded in SQLite. Repeated\n * invocations will produce DUPLICATE data in the backend (e.g. ClickHouse).\n *\n * Recommended idempotency strategies:\n * 1. Run backfill only once per SQLite DB / time range.\n * 2. Use --from / --to to backfill specific time windows; track which windows\n * have already been backfilled externally.\n * 3. Truncate the ClickHouse target table before re-running (destructive; safe\n * for dev environments).\n * 4. Use ClickHouse ReplacingMergeTree / FINAL for approximate deduplication\n * (requires schema changes; not the default otel_traces schema).\n *\n * The --dry-run flag prints span metadata without emitting; use it to verify\n * scope before committing to a real backfill.\n *\n * architecture.md §11 / docs/observability-setup.md / #125 §7\n */\nimport { SqliteQueryAdapter, OtelEmitter, DEFAULT_DB_PATH, isoToUnixNano } from \"../index.js\";\nimport { mapAllEventsToSpans } from \"../otel/span-mapper.js\";\nimport type { MappedSpan, MappedSpanLink } from \"../otel/span-mapper.js\";\nimport type { SpanRecord, LinkRecord } from \"../query/models.js\";\n\n// ── Handler options ───────────────────────────────────────────────────────────\n\nexport interface BackfillHandlerOptions {\n /** SQLite database path. Defaults to DEFAULT_DB_PATH. */\n db?: string;\n /**\n * OTLP HTTP endpoint URL.\n * Fallback: OTEL_EXPORTER_OTLP_ENDPOINT env var.\n * Required unless --dry-run is set.\n */\n endpoint?: string;\n /** OTEL service name reported to the backend. Default: '@aaac/observability'. */\n serviceName?: string;\n /** Filter: only backfill spans starting at or after this ISO 8601 time. */\n from?: string;\n /** Filter: only backfill spans starting at or before this ISO 8601 time. */\n to?: string;\n /** Filter: only backfill spans of this event type (e.g. 'agent.session'). */\n eventType?: string;\n /**\n * Print span metadata to stdout without actually emitting to the OTLP backend.\n * Useful for previewing scope before committing to a real backfill.\n */\n dryRun?: boolean;\n /**\n * Maximum milliseconds to wait for the OTLP flush before the CLI exits.\n * Default: 2000 ms (preserves existing vitest compatibility — unreachable\n * endpoints resolve immediately via connection-refused, so tests are unaffected\n * by the default value).\n *\n * For large backfills (> 5 000 spans) the BatchSpanProcessor needs significantly\n * more time. Recommended production value: 30 000 ms.\n *\n * Rule of thumb: allow ≥ 400 ms per 512-span batch\n * (i.e. ceil(spansFound / 512) * 400 ms, rounded up generously).\n */\n flushTimeoutMs?: number;\n}\n\n// ── MappedSpan reconstruction ─────────────────────────────────────────────────\n\n/**\n * Reconstruct a MappedSpan from a stored SpanRecord + its forward links.\n *\n * Backfill works at the \"span\" level (not the raw event level) because\n * SqliteSink materialises open+close into the `spans` table at write time.\n * Only closed or instant spans carry meaningful duration information.\n */\nfunction spanRecordToMappedSpan(\n span: SpanRecord,\n links: LinkRecord[],\n): MappedSpan {\n const start = span.startTime ?? 0n;\n const end = span.endTime ?? start;\n\n // Reconstruct MappedSpanLinks from the stored canonical_links rows.\n const mappedLinks: MappedSpanLink[] = links.map((l) => ({\n targetSpanId: l.targetSpanId,\n targetTraceId: l.targetTraceId ?? undefined,\n attributes: {\n link_type: l.linkType,\n ...(l.attributes ?? {}),\n },\n }));\n\n return {\n spanId: span.spanId,\n traceId: span.traceId ?? \"\",\n parentSpanId: span.parentSpanId ?? undefined,\n name: span.eventType,\n startTimeUnixNano: start,\n endTimeUnixNano: end,\n attributes: span.attributes,\n spanEvents: [], // span events are not stored in the spans table\n links: mappedLinks,\n };\n}\n\n// ── Handler ───────────────────────────────────────────────────────────────────\n\n/**\n * Handle `aaac-observ backfill`.\n *\n * Emits historical spans from the SQLite store to an OTLP backend.\n * Prints a JSON summary to stdout:\n * { spansFound: number, spansEmitted: number, dryRun: boolean }\n *\n * On error: writes to stderr and exits with code 1.\n *\n * IMPORTANT — idempotency: Re-running backfill without --from/--to restriction\n * will re-emit all matching spans, creating DUPLICATES in the OTLP backend.\n * See module-level comment for mitigation strategies.\n */\nexport async function handleBackfill(\n options: BackfillHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const dryRun = options.dryRun ?? false;\n\n // ── Validate endpoint (not needed for dry-run) ────────────────────────────\n const endpoint = options.endpoint ?? process.env[\"OTEL_EXPORTER_OTLP_ENDPOINT\"];\n if (!dryRun && !endpoint) {\n process.stderr.write(\n \"Error: --endpoint is required (or set OTEL_EXPORTER_OTLP_ENDPOINT) unless --dry-run\\n\",\n );\n process.exit(1);\n }\n\n // ── Validate and parse time filters ───────────────────────────────────────\n // isoToUnixNano() silently falls back to currentTimeUnixNano() for invalid\n // ISO strings (shared utility — behaviour unchanged for other callers).\n // Backfill must validate explicitly so a typo in --from/--to causes exit 1\n // rather than a silent wrong-boundary backfill (F1 / issue #125).\n let fromNano: bigint | undefined;\n let toNano: bigint | undefined;\n if (options.from) {\n if (Number.isNaN(Date.parse(options.from))) {\n process.stderr.write(\n `Error: invalid --from value \"${options.from}\" — expected ISO 8601\\n`,\n );\n process.exit(1);\n }\n fromNano = isoToUnixNano(options.from);\n }\n if (options.to) {\n if (Number.isNaN(Date.parse(options.to))) {\n process.stderr.write(\n `Error: invalid --to value \"${options.to}\" — expected ISO 8601\\n`,\n );\n process.exit(1);\n }\n toNano = isoToUnixNano(options.to);\n }\n\n // ── Query spans from SQLite ────────────────────────────────────────────────\n const adapter = new SqliteQueryAdapter(dbPath);\n let spans: SpanRecord[];\n try {\n spans = adapter.querySpans({\n ...(options.eventType ? { eventType: options.eventType } : {}),\n ...(fromNano !== undefined ? { fromTimeUnixNano: fromNano } : {}),\n ...(toNano !== undefined ? { toTimeUnixNano: toNano } : {}),\n });\n // Only backfill spans that have completed (closed or instant).\n // Open spans are missing their end_time and would emit with zero duration.\n spans = spans.filter((s) => s.status === \"closed\" || s.status === \"instant\");\n } catch (err) {\n process.stderr.write(\n `Error: failed to query SQLite — ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n adapter.close();\n process.exit(1);\n return; // unreachable, but satisfies TS control-flow\n }\n\n const spansFound = spans.length;\n\n if (dryRun) {\n adapter.close();\n const result = { spansFound, spansEmitted: 0, dryRun: true };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n return;\n }\n\n // ── Reconstruct MappedSpans with their links ──────────────────────────────\n const mappedSpans: MappedSpan[] = [];\n for (const span of spans) {\n let links: LinkRecord[] = [];\n try {\n links = adapter.getLinks(span.spanId, \"forward\");\n } catch {\n // fail-open: missing links are non-fatal for backfill\n }\n mappedSpans.push(spanRecordToMappedSpan(span, links));\n }\n\n adapter.close();\n\n // ── Large-backfill warning ────────────────────────────────────────────────\n // Warn before emitting when the operator-supplied (or default) flush timeout\n // is likely too short for the span count. Rule of thumb: each 512-span batch\n // takes ~400 ms → 1 000 spans ≈ 2 batches ≈ 800 ms, well under 2 s, but\n // 13 000 spans ≈ 26 batches ≈ 10 400 ms, which exceeds the 2 s default.\n // Emitting the warning here (before the emit loop) lets the operator ^C and\n // re-run with --flush-timeout-ms 30000 before any data is sent.\n const effectiveFlushMs = options.flushTimeoutMs ?? 2000;\n if (spansFound > 1000 && effectiveFlushMs < 10000) {\n process.stderr.write(\n `Warning: ${spansFound} spans queued but --flush-timeout-ms is ` +\n `${effectiveFlushMs} ms (< 10 000 ms). Large batches may not fully ` +\n `flush before the deadline. Recommended: --flush-timeout-ms 30000\\n`,\n );\n }\n\n // ── Emit via OtelEmitter ──────────────────────────────────────────────────\n const emitter = new OtelEmitter({\n endpoint,\n serviceName: options.serviceName,\n });\n\n let spansEmitted = 0;\n for (const mapped of mappedSpans) {\n try {\n emitter.emit(mapped);\n spansEmitted++;\n } catch {\n // fail-open: individual span failures are non-fatal\n }\n }\n\n // Flush pending batch before reporting success.\n // Race against effectiveFlushMs so that an unreachable OTLP endpoint\n // never causes the CLI to hang indefinitely. OtelEmitter.shutdown() already\n // swallows its own errors, so the race is purely a time-bound escape hatch.\n // Default 2 000 ms preserves vitest idempotency-test compatibility (unreachable\n // endpoints resolve immediately via connection-refused; the timeout is never hit\n // in tests). For production use with > 5 000 spans pass --flush-timeout-ms 30000.\n await Promise.race([\n emitter.shutdown(),\n new Promise<void>((resolve) => setTimeout(resolve, effectiveFlushMs)),\n ]);\n\n const result = { spansFound, spansEmitted, dryRun: false };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * CLI command handlers for aaac-observ.\n *\n * Implements the CommandHandlers interface generated by cli-contracts and\n * delegates to the domain-specific handler modules.\n *\n * Usage (in index.ts):\n * import { handlers } from './handlers.js';\n * import { createProgram } from '../generated/program.js';\n * const program = createProgram(handlers, version);\n * await program.parseAsync();\n */\nimport type { CommandHandlers } from \"../generated/program.js\";\nimport { handleRecord } from \"./record.js\";\nimport { handleRecordHook } from \"./record-hook.js\";\nimport { handleQuery } from \"./query.js\";\nimport { handleBackfill } from \"./backfill.js\";\n\nexport const handlers: CommandHandlers = {\n record: handleRecord,\n recordHook: handleRecordHook,\n query: handleQuery,\n backfill: handleBackfill,\n};\n"],"mappings":"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","names":["dirname","handlers","program","resolve","result","resolve","dirname"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/generated/program.ts","../../src/generated/contract.ts","../../src/cli/record.ts","../../src/cli/query.ts","../../src/cli/backfill.ts","../../src/cli/ingest-vendor-otel.ts","../../src/cli/report.ts","../../src/cli/handlers.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * aaac-observ CLI entry point.\n *\n * Reads the version from package.json, creates the generated Commander\n * program with hand-written handlers, and executes it.\n */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createProgram } from \"../generated/program.js\";\nimport { handlers } from \"./handlers.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(\n readFileSync(resolve(__dirname, \"../../package.json\"), \"utf8\"),\n) as { version: string };\n\nconst program = createProgram(handlers, pkg.version);\nawait program.parseAsync();\n","// Auto-generated by cli-contracts. Do not edit.\nimport { Command } from \"commander\";\nimport { CONTRACT_YAML, CONTRACT_JSON_STR } from \"./contract.js\";\n\nexport interface CommandHandlers {\n record: (options: { eventType?: string; lifecycle?: string; spanId?: string; parentSpanId?: string; sessionId?: string; traceId?: string; source?: string; attr?: string; link?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n recordHook: (hookName: string | undefined, options: { mappingConfig?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n backfill: (options: { db?: string; endpoint?: string; serviceName?: string; from?: string; to?: string; eventType?: string; dryRun?: boolean; flushTimeoutMs?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n ingestVendorOtel: (options: { db?: string; input?: string; source?: string; dryRun?: boolean }, parentOpts: Record<string, unknown>) => Promise<void>;\n query: (options: { kind?: string; issue?: string; traceId?: string; spanId?: string; eventType?: string; taskId?: string; from?: string; to?: string; direction?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n report: (options: { projectId?: string; from?: string; to?: string; baselineFrom?: string; baselineTo?: string; format?: string; db?: string }, parentOpts: Record<string, unknown>) => Promise<void>;\n}\n\nexport function createProgram(\n handlers: CommandHandlers,\n version: string,\n): Command {\n const program = new Command();\n program\n .name(\"aaac-observ\")\n .version(version, \"-V, --version\")\n .description(\"aaac-observ — register external events and query the observability store\");\n\n\n program\n .command(\"record\")\n .description(\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\")\n .option(\"-t, --event-type <value>\", \"Event type (e.g. promotion.commit, process.edit, agent.session)\")\n .option(\"-l, --lifecycle <value>\", \"Event lifecycle phase: open | close | event | instant\")\n .option(\"-s, --span-id <value>\", \"Span ID (auto-generated if omitted)\")\n .option(\"--parent-span-id <value>\", \"Parent span ID for hierarchy\")\n .option(\"--session-id <value>\", \"Session ID to associate with the event\")\n .option(\"--trace-id <value>\", \"Trace ID for distributed tracing\")\n .option(\"--source <value>\", \"Event source identifier (default: external)\")\n .option(\"--attr <value>\", \"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\")\n .option(\"--link <value>\", \"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.record(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"record-hook\")\n .description(\"Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\")\n .argument(\"<hook-name>\", \"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\")\n .option(\"--mapping-config <value>\", \"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (hookName, opts, cmd) => {\n await handlers.recordHook(hookName, opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"backfill\")\n .description(\"Re-emit historical spans from SQLite to an OTLP backend\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .option(\"--endpoint <value>\", \"OTLP HTTP endpoint URL (fallback: OTEL_EXPORTER_OTLP_ENDPOINT)\")\n .option(\"--service-name <value>\", \"OTEL service name reported to backend (default: @aaac/observability)\")\n .option(\"--from <value>\", \"Backfill spans starting at or after this ISO 8601 time\")\n .option(\"--to <value>\", \"Backfill spans starting at or before this ISO 8601 time\")\n .option(\"--event-type <value>\", \"Filter by event type (e.g. agent.session)\")\n .option(\"--dry-run\", \"Print span count without emitting to OTLP backend\")\n .option(\"--flush-timeout-ms <value>\", \"Maximum ms to wait for OTLP flush before exit (default: 2000; use ≥30000 for >5k spans)\")\n .action(async (opts, cmd) => {\n await handlers.backfill(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"ingest-vendor-otel\")\n .description(\"Project a vendor OTLP/JSON trace export (e.g. Claude Code token/cost/trace) into the SQLite Ledger\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .option(\"-i, --input <value>\", \"Path to the OTLP/JSON trace export (reads stdin when omitted)\")\n .option(\"--source <value>\", \"Source tag for projected events (default: claude-code-otel)\")\n .option(\"--dry-run\", \"Parse and project without writing to the Ledger\")\n .action(async (opts, cmd) => {\n await handlers.ingestVendorOtel(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"query\")\n .description(\"Query the observability store via QueryAdapter\")\n .option(\"-k, --kind <value>\", \"Query kind: trace | span | search | links | issue\")\n .option(\"--issue <value>\", \"Issue ID for issue queries (--kind issue)\")\n .option(\"--trace-id <value>\", \"Trace ID for trace queries (--kind trace)\")\n .option(\"-s, --span-id <value>\", \"Span ID for span/links queries (--kind span or --kind links)\")\n .option(\"--event-type <value>\", \"Event type filter for search queries (--kind search)\")\n .option(\"--task-id <value>\", \"Task ID filter for search queries (--kind search)\")\n .option(\"--from <value>\", \"Start time in ISO 8601 format for search queries (--kind search)\")\n .option(\"--to <value>\", \"End time in ISO 8601 format for search queries (--kind search)\")\n .option(\"-d, --direction <value>\", \"Link traversal direction for links queries (default: both)\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.query(opts, cmd.optsWithGlobals());\n });\n\n program\n .command(\"report\")\n .description(\"Generate an Observability diagnostic report (Layer 1-3 + link completion + Person Tier) from the SQLite Ledger\")\n .option(\"-p, --project-id <value>\", \"Restrict the report to a single project_id (project.id attr / service.name)\")\n .option(\"--from <value>\", \"Start time (ISO 8601) for the current/after window\")\n .option(\"--to <value>\", \"End time (ISO 8601) for the current/after window\")\n .option(\"--baseline-from <value>\", \"Start time (ISO 8601) of the baseline/before window (enables Before/After comparison)\")\n .option(\"--baseline-to <value>\", \"End time (ISO 8601) of the baseline/before window (enables Before/After comparison)\")\n .option(\"-f, --format <value>\", \"Output format: markdown (default) | json\")\n .option(\"--db <value>\", \"Path to observability SQLite database (overrides default)\")\n .action(async (opts, cmd) => {\n await handlers.report(opts, cmd.optsWithGlobals());\n });\n\n\n // Built-in extract command (auto-injected by cli-contracts)\n program\n .command(\"extract\")\n .description(\"Extract contract specification for this CLI tool.\")\n .argument(\"[commands...]\", \"Command IDs to extract. Use dot notation.\")\n .option(\"-a, --all\", \"Extract all commands.\", false)\n .option(\"--include-meta\", \"Include extraction metadata.\", true)\n .option(\"-F, --format <format>\", \"Output format (yaml or json).\", \"yaml\")\n .action(async (commands: string[], opts: { all?: boolean; includeMeta?: boolean; format?: string }, cmd: Command) => {\n if (commands.length === 0 && !opts.all) {\n process.stderr.write(JSON.stringify({ code: \"INVALID_ARGS\", message: \"Specify command IDs or use --all\" }) + \"\\n\");\n process.exit(2);\n }\n\n const format = opts.format || \"yaml\";\n const doc = JSON.parse(CONTRACT_JSON_STR);\n const cmdIds = opts.all ? [] : commands;\n\n if (cmdIds.length === 0) {\n // --all: output full contract\n if (format === \"json\") {\n const out: Record<string, unknown> = {};\n if (opts.includeMeta) {\n out._meta = {\n source: \"embedded\",\n type: \"cli-contracts/extract\",\n extractedAt: new Date().toISOString(),\n specVersion: doc.cli_contracts ?? \"0.1.0\",\n commands: [\"aaac-observ.record\",\"aaac-observ.record-hook\",\"aaac-observ.backfill\",\"aaac-observ.ingest-vendor-otel\",\"aaac-observ.query\",\"aaac-observ.report\"],\n };\n }\n Object.assign(out, doc);\n process.stdout.write(JSON.stringify(out, null, 2) + \"\\n\");\n } else {\n // YAML output\n const yamlLines: string[] = [];\n yamlLines.push(\"# aaac-observ extract\");\n yamlLines.push(\"# source: embedded\");\n yamlLines.push(\"# type: cli-contracts/command-extract\");\n if (opts.includeMeta) {\n yamlLines.push(\"---\");\n yamlLines.push(\"source: embedded\");\n yamlLines.push(\"type: cli-contracts/command-extract\");\n yamlLines.push(\"extractedAt: \" + new Date().toISOString());\n yamlLines.push(\"spec_version: \" + (doc.cli_contracts ?? \"0.1.0\"));\n yamlLines.push(\"commands:\");\n for (const id of [\"aaac-observ.record\",\"aaac-observ.record-hook\",\"aaac-observ.backfill\",\"aaac-observ.ingest-vendor-otel\",\"aaac-observ.query\",\"aaac-observ.report\"]) {\n yamlLines.push(\" - \" + id);\n }\n }\n yamlLines.push(\"---\");\n yamlLines.push(CONTRACT_YAML);\n process.stdout.write(yamlLines.join(\"\\n\") + \"\\n\");\n }\n } else {\n // Filter specific commands\n const filtered: Record<string, unknown> = {\n cli_contracts: doc.cli_contracts,\n info: doc.info,\n command_sets: {},\n };\n const fcs = filtered.command_sets as Record<string, Record<string, unknown>>;\n for (const [setId, cs] of Object.entries(doc.command_sets ?? {})) {\n const cmds = (cs as Record<string, unknown>).commands as Record<string, unknown> | undefined;\n if (!cmds) continue;\n const matched: Record<string, unknown> = {};\n for (const [cmdId, cmdDef] of Object.entries(cmds)) {\n const fullId = setId + \".\" + cmdId;\n if (cmdIds.some((id) => id === cmdId || id === fullId || cmdId.startsWith(id + \".\"))) {\n matched[cmdId] = cmdDef;\n }\n }\n if (Object.keys(matched).length > 0) {\n const setCopy = { ...(cs as Record<string, unknown>) };\n setCopy.commands = matched;\n fcs[setId] = setCopy;\n }\n }\n if (doc.components) filtered.components = doc.components;\n process.stdout.write(JSON.stringify(filtered, null, 2) + \"\\n\");\n }\n process.exit(0);\n });\n return program;\n}\n","// Auto-generated by cli-contracts. Do not edit.\n// Embedded contract for the extract subcommand.\n\nexport const CONTRACT_YAML: string = \"# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json\\ncli_contracts: 0.1.0\\n\\ninfo:\\n title: AaaC Observability CLI\\n version: 0.1.0\\n description: aaac-observ — external event registration and query for @aaac/observability\\n\\ncommand_sets:\\n aaac-observ:\\n summary: aaac-observ — register external events and query the observability store\\n executable: aaac-observ\\n commands:\\n record:\\n summary: Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\n options:\\n - name: event-type\\n aliases: [t]\\n schema: { type: string }\\n description: \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n - name: lifecycle\\n aliases: [l]\\n schema:\\n type: string\\n enum: [open, close, event, instant]\\n description: \\\"Event lifecycle phase: open | close | event | instant\\\"\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID (auto-generated if omitted)\\n - name: parent-span-id\\n schema: { type: string }\\n description: Parent span ID for hierarchy\\n - name: session-id\\n schema: { type: string }\\n description: Session ID to associate with the event\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for distributed tracing\\n - name: source\\n schema: { type: string }\\n description: \\\"Event source identifier (default: external)\\\"\\n - name: attr\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n - name: link\\n schema:\\n type: array\\n items: { type: string }\\n description: \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Event registered successfully\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n eventId:\\n type: string\\n spanId:\\n type: string\\n '1':\\n description: Registration failed (validation error or write error)\\n stderr:\\n format: text\\n\\n record-hook:\\n summary: Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\\n arguments:\\n - name: hook-name\\n index: 0\\n required: true\\n schema: { type: string }\\n description: \\\"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\\\"\\n options:\\n - name: mapping-config\\n schema: { type: string }\\n description: \\\"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Hook recorded (or no-op when stdin/mapping unavailable) — always fail-open\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n recorded:\\n type: integer\\n fallback:\\n type: boolean\\n\\n backfill:\\n summary: Re-emit historical spans from SQLite to an OTLP backend\\n description: |\\n Reads closed/instant spans from the local SQLite database and emits them\\n via OtelEmitter to an OTLP-compatible backend.\\n\\n IMPORTANT — idempotency: The OTel SDK does not support specifying custom\\n spanIds, so each backfill invocation creates NEW spans in the backend.\\n Repeated invocations will produce DUPLICATE data. Use --from/--to to\\n control scope. See #125 §7 for full idempotency guidance.\\n options:\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n - name: endpoint\\n schema: { type: string }\\n description: \\\"OTLP HTTP endpoint URL (fallback: OTEL_EXPORTER_OTLP_ENDPOINT)\\\"\\n - name: service-name\\n schema: { type: string }\\n description: \\\"OTEL service name reported to backend (default: @aaac/observability)\\\"\\n - name: from\\n schema: { type: string }\\n description: Backfill spans starting at or after this ISO 8601 time\\n - name: to\\n schema: { type: string }\\n description: Backfill spans starting at or before this ISO 8601 time\\n - name: event-type\\n schema: { type: string }\\n description: \\\"Filter by event type (e.g. agent.session)\\\"\\n - name: dry-run\\n schema: { type: boolean }\\n description: Print span count without emitting to OTLP backend\\n - name: flush-timeout-ms\\n schema: { type: integer }\\n description: \\\"Maximum ms to wait for OTLP flush before exit (default: 2000; use ≥30000 for >5k spans)\\\"\\n exits:\\n '0':\\n description: Backfill completed\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n spansFound:\\n type: integer\\n spansEmitted:\\n type: integer\\n dryRun:\\n type: boolean\\n '1':\\n description: Backfill failed (missing endpoint, DB error, etc.)\\n stderr:\\n format: text\\n\\n ingest-vendor-otel:\\n summary: Project a vendor OTLP/JSON trace export (e.g. Claude Code token/cost/trace) into the SQLite Ledger\\n description: |\\n Canonical Projection batch (observability-integration.md §9.2 / T-D2).\\n\\n Reads a vendor OTLP/JSON trace export from --input (file) or stdin, projects\\n each span into a Canonical Event, and backfills it into the SQLite Ledger.\\n\\n Idempotent: every event carries a deterministic dedup_key\\n (vendor-otel:{traceId}:{spanId}) enforced by UNIQUE + INSERT OR IGNORE, so\\n re-running on the same export inserts nothing new (reported as\\n skippedDuplicate). Hook/runtime records remain the primary source; vendor\\n OTEL is auxiliary evidence.\\n options:\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n - name: input\\n aliases: [i]\\n schema: { type: string }\\n description: \\\"Path to the OTLP/JSON trace export (reads stdin when omitted)\\\"\\n - name: source\\n schema: { type: string }\\n description: \\\"Source tag for projected events (default: claude-code-otel)\\\"\\n - name: dry-run\\n schema: { type: boolean }\\n description: Parse and project without writing to the Ledger\\n exits:\\n '0':\\n description: Ingestion completed\\n stdout:\\n format: json\\n schema:\\n type: object\\n properties:\\n found:\\n type: integer\\n inserted:\\n type: integer\\n skippedDuplicate:\\n type: integer\\n skippedInvalid:\\n type: integer\\n dryRun:\\n type: boolean\\n '1':\\n description: Ingestion failed (unreadable input, invalid JSON, DB error)\\n stderr:\\n format: text\\n\\n query:\\n summary: Query the observability store via QueryAdapter\\n options:\\n - name: kind\\n aliases: [k]\\n schema:\\n type: string\\n enum: [trace, span, search, links, issue]\\n description: \\\"Query kind: trace | span | search | links | issue\\\"\\n - name: issue\\n schema: { type: string }\\n description: \\\"Issue ID for issue queries (--kind issue)\\\"\\n - name: trace-id\\n schema: { type: string }\\n description: Trace ID for trace queries (--kind trace)\\n - name: span-id\\n aliases: [s]\\n schema: { type: string }\\n description: Span ID for span/links queries (--kind span or --kind links)\\n - name: event-type\\n schema: { type: string }\\n description: Event type filter for search queries (--kind search)\\n - name: task-id\\n schema: { type: string }\\n description: Task ID filter for search queries (--kind search)\\n - name: from\\n schema: { type: string }\\n description: Start time in ISO 8601 format for search queries (--kind search)\\n - name: to\\n schema: { type: string }\\n description: End time in ISO 8601 format for search queries (--kind search)\\n - name: direction\\n aliases: [d]\\n schema:\\n type: string\\n enum: [forward, reverse, both]\\n description: \\\"Link traversal direction for links queries (default: both)\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Query succeeded — results as JSON array\\n stdout:\\n format: json\\n schema:\\n type: object\\n '1':\\n description: Query failed\\n stderr:\\n format: text\\n\\n report:\\n summary: Generate an Observability diagnostic report (Layer 1-3 + link completion + Person Tier) from the SQLite Ledger\\n description: |\\n Customer-facing Observability diagnostic (#179 WS-F T-F1). Computes the\\n same Layer 1-3 / link-completion / Person-Tier metrics rendered by the\\n `aaac-diagnostic` Grafana dashboard, over the local SQLite Ledger.\\n\\n Output is Markdown (default) or JSON. Use --project-id to scope to a\\n single project (internal or a cooperating customer project). Supply\\n --baseline-from / --baseline-to to add a Before/After comparison\\n section against a baseline window; --from / --to bound the current\\n (after) window.\\n options:\\n - name: project-id\\n aliases: [p]\\n schema: { type: string }\\n description: Restrict the report to a single project_id (project.id attr / service.name)\\n - name: from\\n schema: { type: string }\\n description: Start time (ISO 8601) for the current/after window\\n - name: to\\n schema: { type: string }\\n description: End time (ISO 8601) for the current/after window\\n - name: baseline-from\\n schema: { type: string }\\n description: Start time (ISO 8601) of the baseline/before window (enables Before/After comparison)\\n - name: baseline-to\\n schema: { type: string }\\n description: End time (ISO 8601) of the baseline/before window (enables Before/After comparison)\\n - name: format\\n aliases: [f]\\n schema:\\n type: string\\n enum: [markdown, json]\\n description: \\\"Output format: markdown (default) | json\\\"\\n - name: db\\n schema: { type: string }\\n description: Path to observability SQLite database (overrides default)\\n exits:\\n '0':\\n description: Report generated — Markdown or JSON on stdout\\n stdout:\\n format: text\\n '1':\\n description: Report generation failed\\n stderr:\\n format: text\\n\";\n\nexport const CONTRACT_JSON_STR: string = \"{\\n \\\"cli_contracts\\\": \\\"0.1.0\\\",\\n \\\"info\\\": {\\n \\\"title\\\": \\\"AaaC Observability CLI\\\",\\n \\\"version\\\": \\\"0.1.0\\\",\\n \\\"description\\\": \\\"aaac-observ — external event registration and query for @aaac/observability\\\"\\n },\\n \\\"command_sets\\\": {\\n \\\"aaac-observ\\\": {\\n \\\"summary\\\": \\\"aaac-observ — register external events and query the observability store\\\",\\n \\\"executable\\\": \\\"aaac-observ\\\",\\n \\\"commands\\\": {\\n \\\"record\\\": {\\n \\\"summary\\\": \\\"Register an external event into the observability pipeline (thin entrypoint for EventCollector.registerExternalEvent)\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"aliases\\\": [\\n \\\"t\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type (e.g. promotion.commit, process.edit, agent.session)\\\"\\n },\\n {\\n \\\"name\\\": \\\"lifecycle\\\",\\n \\\"aliases\\\": [\\n \\\"l\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"open\\\",\\n \\\"close\\\",\\n \\\"event\\\",\\n \\\"instant\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Event lifecycle phase: open | close | event | instant\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID (auto-generated if omitted)\\\"\\n },\\n {\\n \\\"name\\\": \\\"parent-span-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Parent span ID for hierarchy\\\"\\n },\\n {\\n \\\"name\\\": \\\"session-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Session ID to associate with the event\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for distributed tracing\\\"\\n },\\n {\\n \\\"name\\\": \\\"source\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event source identifier (default: external)\\\"\\n },\\n {\\n \\\"name\\\": \\\"attr\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Attribute as key=value (repeatable, e.g. --attr git.commit=abc123)\\\"\\n },\\n {\\n \\\"name\\\": \\\"link\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"array\\\",\\n \\\"items\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n },\\n \\\"description\\\": \\\"Cross-axis link as targetSpanId:linkType[:targetTraceId] (repeatable)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Event registered successfully\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"eventId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"spanId\\\": {\\n \\\"type\\\": \\\"string\\\"\\n }\\n }\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Registration failed (validation error or write error)\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"record-hook\\\": {\\n \\\"summary\\\": \\\"Record a Cursor/git hook event — parses stdin JSON, resolves event_mapping into 3-axis spans/links, injects session_id, and emits human-events (fail-open)\\\",\\n \\\"arguments\\\": [\\n {\\n \\\"name\\\": \\\"hook-name\\\",\\n \\\"index\\\": 0,\\n \\\"required\\\": true,\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Cursor/git hook name (e.g. afterFileEdit, subagentStart, beforeShellExecution)\\\"\\n }\\n ],\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"mapping-config\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to event-mapping.json (default: .agent-logs/config/event-mapping.json)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Hook recorded (or no-op when stdin/mapping unavailable) — always fail-open\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"recorded\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"fallback\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n },\\n \\\"backfill\\\": {\\n \\\"summary\\\": \\\"Re-emit historical spans from SQLite to an OTLP backend\\\",\\n \\\"description\\\": \\\"Reads closed/instant spans from the local SQLite database and emits them\\\\nvia OtelEmitter to an OTLP-compatible backend.\\\\n\\\\nIMPORTANT — idempotency: The OTel SDK does not support specifying custom\\\\nspanIds, so each backfill invocation creates NEW spans in the backend.\\\\nRepeated invocations will produce DUPLICATE data. Use --from/--to to\\\\ncontrol scope. See #125 §7 for full idempotency guidance.\\\\n\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n },\\n {\\n \\\"name\\\": \\\"endpoint\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"OTLP HTTP endpoint URL (fallback: OTEL_EXPORTER_OTLP_ENDPOINT)\\\"\\n },\\n {\\n \\\"name\\\": \\\"service-name\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"OTEL service name reported to backend (default: @aaac/observability)\\\"\\n },\\n {\\n \\\"name\\\": \\\"from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Backfill spans starting at or after this ISO 8601 time\\\"\\n },\\n {\\n \\\"name\\\": \\\"to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Backfill spans starting at or before this ISO 8601 time\\\"\\n },\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Filter by event type (e.g. agent.session)\\\"\\n },\\n {\\n \\\"name\\\": \\\"dry-run\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n },\\n \\\"description\\\": \\\"Print span count without emitting to OTLP backend\\\"\\n },\\n {\\n \\\"name\\\": \\\"flush-timeout-ms\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"description\\\": \\\"Maximum ms to wait for OTLP flush before exit (default: 2000; use ≥30000 for >5k spans)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Backfill completed\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"spansFound\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"spansEmitted\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"dryRun\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n }\\n }\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Backfill failed (missing endpoint, DB error, etc.)\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"ingest-vendor-otel\\\": {\\n \\\"summary\\\": \\\"Project a vendor OTLP/JSON trace export (e.g. Claude Code token/cost/trace) into the SQLite Ledger\\\",\\n \\\"description\\\": \\\"Canonical Projection batch (observability-integration.md §9.2 / T-D2).\\\\n\\\\nReads a vendor OTLP/JSON trace export from --input (file) or stdin, projects\\\\neach span into a Canonical Event, and backfills it into the SQLite Ledger.\\\\n\\\\nIdempotent: every event carries a deterministic dedup_key\\\\n(vendor-otel:{traceId}:{spanId}) enforced by UNIQUE + INSERT OR IGNORE, so\\\\nre-running on the same export inserts nothing new (reported as\\\\nskippedDuplicate). Hook/runtime records remain the primary source; vendor\\\\nOTEL is auxiliary evidence.\\\\n\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n },\\n {\\n \\\"name\\\": \\\"input\\\",\\n \\\"aliases\\\": [\\n \\\"i\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to the OTLP/JSON trace export (reads stdin when omitted)\\\"\\n },\\n {\\n \\\"name\\\": \\\"source\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Source tag for projected events (default: claude-code-otel)\\\"\\n },\\n {\\n \\\"name\\\": \\\"dry-run\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n },\\n \\\"description\\\": \\\"Parse and project without writing to the Ledger\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Ingestion completed\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\",\\n \\\"properties\\\": {\\n \\\"found\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"inserted\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"skippedDuplicate\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"skippedInvalid\\\": {\\n \\\"type\\\": \\\"integer\\\"\\n },\\n \\\"dryRun\\\": {\\n \\\"type\\\": \\\"boolean\\\"\\n }\\n }\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Ingestion failed (unreadable input, invalid JSON, DB error)\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"query\\\": {\\n \\\"summary\\\": \\\"Query the observability store via QueryAdapter\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"kind\\\",\\n \\\"aliases\\\": [\\n \\\"k\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"trace\\\",\\n \\\"span\\\",\\n \\\"search\\\",\\n \\\"links\\\",\\n \\\"issue\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Query kind: trace | span | search | links | issue\\\"\\n },\\n {\\n \\\"name\\\": \\\"issue\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Issue ID for issue queries (--kind issue)\\\"\\n },\\n {\\n \\\"name\\\": \\\"trace-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Trace ID for trace queries (--kind trace)\\\"\\n },\\n {\\n \\\"name\\\": \\\"span-id\\\",\\n \\\"aliases\\\": [\\n \\\"s\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Span ID for span/links queries (--kind span or --kind links)\\\"\\n },\\n {\\n \\\"name\\\": \\\"event-type\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Event type filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"task-id\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Task ID filter for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Start time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"End time in ISO 8601 format for search queries (--kind search)\\\"\\n },\\n {\\n \\\"name\\\": \\\"direction\\\",\\n \\\"aliases\\\": [\\n \\\"d\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"forward\\\",\\n \\\"reverse\\\",\\n \\\"both\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Link traversal direction for links queries (default: both)\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Query succeeded — results as JSON array\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"json\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"object\\\"\\n }\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Query failed\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n },\\n \\\"report\\\": {\\n \\\"summary\\\": \\\"Generate an Observability diagnostic report (Layer 1-3 + link completion + Person Tier) from the SQLite Ledger\\\",\\n \\\"description\\\": \\\"Customer-facing Observability diagnostic (#179 WS-F T-F1). Computes the\\\\nsame Layer 1-3 / link-completion / Person-Tier metrics rendered by the\\\\n`aaac-diagnostic` Grafana dashboard, over the local SQLite Ledger.\\\\n\\\\nOutput is Markdown (default) or JSON. Use --project-id to scope to a\\\\nsingle project (internal or a cooperating customer project). Supply\\\\n--baseline-from / --baseline-to to add a Before/After comparison\\\\nsection against a baseline window; --from / --to bound the current\\\\n(after) window.\\\\n\\\",\\n \\\"options\\\": [\\n {\\n \\\"name\\\": \\\"project-id\\\",\\n \\\"aliases\\\": [\\n \\\"p\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Restrict the report to a single project_id (project.id attr / service.name)\\\"\\n },\\n {\\n \\\"name\\\": \\\"from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Start time (ISO 8601) for the current/after window\\\"\\n },\\n {\\n \\\"name\\\": \\\"to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"End time (ISO 8601) for the current/after window\\\"\\n },\\n {\\n \\\"name\\\": \\\"baseline-from\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Start time (ISO 8601) of the baseline/before window (enables Before/After comparison)\\\"\\n },\\n {\\n \\\"name\\\": \\\"baseline-to\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"End time (ISO 8601) of the baseline/before window (enables Before/After comparison)\\\"\\n },\\n {\\n \\\"name\\\": \\\"format\\\",\\n \\\"aliases\\\": [\\n \\\"f\\\"\\n ],\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\",\\n \\\"enum\\\": [\\n \\\"markdown\\\",\\n \\\"json\\\"\\n ]\\n },\\n \\\"description\\\": \\\"Output format: markdown (default) | json\\\"\\n },\\n {\\n \\\"name\\\": \\\"db\\\",\\n \\\"schema\\\": {\\n \\\"type\\\": \\\"string\\\"\\n },\\n \\\"description\\\": \\\"Path to observability SQLite database (overrides default)\\\"\\n }\\n ],\\n \\\"exits\\\": {\\n \\\"0\\\": {\\n \\\"description\\\": \\\"Report generated — Markdown or JSON on stdout\\\",\\n \\\"stdout\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n },\\n \\\"1\\\": {\\n \\\"description\\\": \\\"Report generation failed\\\",\\n \\\"stderr\\\": {\\n \\\"format\\\": \\\"text\\\"\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n}\";\n","/**\n * record handler — thin entrypoint for EventCollector.registerExternalEvent.\n *\n * Wires the CLI options to the write pipeline:\n * options → EventCollector.registerExternalEvent\n * → Normalizer → Correlator → Enricher → SqliteSink + OtelEmitter\n *\n * architecture.md §9 / OBSERVABILITY_TASKS.md Phase 3-2\n * shift-left-event-mapping.md: aaac-observ record is the thin entrypoint\n */\nimport { createPipeline, generateId, DEFAULT_DB_PATH } from \"../index.js\";\nimport type { CanonicalLink, AttrValue, Lifecycle } from \"../types/canonical-event.js\";\n\n// ── Parsers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse comma-separated key=value attribute string into a Record.\n * Also accepts a JSON object string.\n *\n * Examples:\n * \"git.commit=abc123,git.branch=main\"\n * '{\"git.commit\":\"abc123\",\"git.branch\":\"main\"}'\n */\nexport function parseAttr(attrStr: string | undefined): Record<string, AttrValue> {\n if (!attrStr) return {};\n const trimmed = attrStr.trim();\n if (trimmed.startsWith(\"{\")) {\n try {\n return JSON.parse(trimmed) as Record<string, AttrValue>;\n } catch {\n // fall through to key=value parsing\n }\n }\n const result: Record<string, AttrValue> = {};\n for (const pair of trimmed.split(\",\")) {\n const eqIdx = pair.indexOf(\"=\");\n if (eqIdx > 0) {\n const key = pair.slice(0, eqIdx).trim();\n const val = pair.slice(eqIdx + 1).trim();\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * Parse comma-separated link string into a CanonicalLink array.\n * Format per item: targetSpanId:linkType[:targetTraceId]\n *\n * Example: \"spanId123:materializes_as_commit,spanId456:contains_change:traceId789\"\n */\nexport function parseLinks(linkStr: string | undefined): CanonicalLink[] {\n if (!linkStr) return [];\n const links: CanonicalLink[] = [];\n for (const item of linkStr.split(\",\")) {\n const parts = item.trim().split(\":\");\n if (parts.length >= 2) {\n const targetSpanId = parts[0].trim();\n const linkType = parts[1].trim();\n const targetTraceId = parts[2]?.trim() || undefined;\n if (targetSpanId && linkType) {\n links.push({ targetSpanId, linkType, targetTraceId });\n }\n }\n }\n return links;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n * NOTE: attr and link are strings (last value from --option) not arrays,\n * because the generated Commander program does not use a collector.\n * Comma-separate multiple values: --attr \"key1=val1,key2=val2\"\n */\nexport interface RecordHandlerOptions {\n eventType?: string;\n lifecycle?: string;\n spanId?: string;\n parentSpanId?: string;\n sessionId?: string;\n traceId?: string;\n source?: string;\n attr?: string;\n link?: string;\n db?: string;\n}\n\n/**\n * Handle `aaac-observ record`.\n *\n * Required options: --event-type, --lifecycle\n * On success: writes the event through the full pipeline and prints\n * { eventId, spanId } to stdout.\n * On error: writes to stderr and exits with code 1.\n */\nexport async function handleRecord(\n options: RecordHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n // ── Validate required options ──────────────────────────────────────────────\n if (!options.eventType) {\n process.stderr.write(\"Error: --event-type (-t) is required\\n\");\n process.exit(1);\n }\n const validLifecycles: readonly string[] = [\"open\", \"close\", \"event\", \"instant\"];\n if (!options.lifecycle) {\n process.stderr.write(\n `Error: --lifecycle (-l) is required (${validLifecycles.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validLifecycles.includes(options.lifecycle)) {\n process.stderr.write(\n `Error: --lifecycle must be one of: ${validLifecycles.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n // ── Build event payload ────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const spanId = options.spanId ?? generateId();\n const attributes = parseAttr(options.attr);\n const links = parseLinks(options.link);\n\n // Inject well-known scalars into attributes so the Normalizer picks them up\n if (options.sessionId) {\n attributes[\"session_id\"] = options.sessionId;\n }\n // trace_id stored as attribute for user reference; canonical traceId is\n // managed by Normalizer/Correlator (propagated via parentSpanId chain)\n if (options.traceId) {\n attributes[\"trace_id\"] = options.traceId;\n }\n\n // ── Build & execute pipeline ───────────────────────────────────────────────\n let capturedEventId: string | undefined;\n\n const { collector, sink } = createPipeline({\n dbPath,\n // Use afterCorrelate to capture the event id before it is written\n afterCorrelate: (event) => {\n capturedEventId = event.id;\n return event;\n },\n // Enable the default enrichment rules (R1–R5). This is the path the git\n // post-commit hook uses to record promotion.commit (close) with\n // committed_files; R5 (cross-axis) must run here to add the\n // materializes_as_commit link to task spans whose modified files intersect\n // the commit. R3/R4 accumulation persists in the SQLite-backed cache of the\n // same db file, so R5 sees it across the process boundary (#143 / #140).\n // Enricher is fail-open, so a rule error never blocks recording.\n });\n\n try {\n collector.registerExternalEvent({\n source: options.source ?? \"external\",\n eventType: options.eventType,\n lifecycle: options.lifecycle as Lifecycle,\n spanId,\n parentSpanId: options.parentSpanId,\n attributes,\n links,\n });\n } catch (err) {\n process.stderr.write(\n `Error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n sink.close();\n process.exit(1);\n }\n\n sink.close();\n\n const result = { eventId: capturedEventId, spanId };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * query handler — read path via QueryAdapter.\n *\n * Routes --kind to the appropriate SqliteQueryAdapter method and emits\n * results as JSON to stdout.\n *\n * Supported kinds:\n * trace → QueryAdapter.getTrace(--trace-id)\n * span → QueryAdapter.getSpan(--span-id) [+ QueryAdapter.getSpanEvents]\n * search → QueryAdapter.querySpans({ eventType, taskId, from, to })\n * links → QueryAdapter.getLinks(--span-id, --direction)\n * issue → analyzeOutcomeChain(--issue) [#178 T-E2]\n *\n * architecture.md §12 / OBSERVABILITY_TASKS.md Phase 3-2 (record CLI + query)\n */\nimport { SqliteQueryAdapter, DEFAULT_DB_PATH } from \"../index.js\";\nimport { isoToUnixNano } from \"../types/ids.js\";\nimport type { LinkDirection } from \"../query/models.js\";\nimport { analyzeOutcomeChain, OUTCOME_CHAIN_STAGES } from \"../analysis/outcome-chain.js\";\n\n// ── Handler ───────────────────────────────────────────────────────────────────\n\n/**\n * Options as parsed from the generated CommandHandlers interface.\n */\nexport interface QueryHandlerOptions {\n kind?: string;\n traceId?: string;\n spanId?: string;\n eventType?: string;\n taskId?: string;\n from?: string;\n to?: string;\n direction?: string;\n issue?: string;\n db?: string;\n}\n\n/** JSON replacer that serialises BigInt as a string. */\nfunction bigIntReplacer(_key: string, value: unknown): unknown {\n return typeof value === \"bigint\" ? value.toString() : value;\n}\n\n/**\n * Handle `aaac-observ query`.\n *\n * Required option: --kind (trace|span|search|links)\n * On success: writes a JSON array/object to stdout.\n * On error: writes to stderr and exits with code 1.\n *\n * DESIGN NOTE: all required-arg validation happens BEFORE the adapter is\n * created, so that when process.exit is mocked to throw in tests there is no\n * risk of a double-close on the database connection.\n */\nexport async function handleQuery(\n options: QueryHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n // Proposal shorthand: `aaac-observ query --issue <id>` (#178 T-E2)\n if (options.issue && !options.kind) {\n options.kind = \"issue\";\n }\n\n const validKinds = [\"trace\", \"span\", \"search\", \"links\", \"issue\"] as const;\n type QueryKind = (typeof validKinds)[number];\n\n // ── Validate options (before opening the adapter) ─────────────────────────\n if (!options.kind) {\n process.stderr.write(\n `Error: --kind (-k) is required (${validKinds.join(\"|\")})\\n`,\n );\n process.exit(1);\n }\n if (!validKinds.includes(options.kind as QueryKind)) {\n process.stderr.write(\n `Error: --kind must be one of: ${validKinds.join(\"|\")}\\n`,\n );\n process.exit(1);\n }\n\n const kind = options.kind as QueryKind;\n\n // Validate sub-command required args (before adapter creation)\n if (kind === \"trace\" && !options.traceId) {\n process.stderr.write(\"Error: --trace-id is required for --kind trace\\n\");\n process.exit(1);\n }\n if (kind === \"span\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind span\\n\");\n process.exit(1);\n }\n if (kind === \"links\" && !options.spanId) {\n process.stderr.write(\"Error: --span-id is required for --kind links\\n\");\n process.exit(1);\n }\n if (kind === \"issue\" && !options.issue) {\n process.stderr.write(\"Error: --issue is required for --kind issue\\n\");\n process.exit(1);\n }\n\n // ── Open adapter ──────────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const adapter = new SqliteQueryAdapter(dbPath);\n\n try {\n let result: unknown;\n\n switch (kind) {\n // ── trace: all spans in a trace ─────────────────────────────────────\n case \"trace\": {\n // options.traceId is guaranteed by pre-validation above\n result = adapter.getTrace(options.traceId!);\n break;\n }\n\n // ── span: single span + its events ──────────────────────────────────\n case \"span\": {\n // options.spanId is guaranteed by pre-validation above\n const span = adapter.getSpan(options.spanId!);\n const events = adapter.getSpanEvents(options.spanId!);\n result = span !== undefined ? { span, events } : null;\n break;\n }\n\n // ── search: spans matching event-type / task-id / time range ─────────\n case \"search\": {\n const filter: Parameters<typeof adapter.querySpans>[0] = {};\n if (options.eventType) filter.eventType = options.eventType;\n if (options.taskId) filter.taskId = options.taskId;\n if (options.from) filter.fromTimeUnixNano = isoToUnixNano(options.from);\n if (options.to) filter.toTimeUnixNano = isoToUnixNano(options.to);\n result = adapter.querySpans(filter);\n break;\n }\n\n // ── links: link traversal ─────────────────────────────────────────────\n case \"links\": {\n const validDirections = [\"forward\", \"reverse\", \"both\"] as const;\n type Dir = (typeof validDirections)[number];\n const dir: Dir =\n options.direction !== undefined &&\n validDirections.includes(options.direction as Dir)\n ? (options.direction as Dir)\n : \"both\";\n result = adapter.getLinks(options.spanId!, dir as LinkDirection);\n break;\n }\n\n // ── issue: Outcome chain analysis for an Issue (#178 T-E2) ──────────\n case \"issue\": {\n // options.issue is guaranteed by pre-validation above\n result = analyzeOutcomeChain(adapter, options.issue!);\n break;\n }\n }\n\n process.stdout.write(JSON.stringify(result, bigIntReplacer) + \"\\n\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n\n // Graceful handling: an un-initialised (fresh empty) database has no\n // tables yet. Return an empty / null result rather than an error so that\n // `aaac-observ query` on a machine with no recorded events behaves\n // predictably.\n if (msg.includes(\"no such table\") || msg.includes(\"no such column\")) {\n let emptyResult: unknown;\n if (kind === \"span\") {\n emptyResult = null;\n } else if (kind === \"issue\") {\n // No tables → all stages are missing\n emptyResult = {\n issueId: options.issue ?? \"\",\n stages: OUTCOME_CHAIN_STAGES.map((s) => ({\n eventType: s,\n status: \"missing\",\n spanIds: [],\n })),\n complete: false,\n gaps: [...OUTCOME_CHAIN_STAGES],\n provisional: [],\n };\n } else {\n emptyResult = [];\n }\n process.stdout.write(JSON.stringify(emptyResult, bigIntReplacer) + \"\\n\");\n adapter.close();\n return;\n }\n\n process.stderr.write(`Error: ${msg}\\n`);\n adapter.close();\n process.exit(1);\n }\n\n adapter.close();\n}\n","/**\n * backfill handler — re-emit historical spans from SQLite to an OTLP backend.\n *\n * Reads closed/instant spans from the local SQLite database and emits them\n * via OtelEmitter to an OTLP-compatible backend (e.g. OTel Collector →\n * ClickHouse).\n *\n * ## Idempotency design (important)\n *\n * The OTel SDK does NOT support specifying a custom spanId when creating spans.\n * Therefore, each backfill invocation will create NEW spans in the OTLP backend\n * with different spanIds than the originals recorded in SQLite. Repeated\n * invocations will produce DUPLICATE data in the backend (e.g. ClickHouse).\n *\n * Recommended idempotency strategies:\n * 1. Run backfill only once per SQLite DB / time range.\n * 2. Use --from / --to to backfill specific time windows; track which windows\n * have already been backfilled externally.\n * 3. Truncate the ClickHouse target table before re-running (destructive; safe\n * for dev environments).\n * 4. Use ClickHouse ReplacingMergeTree / FINAL for approximate deduplication\n * (requires schema changes; not the default otel_traces schema).\n *\n * The --dry-run flag prints span metadata without emitting; use it to verify\n * scope before committing to a real backfill.\n *\n * architecture.md §11 / docs/observability-setup.md / #125 §7\n */\nimport { SqliteQueryAdapter, OtelEmitter, DEFAULT_DB_PATH, isoToUnixNano } from \"../index.js\";\nimport { mapAllEventsToSpans } from \"../otel/span-mapper.js\";\nimport type { MappedSpan, MappedSpanLink } from \"../otel/span-mapper.js\";\nimport type { SpanRecord, LinkRecord } from \"../query/models.js\";\n\n// ── Handler options ───────────────────────────────────────────────────────────\n\nexport interface BackfillHandlerOptions {\n /** SQLite database path. Defaults to DEFAULT_DB_PATH. */\n db?: string;\n /**\n * OTLP HTTP endpoint URL.\n * Fallback: OTEL_EXPORTER_OTLP_ENDPOINT env var.\n * Required unless --dry-run is set.\n */\n endpoint?: string;\n /** OTEL service name reported to the backend. Default: '@aaac/observability'. */\n serviceName?: string;\n /** Filter: only backfill spans starting at or after this ISO 8601 time. */\n from?: string;\n /** Filter: only backfill spans starting at or before this ISO 8601 time. */\n to?: string;\n /** Filter: only backfill spans of this event type (e.g. 'agent.session'). */\n eventType?: string;\n /**\n * Print span metadata to stdout without actually emitting to the OTLP backend.\n * Useful for previewing scope before committing to a real backfill.\n */\n dryRun?: boolean;\n /**\n * Maximum milliseconds to wait for the OTLP flush before the CLI exits.\n * Default: 2000 ms (preserves existing vitest compatibility — unreachable\n * endpoints resolve immediately via connection-refused, so tests are unaffected\n * by the default value).\n *\n * For large backfills (> 5 000 spans) the BatchSpanProcessor needs significantly\n * more time. Recommended production value: 30 000 ms.\n *\n * Rule of thumb: allow ≥ 400 ms per 512-span batch\n * (i.e. ceil(spansFound / 512) * 400 ms, rounded up generously).\n *\n * Accepts `string` because the generated CLI program forwards the raw\n * `--flush-timeout-ms` option value verbatim; it is coerced to a number\n * (see `effectiveFlushMs`) inside the handler.\n */\n flushTimeoutMs?: number | string;\n}\n\n// ── MappedSpan reconstruction ─────────────────────────────────────────────────\n\n/**\n * Reconstruct a MappedSpan from a stored SpanRecord + its forward links.\n *\n * Backfill works at the \"span\" level (not the raw event level) because\n * SqliteSink materialises open+close into the `spans` table at write time.\n * Only closed or instant spans carry meaningful duration information.\n */\nfunction spanRecordToMappedSpan(\n span: SpanRecord,\n links: LinkRecord[],\n): MappedSpan {\n const start = span.startTime ?? 0n;\n const end = span.endTime ?? start;\n\n // Reconstruct MappedSpanLinks from the stored canonical_links rows.\n const mappedLinks: MappedSpanLink[] = links.map((l) => ({\n targetSpanId: l.targetSpanId,\n targetTraceId: l.targetTraceId ?? undefined,\n attributes: {\n link_type: l.linkType,\n ...(l.attributes ?? {}),\n },\n }));\n\n return {\n spanId: span.spanId,\n traceId: span.traceId ?? \"\",\n parentSpanId: span.parentSpanId ?? undefined,\n name: span.eventType,\n startTimeUnixNano: start,\n endTimeUnixNano: end,\n attributes: span.attributes,\n spanEvents: [], // span events are not stored in the spans table\n links: mappedLinks,\n };\n}\n\n// ── Handler ───────────────────────────────────────────────────────────────────\n\n/**\n * Handle `aaac-observ backfill`.\n *\n * Emits historical spans from the SQLite store to an OTLP backend.\n * Prints a JSON summary to stdout:\n * { spansFound: number, spansEmitted: number, dryRun: boolean }\n *\n * On error: writes to stderr and exits with code 1.\n *\n * IMPORTANT — idempotency: Re-running backfill without --from/--to restriction\n * will re-emit all matching spans, creating DUPLICATES in the OTLP backend.\n * See module-level comment for mitigation strategies.\n */\nexport async function handleBackfill(\n options: BackfillHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const dryRun = options.dryRun ?? false;\n\n // ── Validate endpoint (not needed for dry-run) ────────────────────────────\n const endpoint = options.endpoint ?? process.env[\"OTEL_EXPORTER_OTLP_ENDPOINT\"];\n if (!dryRun && !endpoint) {\n process.stderr.write(\n \"Error: --endpoint is required (or set OTEL_EXPORTER_OTLP_ENDPOINT) unless --dry-run\\n\",\n );\n process.exit(1);\n }\n\n // ── Validate and parse time filters ───────────────────────────────────────\n // isoToUnixNano() silently falls back to currentTimeUnixNano() for invalid\n // ISO strings (shared utility — behaviour unchanged for other callers).\n // Backfill must validate explicitly so a typo in --from/--to causes exit 1\n // rather than a silent wrong-boundary backfill (F1 / issue #125).\n let fromNano: bigint | undefined;\n let toNano: bigint | undefined;\n if (options.from) {\n if (Number.isNaN(Date.parse(options.from))) {\n process.stderr.write(\n `Error: invalid --from value \"${options.from}\" — expected ISO 8601\\n`,\n );\n process.exit(1);\n }\n fromNano = isoToUnixNano(options.from);\n }\n if (options.to) {\n if (Number.isNaN(Date.parse(options.to))) {\n process.stderr.write(\n `Error: invalid --to value \"${options.to}\" — expected ISO 8601\\n`,\n );\n process.exit(1);\n }\n toNano = isoToUnixNano(options.to);\n }\n\n // ── Query spans from SQLite ────────────────────────────────────────────────\n const adapter = new SqliteQueryAdapter(dbPath);\n let spans: SpanRecord[];\n try {\n spans = adapter.querySpans({\n ...(options.eventType ? { eventType: options.eventType } : {}),\n ...(fromNano !== undefined ? { fromTimeUnixNano: fromNano } : {}),\n ...(toNano !== undefined ? { toTimeUnixNano: toNano } : {}),\n });\n // Only backfill spans that have completed (closed or instant).\n // Open spans are missing their end_time and would emit with zero duration.\n spans = spans.filter((s) => s.status === \"closed\" || s.status === \"instant\");\n } catch (err) {\n process.stderr.write(\n `Error: failed to query SQLite — ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n adapter.close();\n process.exit(1);\n return; // unreachable, but satisfies TS control-flow\n }\n\n const spansFound = spans.length;\n\n if (dryRun) {\n adapter.close();\n const result = { spansFound, spansEmitted: 0, dryRun: true };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n return;\n }\n\n // ── Reconstruct MappedSpans with their links ──────────────────────────────\n const mappedSpans: MappedSpan[] = [];\n for (const span of spans) {\n let links: LinkRecord[] = [];\n try {\n links = adapter.getLinks(span.spanId, \"forward\");\n } catch {\n // fail-open: missing links are non-fatal for backfill\n }\n mappedSpans.push(spanRecordToMappedSpan(span, links));\n }\n\n adapter.close();\n\n // ── Large-backfill warning ────────────────────────────────────────────────\n // Warn before emitting when the operator-supplied (or default) flush timeout\n // is likely too short for the span count. Rule of thumb: each 512-span batch\n // takes ~400 ms → 1 000 spans ≈ 2 batches ≈ 800 ms, well under 2 s, but\n // 13 000 spans ≈ 26 batches ≈ 10 400 ms, which exceeds the 2 s default.\n // Emitting the warning here (before the emit loop) lets the operator ^C and\n // re-run with --flush-timeout-ms 30000 before any data is sent.\n const effectiveFlushMs =\n options.flushTimeoutMs != null ? Number(options.flushTimeoutMs) : 2000;\n if (spansFound > 1000 && effectiveFlushMs < 10000) {\n process.stderr.write(\n `Warning: ${spansFound} spans queued but --flush-timeout-ms is ` +\n `${effectiveFlushMs} ms (< 10 000 ms). Large batches may not fully ` +\n `flush before the deadline. Recommended: --flush-timeout-ms 30000\\n`,\n );\n }\n\n // ── Emit via OtelEmitter ──────────────────────────────────────────────────\n const emitter = new OtelEmitter({\n endpoint,\n serviceName: options.serviceName,\n });\n\n let spansEmitted = 0;\n for (const mapped of mappedSpans) {\n try {\n emitter.emit(mapped);\n spansEmitted++;\n } catch {\n // fail-open: individual span failures are non-fatal\n }\n }\n\n // Flush pending batch before reporting success.\n // Race against effectiveFlushMs so that an unreachable OTLP endpoint\n // never causes the CLI to hang indefinitely. OtelEmitter.shutdown() already\n // swallows its own errors, so the race is purely a time-bound escape hatch.\n // Default 2 000 ms preserves vitest idempotency-test compatibility (unreachable\n // endpoints resolve immediately via connection-refused; the timeout is never hit\n // in tests). For production use with > 5 000 spans pass --flush-timeout-ms 30000.\n await Promise.race([\n emitter.shutdown(),\n new Promise<void>((resolve) => setTimeout(resolve, effectiveFlushMs)),\n ]);\n\n const result = { spansFound, spansEmitted, dryRun: false };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * ingest-vendor-otel handler — Canonical Projection of vendor OTEL into the Ledger.\n *\n * observability-integration.md §9.2 (T-D2): backfill Claude Code (vendor) OTEL\n * token/cost/trace exports into the SQLite Ledger so they become queryable\n * Canonical Events. The Ledger remains the canonical source of truth; vendor OTEL\n * is auxiliary evidence (\"OTEL は補助証跡。正本は Canonical Event\").\n *\n * ## Idempotency (§9.2)\n *\n * Every projected event carries a deterministic dedup_key\n * (`vendor-otel:{traceId}:{spanId}`). SqliteSink enforces UNIQUE(dedup_key) via\n * INSERT OR IGNORE, so re-running this command on the same export is a true no-op:\n * already-present rows are reported as `skippedDuplicate`. Hook/runtime records\n * (dedup_key = NULL) are the primary source and are never overwritten.\n *\n * Reads the OTLP/JSON trace export from --input (a file path) or stdin. Writes\n * directly to the SQLite Ledger (no OTLP re-emit — vendor spans already live in\n * their own pipeline), which makes the projection inherently fail-open with\n * respect to ClickHouse availability.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { SqliteSink, DEFAULT_DB_PATH } from \"../index.js\";\nimport {\n mapVendorOtelExport,\n VENDOR_OTEL_SOURCE,\n} from \"../normalize/vendor-otel-mapper.js\";\n\n// ── Handler options ───────────────────────────────────────────────────────────\n\nexport interface IngestVendorOtelOptions {\n /** SQLite database path. Defaults to DEFAULT_DB_PATH. */\n db?: string;\n /** Path to the vendor OTLP/JSON trace export. Reads stdin when omitted. */\n input?: string;\n /** `source` tag applied to projected events. Default: 'claude-code-otel'. */\n source?: string;\n /** Parse + project without writing to the Ledger (prints counts only). */\n dryRun?: boolean;\n}\n\n/** Result printed to stdout as JSON. */\nexport interface IngestVendorOtelResult {\n /** Vendor spans projected to CanonicalEvents. */\n found: number;\n /** New rows written to the Ledger. */\n inserted: number;\n /** Rows already present (dedup_key collision) — idempotent re-run evidence. */\n skippedDuplicate: number;\n /** Vendor spans skipped for lacking a stable trace/span identity. */\n skippedInvalid: number;\n /** True when --dry-run was set (no writes performed). */\n dryRun: boolean;\n}\n\n// ── stdin helper ───────────────────────────────────────────────────────────────\n\n/** Read the whole stdin stream as a UTF-8 string (\"\" on TTY / error). */\nasync function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n return new Promise<string>((resolve) => {\n let data = \"\";\n let settled = false;\n const finish = (): void => {\n if (settled) return;\n settled = true;\n resolve(data);\n };\n try {\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", finish);\n process.stdin.on(\"error\", finish);\n process.stdin.on(\"close\", finish);\n } catch {\n finish();\n }\n });\n}\n\n// ── Handler ───────────────────────────────────────────────────────────────────\n\n/**\n * Handle `aaac-observ ingest-vendor-otel`.\n *\n * Prints a JSON summary to stdout:\n * { found, inserted, skippedDuplicate, skippedInvalid, dryRun }\n *\n * On error (unreadable input / invalid JSON / DB failure): writes to stderr and\n * exits with code 1.\n */\nexport async function handleIngestVendorOtel(\n options: IngestVendorOtelOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const dryRun = options.dryRun ?? false;\n const source = options.source ?? VENDOR_OTEL_SOURCE;\n\n // ── Read input (file or stdin) ─────────────────────────────────────────────\n let raw: string;\n try {\n raw = options.input ? await readFile(options.input, \"utf8\") : await readStdin();\n } catch (err) {\n process.stderr.write(\n `Error: failed to read input — ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n return;\n }\n\n if (raw.trim() === \"\") {\n process.stderr.write(\n \"Error: no input — provide --input <file> or pipe an OTLP/JSON trace export via stdin\\n\",\n );\n process.exit(1);\n return;\n }\n\n // ── Parse OTLP/JSON ────────────────────────────────────────────────────────\n let doc: unknown;\n try {\n doc = JSON.parse(raw);\n } catch (err) {\n process.stderr.write(\n `Error: invalid JSON input — ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n return;\n }\n\n // ── Project to CanonicalEvents ─────────────────────────────────────────────\n const { events, skipped: skippedInvalid } = mapVendorOtelExport(doc, { source });\n const found = events.length;\n\n if (dryRun) {\n const result: IngestVendorOtelResult = {\n found,\n inserted: 0,\n skippedDuplicate: 0,\n skippedInvalid,\n dryRun: true,\n };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n return;\n }\n\n // ── Write to the Ledger (idempotent via dedup_key) ─────────────────────────\n const sink = new SqliteSink(dbPath);\n let inserted = 0;\n let skippedDuplicate = 0;\n try {\n for (const event of events) {\n if (sink.write(event)) {\n inserted += 1;\n } else {\n skippedDuplicate += 1;\n }\n }\n } catch (err) {\n sink.close();\n process.stderr.write(\n `Error: failed to write to Ledger — ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exit(1);\n return;\n }\n sink.close();\n\n const result: IngestVendorOtelResult = {\n found,\n inserted,\n skippedDuplicate,\n skippedInvalid,\n dryRun: false,\n };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n}\n","/**\n * report handler — Observability 診断レポート生成 (#179 WS-F T-F1).\n *\n * Reads the local SQLite Ledger via SqliteQueryAdapter and renders the Layer 1–3\n * / link-completion / Person-Tier diagnostic as Markdown (default) or JSON.\n *\n * Mirrors the metric formulas of the `aaac-diagnostic` Grafana dashboard\n * (docker/observability/analysis-views.sql) so the CLI report and the dashboard\n * agree. Optionally produces a Before/After comparison when a baseline window is\n * supplied.\n *\n * Follows the handleQuery pattern: required-arg validation runs before the\n * adapter is opened, and a fresh/empty database degrades gracefully to an empty\n * report rather than an error.\n */\nimport { SqliteQueryAdapter, DEFAULT_DB_PATH } from \"../index.js\";\nimport {\n generateDiagnosticReport,\n renderReportMarkdown,\n renderReportJson,\n type DiagnosticReport,\n type DiagnosticReportOptions,\n} from \"../analysis/diagnostic-report.js\";\n\nexport interface ReportHandlerOptions {\n projectId?: string;\n from?: string;\n to?: string;\n baselineFrom?: string;\n baselineTo?: string;\n format?: string;\n db?: string;\n}\n\n/** An empty report shaped like generateDiagnosticReport output (for fresh DBs). */\nfunction emptyReport(options: ReportHandlerOptions): DiagnosticReport {\n const report: DiagnosticReport = {\n generatedAt: new Date().toISOString(),\n projectId: options.projectId ?? null,\n period: { from: options.from ?? null, to: options.to ?? null },\n metrics: {\n layer1: [],\n layer2: [],\n layer3: [],\n linkCompletion: { sessionLinkRate: [], stageArrival: [], provisional: [] },\n tierRatio: [],\n summary: {\n issueCount: 0,\n sessionCount: 0,\n totalCostUsd: 0,\n avgLeverage: 0,\n avgCommitRate: 0,\n avgCiPassRate: 0,\n avgChainCompleteness: 0,\n sessionLinkRate: 0,\n tierCounts: { high: 0, mid: 0, low: 0 },\n },\n },\n };\n return report;\n}\n\n/**\n * Handle `aaac-observ report`.\n *\n * On success: writes the rendered report (Markdown or JSON) to stdout.\n * On error: writes to stderr and exits with code 1.\n */\nexport async function handleReport(\n options: ReportHandlerOptions,\n _parentOpts: Record<string, unknown>,\n): Promise<void> {\n const validFormats = [\"markdown\", \"json\"] as const;\n type Format = (typeof validFormats)[number];\n\n // ── Validate options (before opening the adapter) ─────────────────────────\n if (options.format !== undefined && !validFormats.includes(options.format as Format)) {\n process.stderr.write(`Error: --format must be one of: ${validFormats.join(\"|\")}\\n`);\n process.exit(1);\n }\n const format: Format = (options.format as Format) ?? \"markdown\";\n\n const render = (report: DiagnosticReport): string =>\n format === \"json\" ? renderReportJson(report) : renderReportMarkdown(report);\n\n // ── Open adapter ──────────────────────────────────────────────────────────\n const dbPath = options.db ?? DEFAULT_DB_PATH;\n const adapter = new SqliteQueryAdapter(dbPath);\n\n try {\n const reportOptions: DiagnosticReportOptions = {};\n if (options.projectId !== undefined) reportOptions.projectId = options.projectId;\n if (options.from !== undefined) reportOptions.from = options.from;\n if (options.to !== undefined) reportOptions.to = options.to;\n if (options.baselineFrom !== undefined) reportOptions.baselineFrom = options.baselineFrom;\n if (options.baselineTo !== undefined) reportOptions.baselineTo = options.baselineTo;\n\n const report = generateDiagnosticReport(adapter, reportOptions);\n process.stdout.write(render(report) + \"\\n\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n\n // Fresh/empty database: no tables yet → emit an empty report, not an error.\n if (msg.includes(\"no such table\") || msg.includes(\"no such column\")) {\n process.stdout.write(render(emptyReport(options)) + \"\\n\");\n adapter.close();\n return;\n }\n\n process.stderr.write(`Error: ${msg}\\n`);\n adapter.close();\n process.exit(1);\n }\n\n adapter.close();\n}\n","/**\n * CLI command handlers for aaac-observ.\n *\n * Implements the CommandHandlers interface generated by cli-contracts and\n * delegates to the domain-specific handler modules.\n *\n * Usage (in index.ts):\n * import { handlers } from './handlers.js';\n * import { createProgram } from '../generated/program.js';\n * const program = createProgram(handlers, version);\n * await program.parseAsync();\n */\nimport type { CommandHandlers } from \"../generated/program.js\";\nimport { handleRecord } from \"./record.js\";\nimport { handleRecordHook } from \"./record-hook.js\";\nimport { handleQuery } from \"./query.js\";\nimport { handleBackfill } from \"./backfill.js\";\nimport { handleIngestVendorOtel } from \"./ingest-vendor-otel.js\";\nimport { handleReport } from \"./report.js\";\n\nexport const handlers: CommandHandlers = {\n record: handleRecord,\n recordHook: handleRecordHook,\n query: handleQuery,\n backfill: handleBackfill,\n ingestVendorOtel: handleIngestVendorOtel,\n report: handleReport,\n};\n"],"mappings":"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","names":["handlers","program","result","resolve","resolve","result"]}