Architecture¶
How OmniDevX is structured today: the modules, the data flow from harness storage to reports, and the contracts each layer keeps. This page tracks current behavior; for planned changes see the specs.
Module Layout¶
OmniDevX is split across modules so that heavy dependencies (SQLite, the GitHub API client) stay out of the core, and provider-specific storage handling lives with the provider that owns it.
| Module | Role | Contents |
|---|---|---|
github.com/plexusone/omnidevx-core |
Canonical IR and thin collectors | Event model, Collector contract, store, report, identity; stdlib-only Claude Code and Git collectors; OpenTelemetry receiver |
github.com/plexusone/omni-openai/omnidevx |
Codex CLI collector | Reads ~/.codex SQLite state and rollout JSONL |
github.com/plexusone/omni-aws/omnidevx |
Kiro CLI collector | Reads the Kiro CLI database and optional session archives |
github.com/plexusone/omni-github/omnidevx |
GitHub collector | REST and GraphQL contribution data |
github.com/plexusone/omnidevx (this repo) |
Batteries-included distribution | Re-exports core types and provider constructors, Engine, NewDefault, the omnidevx CLI |
Boundary rules:
- Canonical types, contracts, and stdlib-only collectors belong in
omnidevx-core. - Storage-format handling for a vendor's tool belongs in that vendor's provider module, which is the source of truth for its schema drift.
- This repo composes; it does not parse harness storage itself.
Data Flow¶
Harness storage / APIs Collectors Canonical IR
────────────────────── ────────── ────────────
~/.claude/projects/*.jsonl ──→ claudecode (core) ──┐
~/.codex/state_N.sqlite ──→ omni-openai/omnidevx ──┤
~/.codex/sessions/…jsonl │
Kiro CLI database ──→ omni-aws/omnidevx ──┼─→ []Event ─→ store ─→ report
git repositories ──→ git (core) ──┤ (metadata (JSONL) (daily,
GitHub API ──→ omni-github/omnidevx ──┘ only) period)
Engine runs the collectors; the CLI's collect command writes their
events to the store. Reporting reads events back from the store.
Collector Contract¶
Every source implements Collector from omnidevx-core:
type Collector interface {
Source() Source
Collect(ctx context.Context, req CollectRequest) (*CollectionResult, error)
}
- A
CollectRequestcarries a half-openPeriodand an optionalSubjectRef, which the collector stamps on every event. - Collectors normalize only. They convert provider-native records into canonical events and never compute framework metrics.
- Damaged or unparseable records become
Diagnosticentries in the result rather than being dropped silently. Only failures that prevent reading the source at all are returned as errors.
Event Model¶
Event is the canonical observation:
| Field | Meaning |
|---|---|
ID |
Deterministic per source record, so re-collecting the same history deduplicates |
Type |
ai.* for coding-agent activity (sessions, prompts, messages, tools, patches, usage); devx.* for development work (commits, contributions, profiles) |
Timestamp |
When the activity happened |
Subject |
Whose activity it is (PersonID, plus local account and device for later identity resolution) |
Source |
Canonical provider and product names (e.g. anthropic / claude-code) |
Context |
Session ID, repository, workspace, git branch |
Attributes |
Metadata keyed by shared Attr* constants (model, token counts, tool, duration, commit stats, cost) |
Provenance |
Collection mode (history, otel, hooks, api, survey) and confidence in [0, 1] |
Providers use the shared attribute keys rather than ad-hoc names so metrics can be computed across sources. Telemetry JSON uses snake_case attribute keys; the event envelope uses camelCase field names.
Privacy Contract¶
Canonical events carry metadata only: event types, timestamps, durations, counts, models, token figures, and repository identifiers. Prompt text, model responses, and file contents are never captured. Every collector, including those in provider modules, must preserve this.
Event Store¶
omnidevx-core/store persists events as daily JSONL files:
- Inspectable: plain text, so a developer can read exactly what has been recorded about them.
- Reprocessable: reports are recomputed from stored events when metric formulas change.
- Idempotent: an event whose ID already exists in its day file is skipped, so overlapping collection runs are safe.
- Private: files are created with owner-only permissions.
- Damage-tolerant: unreadable lines surface as diagnostics on read.
Reports¶
omnidevx-core/report builds daily summaries from events and rolls them up
into a per-developer period report (metrics, source coverage, data
quality). Token cost is estimated from an embedded, versioned model pricing
table when a source does not report cost directly.
Engine¶
Engine composes collectors by constructor injection; there is no global
registry.
New(...)andAdd(...)assemble the collector set.NewDefault()builds the collectors that work with no configuration: Claude Code, Codex CLI, and Kiro CLI, reading their default local stores. Git (needs repository roots) and GitHub (needs credentials) are added explicitly.Collectruns every collector with the same request. One collector failing does not stop the others: successful results are returned together with a joined error naming each failing source.Eventsflattens results into one slice, preserving collector order.
This repo's tests check engine composition, failure isolation, and context cancellation, and run the Claude Code, Codex CLI, and Kiro CLI constructors against empty stores. Claude Code reports an error when its projects directory is missing; Codex and Kiro return empty results.
CLI¶
cmd/omnidevx is a thin adapter over the library:
omnidevx collect --person <id> --since <date> --until <date>runsNewDefault()over the period and writes events to the store (--storeoverrides the root;--dry-runskips the write).omnidevx versionprints the version.
Adding a Collector¶
- Implement
Collectorin the module that owns the source:omnidevx-coreif it needs only the standard library, otherwise the vendor's provider module. - Emit canonical event types and shared attribute keys, with deterministic
IDs and honest
Provenance. - Re-export the constructor and options from
omnidevx.go, and add it toNewDefault()only if it works without configuration. - Extend the constructor test in
engine_test.goand document it in Collectors.
Related Specs¶
- Session Recovery
(
INIT-OMNIDEVX-001, planned): adds a session catalog that reads harness sessions under a separate content-access contract, alongside the metadata-only event stream described here.