Skip to content

Embedded mode foundation (Plan 1)#12

Draft
knutties wants to merge 21 commits into
mainfrom
feat/embedded-mode
Draft

Embedded mode foundation (Plan 1)#12
knutties wants to merge 21 commits into
mainfrom
feat/embedded-mode

Conversation

@knutties

@knutties knutties commented Apr 29, 2026

Copy link
Copy Markdown
Collaborator

This PR lands the foundation for Kronos embedded mode: design spec plus Plan 1 implementation. Worker extraction (Plan 2) is the follow-up, stacked on top of this branch.

Stacking

  • This PRfeat/embedded-modemain. Design spec + Plan 1 (Foundation).
  • Next PR (coming)feat/worker-extractionfeat/embedded-mode. Plan 2 (Worker extraction).

What's in this PR

Design spec (docs/superpowers/specs/2026-04-29-kronos-embedded-mode-design.md) — captures the full embedded-mode initiative.

Plan 1: Foundation (docs/superpowers/plans/2026-04-29-plan-1-foundation.md) — groundwork that does NOT change service-mode behavior:

  • Two new empty crates wired into the workspace: kronos-client, kronos-embedded-worker.
  • SchemaConfig value type in kronos-common (system_schema, tenant_schema_prefix).
  • Migrations parameterized on {{system_schema}} placeholders. Service default "public" preserves byte-identical behavior.
  • Migration template renderer + embedded migration list + apply() entry point in kronos-common.
  • Public kronos_client::migrate(&pool, &opts) API and kronos-migrate CLI binary (clap).
  • SchemaConfig plumbed through AppConfig from TE_SYSTEM_SCHEMA / TE_TENANT_SCHEMA_PREFIX env vars.
  • justfile db-migrate recipe now invokes kronos-migrate instead of raw psql.
  • One incidental fix: apply() now splits SQL on statement boundaries (respecting $$ ... $$ and '...' quoting) so CREATE INDEX CONCURRENTLY works under PostgreSQL's simple-query protocol. Has unit-test coverage.
  • Smoke test (crates/client/tests/migrate_kronos_namespace.rs) verifies non-default system_schema = "kronos_smoke" lands tables in the configured namespace.

What does not change in this PR

  • HTTP request/response shapes for any endpoint.
  • DB schema (only the rendering of it).
  • TE_* environment variable contract for the binaries (only adds two new ones with defaults that preserve today).
  • Prometheus metric names, labels, ports.
  • Smithy SDK contract.

Test plan

  • cargo build --workspace — green
  • cargo test -p kronos-common — green (includes 8 split_statements unit tests)
  • cargo test -p kronos-client --test migrate_kronos_namespace -- --ignored — passes against live DB (kronos_smoke namespace)
  • just db-reset — all four migrations apply cleanly with default config
  • Reviewer to confirm spec direction still aligns
  • just test-e2e regression check (deferred — recipe references kronos-scheduler crate not on this branch; pre-existing branch state)

Review focus

  • Crate boundary between kronos-client and kronos-embedded-worker (the latter is still empty in this PR — Plan 2 fills it).
  • SchemaConfig value type and the {{system_schema}} template approach.
  • Whether the bundled split_statements fix should be a separate PR — happy to split if preferred.

🤖 Generated with Claude Code

Captures the brainstormed design for exposing Kronos's job-management
functionality as Rust library crates (kronos-client + kronos-embedded-worker)
that host applications embed directly, removing the need to operate the API
server and worker as separate deployable services.

Key decisions: Rust-only for v1; single-tenant ergonomics via workspace
pinning while preserving the schema-per-workspace DB layout; pg_cron remains
required for CRON triggers; service crates refactored to sit on top of the
new library crates so there is one code path for both modes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ad633bc-56ec-4b9a-ba5d-8ee3cb46a376

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/embedded-mode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

knutties and others added 20 commits April 29, 2026 14:07
Adds two cross-cutting sections raised on the draft PR:

- Schema namespacing: parameterize migrations and runtime SQL on
  system_schema and tenant_schema_prefix. Service binaries default to
  "public" / "" (zero change for existing deployments). Library defaults
  to "kronos" / "kronos_" (avoids host-app collisions on common names
  like organizations / workspaces).

- Coexistence with other DB libraries (Diesel, etc.): library is
  internally sqlx-only; hosts using Diesel run with two pools against
  the same Postgres database. For atomicity-critical flows, document
  the transactional outbox pattern. Connection-agnostic executor trait
  is explicitly out of v1 scope.

Updates Backwards-compatibility, Risks, Phasing, and Testing sections
to stay consistent with the new decisions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes a small ambiguity: the spec showed KronosClient::builder(pool)
and Worker::builder(pool) without naming the type. Make it explicit
that pool is sqlx::PgPool (taken by value, internally Arc-cloned).

Also tighten the ORM coexistence section to spell out that the host's
Diesel r2d2/bb8 pool holding diesel::PgConnection stays separate from
the sqlx::PgPool the host hands to Kronos.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the rough phase sketch with an explicit four-plan grouping
that maps directly to four sequential PRs, each leaving the system
in a green state with all existing integration tests passing.

- Plan 1 (Foundation): new crates + schema parameterization
- Plan 2 (Worker extraction): move poller/pipeline/backoff/dispatcher
- Plan 3 (Client extraction): build kronos-client and cut over handlers
- Plan 4 (Embedded validation + docs): E2E test + README updates

Each plan has explicit "ships when" criteria, dependencies between
plans are stated, and out-of-band follow-ups are listed for context.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Twelve bite-sized tasks covering crate scaffolding (kronos-client,
kronos-embedded-worker), the SchemaConfig value type, migration
templating with {{system_schema}}/{{tenant_schema_prefix}}
placeholders, parameterized public-schema migrations, an embedded
migration runner with apply()/MigrateError, kronos-migrate CLI,
SchemaRegistry update for the configured system schema, AppConfig
SchemaEnv (TE_SYSTEM_SCHEMA / TE_TENANT_SCHEMA_PREFIX), justfile
rewiring, and a smoke test that runs migrations into a kronos_smoke
namespace. Defaults preserve service behaviour byte-identically.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ig tests

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…stem_schema

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add `system_schema: String` field to `SchemaRegistry` and thread it through
`new()`. `get_active_schemas` now builds the query with `quote_ident` instead
of hard-coding `public.workspaces`. Add private `quote_ident` helper (defense
in depth on top of the upstream `SchemaConfig::validate` check). Update the
only call site in `crates/worker/src/poller.rs` to pass `"public".to_string()`
so service-mode behaviour is unchanged until Task 8 wires `AppConfig`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…schema_config

Save+restore env vars in picks_up_non_default_values to match the
pattern used in defaults_match_today, preventing developer-set vars
from leaking if an assertion panics. Also adds a brief doc comment
to to_schema_config explaining its role and the validate() contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes `kronos_client::migrate(&pool, &opts)` as the public library entry
point wrapping `kronos_common::migrations::apply`, and adds the `kronos-migrate`
binary driven by CLI flags / env vars (TE_DATABASE_URL, TE_SYSTEM_SCHEMA,
TE_TENANT_SCHEMA_PREFIX) for use in the `just db-migrate` recipe.
…giene

- Add hide_env_values = true to --database-url to prevent Postgres URLs
  (which often embed passwords) from leaking into --help output
- Validate SchemaConfig before connecting so bad input fails fast with a
  clear error instead of an obscure RenderError::InvalidConfig later
- Replace println! success message with tracing::info! for structured
  logging consistency with the rest of the codebase

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace four raw psql calls with a single `cargo run -p kronos-client
--bin kronos-migrate` invocation. Defaults to system_schema=public and
tenant_schema_prefix="" to preserve existing behaviour.

Also fix the migration apply() runner to split SQL on statement
boundaries (respecting dollar-quoted blocks and single-quoted strings)
before executing each statement individually. This allows
CREATE INDEX CONCURRENTLY to succeed because PostgreSQL rejects it when
it runs inside an implicit transaction created by the simple-query
protocol for multi-statement strings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds an integration test that applies migrations under system_schema="kronos_smoke"
and asserts organizations and workspaces tables exist in that schema, not public.
This is the load-bearing check that Tasks 4-7 parameterization works for embedded-mode consumers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@knutties

Copy link
Copy Markdown
Collaborator Author

Plan 1 (Foundation) is now committed on this branch:

  • Two new empty crates (kronos-client, kronos-embedded-worker) wired into the workspace
  • SchemaConfig value type and migration template renderer in kronos-common
  • Migrations parameterized on {{system_schema}} (service default public preserves today)
  • kronos_client::migrate(&pool, &opts) public API and kronos-migrate CLI binary
  • justfile db-migrate uses the new binary
  • Smoke test verifies non-default system_schema = "kronos_smoke" works
  • All existing integration tests pass with default config

Plan 2 (Worker extraction) will follow.

@knutties knutties changed the title Design: Kronos embedded mode (Rust library crates) Embedded mode foundation (Plan 1) Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant