Skip to content

[G1] Merge ai-hist-core and ai-hist-engine into one published ai-hist crate with feature gates, a release pipeline and a semver policy #162

Closed

Description

@willwashburn

Part of #160 (group 1 — independent).

Problem

burn is a Rust workspace (relayburn-sdk is published to crates.io) and can only depend on relayhistory from Rust. relayhistory's Rust crates are workspace-internal and split in a way that helps nobody:

  • crates/ai-hist-core/Cargo.toml — ai-hist-core 0.1.0, never released.
  • crates/ai-hist/Cargo.toml — package ai-hist-engine 0.2.0 (lib ai_hist_engine), never released.
  • Every consumer already depends on both: crates/ai-hist-cli/Cargo.toml:13-14, crates/ai-hist-napi/Cargo.toml:13-14, plugins/provider-sources/rust/Cargo.toml:10-11, plugins/relayhistory/rust/Cargo.toml:15-16. Nothing consumes core alone.
  • The layering story is already broken: core holds parse_claude / parse_codex / parse_cursor_text (crates/ai-hist-core/src/lib.rs:1123-1175) and the OpenCode sync (:2124-2222), while the main Claude/Codex parsers live in engine. It is not "storage vs parsing"; it is two halves of one library.
  • Public surface is ~250 pub items across the two crates, and 47 functions in core take a raw rusqlite::Connection — an embedder is handed the database driver and a bag of free functions.
  • docs/releasing.md describes only the npm pipeline (.github/workflows/publish.yml, name bound to npm OIDC trusted publishing — must not be renamed). There is no cargo publish step anywhere.
  • All four candidate names are free on crates.io (ai-hist, relayhistory, ai-hist-core, ai-hist-engine return 404 today).

Both workspaces already agree on rusqlite = 0.32 with bundled, so linking is feasible without a version bump on either side.

Decision

Publish one crate, ai-hist (matching the npm package name), formed by merging crates/ai-hist-core and crates/ai-hist into crates/ai-hist. ai-hist-cli and ai-hist-napi stay as unpublished workspace members depending on it (publish = false). The two plugin Rust crates likewise depend on ai-hist only.

Scope

  1. Merge the crates. Move ai-hist-core/src/{storage,observations,relationships,source_evidence,privacy,delivery} under crates/ai-hist/src/ as modules; fold ai-hist-core/src/lib.rs into crates/ai-hist/src/store/ (schema, open/init, migrations, queries). Delete crates/ai-hist-core. Update the four dependents' Cargo.toml. cargo test --workspace must be green with no test body changes beyond use paths — this is a move, not a rewrite.
  2. Tighten visibility. Everything that takes a &Connection becomes pub(crate). The only public entry point is the SessionStore facade from [G2] SessionStore facade: the single public entry point (seven methods, typed evidence, no raw connections, no contract constant) #178 (that issue defines the methods; this one makes the rest private so it can). Where the CLI/napi/plugins currently call connection-level functions, route them through the facade or through a #[doc(hidden)] pub mod internal gated behind a unstable-internal feature the workspace enables and external consumers do not. Check with cargo public-api that the default-feature surface is SessionStore, the evidence structs, the error type, and the Source enum only.
  3. Feature gates, default off: delivery (the durable delivery coordinator — 37 public items on its own, only the cloud plugin needs it), opencode-backup (the whole-DB Connection::backup path; see [G1] OpenCode: event-level parity across both storage layouts (SQLite opencode.db and legacy JSON storage/), with tokens, model/provider, parts, step-finish, compaction, parentID #168), git-hooks (install_git_hooks / link_git_commit). The napi and CLI crates enable what they need. Verify cargo tree -e features -p ai-hist --no-default-features pulls no tempfile/fs2/url unless a feature asks for it.
  4. Make it publishable. description, license (MIT), repository, readme, keywords, categories; workspace rust-version (MSRV) verified with cargo +<msrv> check; cargo publish --dry-run -p ai-hist on CI.
  5. Release pipeline. Add a publish-crate job to .github/workflows/publish.yml (do not rename the workflow) after the npm core publish, using crates.io trusted publishing (OIDC) or CARGO_REGISTRY_TOKEN; idempotent when the version already exists (skip, don't fail); honour dry_run.
  6. Versioning policy in docs/releasing.md: the crate shares the npm version line (0.18.x today) so one release tag means one artefact set. Cargo semver is the Rust contract — there is no separate Rust contract-version constant. Any change to a public struct field, enum variant, or behaviour a consumer observes is a minor bump pre-1.0 and gets a ### Rust API entry in CHANGELOG.md; a cargo public-api diff in CI (see [G2] Embedder guide, example consumer crate, and a CI job that builds an out-of-tree Rust consumer against the published crates #182) enforces the changelog entry.
  7. Docs. docs/releasing.md "Rust crate" section; README.md two-line "Embedding from Rust" pointer; docs/architecture.md call graph updated (one Rust box, not two).

Acceptance

  • crates/ai-hist-core no longer exists; cargo test --workspace green.
  • cargo public-api -p ai-hist (default features) lists SessionStore, its methods, the evidence structs, Source, Error, and nothing that mentions rusqlite.
  • cargo publish --dry-run -p ai-hist passes on CI; a tagged release publishes it; cargo add ai-hist in a scratch crate outside the workspace compiles SessionStore::open(Default::default())?.sync(Default::default())?.
  • cargo build -p ai-hist --no-default-features compiles without the delivery module.

Out of scope

The facade method set (#178); any parser change.

Activity

  1. changed the title [-][G1] Publish ai-hist-core and ai-hist-engine to crates.io with a release pipeline and semver policy[/-] [+][G1] Merge ai-hist-core and ai-hist-engine into one published `ai-hist` crate with feature gates, a release pipeline and a semver policy[/+] on Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions