Skip to main content

Normalize coding-agent transcripts into deterministic Trajectory contracts

Project description

hypabolic-trajectory

Normalize coding-agent session transcripts into deterministic, versioned Trajectory contracts. Independent native Python 3.11+ runtime of the Hypabolic Trajectory product — same wire contracts and shared conformance suite as .NET, TypeScript, and Rust. No FFI to other language runtimes.

PyPI name hypabolic-trajectory (org Hypabolic)
Import root hypabolic_trajectory
Requires-Python >=3.11
Normalizer contract 0.2.0 (identity-stable; embedded in canonical output)
Wire package version WIRE_PACKAGE_VERSION — tip pin "0.1.0" (must match peer runtimes + goldens on the same tag; not auto-bound to package SemVer)
License MIT

Published vs tree: NuGet / npm / crates 0.1.0 already ship without AHP. The first public PyPI cut of this package ships on the next synchronized multi-registry tag (with tip sources including AHP Shape A). Until that tag, install from this monorepo (editable) or wait for the tag.


Package map

Dist / extra Import Role
hypabolic-trajectory (core wheel) hypabolic_trajectory Normalize, project (including pure project_otel_genai), listing, bundled contracts + runtime-capabilities.json, and hypabolic_trajectory.otel (SpanSetSink / emit_to, no SDK)
hypabolic-trajectory[otel] same hypabolic_trajectory.otel Optional OpenTelemetry SDK sink helpers + opentelemetry-* deps only. Does not gate pure projection or emit_to with a pure sink.
Conformance runner not published python/tools/trajectory_conformance — stdin/stdout protocol v1 for conformance/verify.py
Sample CLI not published python/samples/trajectory_cli — browse/list/show (no console script)

Cross-ecosystem map (same product):

Ecosystem Core Optional
.NET Hypabolic.Trajectory .OpenTelemetry, .Testing
TypeScript @hypabolic/trajectory @hypabolic/trajectory-node, @hypabolic/trajectory-otel
Rust hypabolic-trajectory hypabolic-trajectory-opentelemetry
Python hypabolic-trajectory [otel] SDK sinks only

Install

# After first public tag (replace with the tag SemVer):
pip install hypabolic-trajectory==<tag-semver>
pip install 'hypabolic-trajectory[otel]==<tag-semver>'   # optional SDK sinks

# From this monorepo (development):
python -m pip install -e './python[dev]'

The published wheel has no console scripts. The conformance runner and sample CLI are monorepo-only.

Sample CLI (unpublished)

# After editable install of the core package:
PYTHONPATH=python/samples python -m trajectory_cli list --source pi
PYTHONPATH=python/samples python -m trajectory_cli show \
  --source pi \
  --path conformance/cases/pi/tool-calls/input.jsonl
PYTHONPATH=python/samples python -m trajectory_cli browse

See samples/trajectory_cli/README.md.


Supported public imports (semver-stable)

Only these paths are supported:

  1. Package root hypabolic_trajectory — names in root __all__
  2. hypabolic_trajectory.ir — multi-project IR surface (ir.__all__)
  3. hypabolic_trajectory.otel — always importable from the core wheel

Any other module path (api, engine, normalize, sources, …) is unsupported and may break without notice.

from hypabolic_trajectory import (
    # free functions
    normalize_to_ir,
    normalize_to_letta,
    normalize_to_canonical,
    normalize_to_hypabolic,
    project_letta,
    project_canonical,
    project_hypabolic,
    project_openai,
    project_minimal_jsonl,
    project_otel_genai,
    list_trajectories,
    serialize_projection,
    canonical_json,
    # engine
    TrajectoryEngine,
    # request / errors
    NormalizeRequest,
    SourceContext,
    NormalizeOptions,
    Bounds,
    Filters,
    TrajectoryError,
    Diagnostic,
    TrajectorySource,
    # versions
    NORMALIZER_CONTRACT_VERSION,  # "0.2.0"
    WIRE_PACKAGE_VERSION,         # tip "0.1.0" until coordinated bump
    PACKAGE_VERSION,
    __version__,
)
from hypabolic_trajectory.ir import TrajectoryIR  # also re-exported at root
from hypabolic_trajectory.otel import SpanSetSink, emit_to

OTEL import matrix

Import / call Without opentelemetry-* With [otel] extra
from hypabolic_trajectory import project_otel_genai Succeeds Succeeds
from hypabolic_trajectory.otel import SpanSetSink, emit_to Succeeds Succeeds
emit_to(pure_sink, ir) pure project + sink.emit; no SDK Same
Concrete SDK helper symbols (if shipped) ImportError with install hint Succeeds when deps present
Whole hypabolic_trajectory.otel package Always in the core wheel Same

Quick usage

from pathlib import Path
from hypabolic_trajectory import (
    NormalizeRequest,
    TrajectorySource,
    list_trajectories,
    normalize_to_ir,
    project_hypabolic,
    project_letta,
    project_otel_genai,
    serialize_projection,
    TrajectoryEngine,
)

# Listing always takes an explicit root (no home default in library APIs).
page = list_trajectories(
    source=TrajectorySource.CLAUDE_CODE,
    root=Path.home() / ".claude" / "projects",
    limit=20,
)
session = page.items[0]

transcript = Path(session.path).read_bytes()
request = NormalizeRequest(
    source=TrajectorySource.CLAUDE_CODE,
    transcript=transcript,
)

ir = normalize_to_ir(request)
hypabolic = project_hypabolic(ir)
messages = project_letta(ir)
spans = project_otel_genai(ir)  # pure; no OTEL SDK required

# Product JSON emit (shared Trajectory string-escape; never stdlib json.dumps
# for identity/product schemas):
wire = serialize_projection(hypabolic)

# Engine (tip matrix including pure otel); free functions ignore engine mutations.
engine = TrajectoryEngine.create_default()
out = engine.project(ir, "hypabolic-trajectory-v1")

Filters (normalize options)

NormalizeOptions.filters controls content inclusion during normalization:

Field Values Default Effect
filters.tool_results "include" | "omit" "include" When "omit", tool-result record bodies are dropped while structure/diagnostics remain policy-correct

Bounds (options.bounds.tool_arguments / tool_results) cap argument/result character lengths with documented truncation strategies. Domain validation of options runs at free-function / engine entry (DTO construction does not raise TrajectoryError).

from hypabolic_trajectory import (
    Filters,
    NormalizeOptions,
    NormalizeRequest,
    TrajectorySource,
    normalize_to_ir,
)

request = NormalizeRequest(
    source=TrajectorySource.PI,
    transcript=b"...",
    options=NormalizeOptions(filters=Filters(tool_results="omit")),
)
ir = normalize_to_ir(request)

Dual timestamps

Source adapters and the normalizer preserve dual timing when present (never fabricated):

Clock Field Role
Millisecond timestamp_ms (int) Filled / synthesized body clock used for public message, canonical, Hypabolic, and jsonl-minimal timestamps
Precise timestamp_precise (str, optional) Source-native high-resolution string when present; copied onto IR records and model invocations

Public timestamp formats (filled timestamp_ms only — never source_timestamp_ms):

Surface Format
Message / canonical / Hypabolic yyyy-MM-ddTHH:mm:ss.fffZ
Listing updated_at Z form
jsonl-minimal yyyy-MM-ddTHH:mm:ss.fff+00:00 (three fractional digits; omit key when filled ms is absent)
OTEL span bounds precise as-is; else ms → seven-digit pad (Z0000+00:00)

Identity formulas and string escape

Authority: contracts/spec/identity.md, contracts/spec/canonical-json.md, and peer goldens. Digests are 64 lowercase hex.

Product field Formula
Hypabolic trajectory_id sha256(utf8(compact_json([source_wire_name, group_id])))
Model-invocation id sha256(utf8(compact_json([group_id, identity, "model-invocation"])))
Model-invocation absolute offset checked decoded.source_offset + base_byte_offset (signed int64; overflow → invalid_input)
segment.partial config.partial or base_byte_offset != 0

Trajectory string-escape (shared by canonical_json, serialize_projection, and each project_minimal_jsonl line):

  1. Walk the string as UTF-16 code units.
  2. Short escapes for ", \\, \b, \t, \n, \f, \r.
  3. \uXXXX four uppercase hex for U+0000–001F, U+E000–F8FF, U+2028, U+2029, and surrogates; else UTF-8.
  4. Do not escape solidus /; no Unicode normalization; no BOM.

Only identity hashing sorts object keys by UTF-16 code unit order via canonical_json. Product serialize_projection preserves insertion order.


Capabilities and sources

Advertised surface is the tip matrix in python/runtime-capabilities.json (must equal contracts/compatibility.json and peer TS/Rust manifests at ship):

  • Sources: pi, claude-code, codex, openclaw, hermes, ahp (Shape A offline ChatState snapshot; listing Phase 3 stub)
  • Outputs: letta-trajectory-v1, letta-canonical-v1, hypabolic-trajectory-v1, openai-chat-messages, jsonl-minimal, otel-genai-spans-v1
  • Capabilities: normalize, normalize-partial, list-explicit-root, typed-diagnostics, typed-fatal-errors, deterministic-rerun
  • Slice: ML13 (historical id; AHP is a post-0.1.0 source addition on tip)

Do not treat IMPLEMENTED_SOURCES as a registry claim — the capabilities file is authoritative for CI and advertising.


Conformance (filtered runner argv)

Protocol v1 runner (unpublished): python/tools/trajectory_conformance.

# Editable install + unit tests
python -m pip install -e './python[dev]'
python -m pytest python/tests -q

# Tip suite (unfiltered verify defaults to compatibility tip set):
python conformance/verify.py --repository-root . -- \
  env PYTHONPATH=python/src:python/tools python -m trajectory_conformance

# Progressive / filtered argv from claimed capabilities (fail-closed when ⊂ tip):
python conformance/verify.py --repository-root . \
  $(python tools/conformance_argv_from_capabilities.py --repository-root .) \
  -- \
  env PYTHONPATH=python/src:python/tools python -m trajectory_conformance

# Manual filters while iterating:
python conformance/verify.py --repository-root . \
  --source pi --operation normalize-letta --operation normalize-canonical -- \
  env PYTHONPATH=python/src:python/tools python -m trajectory_conformance

When claimed sources/outputs are a proper subset of tip, CI must pass explicit --source / --operation filters (generator: tools/conformance_argv_from_capabilities.py). Never run unfiltered verify while claimed ⊂ tip.

Identity baseline:

sha256sum --check conformance/identity-baseline.sha256

Version pins

Symbol / field Value / rule
NORMALIZER_CONTRACT_VERSION "0.2.0" — wire identity contract
WIRE_PACKAGE_VERSION "0.1.0" tip pin until all runtimes + goldens move together
PACKAGE_VERSION / __version__ From installed dist metadata (stamp-synced with root VERSION)
Canonical normalizer_version Contract version 0.2.0 (never package SemVer)
Hypabolic normalizer.version WIRE_PACKAGE_VERSION
OTEL instrumentation_version WIRE_PACKAGE_VERSION

Development

From the monorepo root:

python -m pip install -e './python[dev]'
python -m pytest python/tests -q

Implementation authority: docs/python-implementation-spec.md, contracts/, and conformance/. Status: docs/python-impl-status.md.

License

MIT — Copyright 2026 Hypabolic

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hypabolic_trajectory-0.1.1.tar.gz (74.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hypabolic_trajectory-0.1.1-py3-none-any.whl (108.7 kB view details)

Uploaded Python 3

File details

Details for the file hypabolic_trajectory-0.1.1.tar.gz.

File metadata

  • Download URL: hypabolic_trajectory-0.1.1.tar.gz
  • Upload date:
  • Size: 74.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for hypabolic_trajectory-0.1.1.tar.gz
Algorithm Hash digest
SHA256 704311efc598bd7b72aaae7fa9b96a9b049301d5a47304156714396255aad2aa
MD5 07bad90969b862ec079b14ce12be41c1
BLAKE2b-256 3f11510f6f1fec2e12dce353a1c5a3e7772bbdd4ccc04df911db9efdac9420e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypabolic_trajectory-0.1.1.tar.gz:

Publisher: release.yml on Hypabolic/Trajectory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hypabolic_trajectory-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for hypabolic_trajectory-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 407c25343d4d3c85cf927ca4e1063d0c09443cc022f416da9b18f013690f914a
MD5 5f3bd4dadd97c57963272eeb39522153
BLAKE2b-256 3df36884abc03bff3d5441e153f86089812b048f06878154832c801104a11b64

See more details on using hashes here.

Provenance

The following attestation bundles were made for hypabolic_trajectory-0.1.1-py3-none-any.whl:

Publisher: release.yml on Hypabolic/Trajectory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page