Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
95cf47b
Add safe v1 to Reborn migration workflow
serrrfirat Jul 10, 2026
d8e0be0
feat: migrate read-only commands `doctor`, `status` and `config list/…
denbite Jul 12, 2026
c357d13
Add safe v1 to Reborn migration workflow
serrrfirat Jul 10, 2026
6a26bf8
no-mistakes(review): Harden v1 migration safety and quarantine enforc…
serrrfirat Jul 13, 2026
f183c19
no-mistakes(test): Update Reborn migration CLI test expectations
serrrfirat Jul 13, 2026
0f03fb0
no-mistakes(test): Clarify migration verification help
serrrfirat Jul 13, 2026
f38c56a
no-mistakes(document): Synchronize v1 migration documentation with Re…
serrrfirat Jul 13, 2026
e43cf89
no-mistakes(lint): Fix migration formatting and Clippy warnings
serrrfirat Jul 13, 2026
aa8dbed
no-mistakes: apply CI fixes
serrrfirat Jul 13, 2026
7313a48
fix(migration): address review feedback safely
serrrfirat Jul 13, 2026
0cfa649
no-mistakes(review): Harden Reborn migration safety and deterministic…
serrrfirat Jul 13, 2026
5d67f18
no-mistakes(test): Count engine-v2 projects during migration planning
serrrfirat Jul 13, 2026
afdb88b
no-mistakes(document): Synchronize Reborn migration documentation
serrrfirat Jul 13, 2026
72e72c4
no-mistakes(lint): Fix migration static-analysis issues
serrrfirat Jul 13, 2026
10924b4
no-mistakes(lint): Resolve migration lint warnings
serrrfirat Jul 13, 2026
103c902
no-mistakes(review): Harden Reborn migration safety and deterministic…
serrrfirat Jul 13, 2026
aa5674c
no-mistakes(test): Align migration tests with run-bound lifecycle
serrrfirat Jul 13, 2026
82089a9
no-mistakes(document): Synchronize Reborn migration and deployment do…
serrrfirat Jul 13, 2026
9f215c2
no-mistakes(lint): Format Reborn migration CLI changes
serrrfirat Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- *(reborn)* add the explicit offline `plan` / `apply` / `resume` / `verify` /
`status` v1 migration workflow with sealed manifests, deterministic replay,
target quarantine, structural durable-store verification, and a same-version
companion in Docker and paired source builds. Native installers do not yet
package the companion pair.

### Fixed

- *(slack)* resolve known DM conversation IDs through an exact Slack lookup before encoding mentions, avoiding wrong-target posts when conversation lists are long or display names are ambiguous.
Expand Down
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 27 additions & 8 deletions Dockerfile.reborn
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
# docker build -f Dockerfile.reborn -t ironclaw-reborn:latest .
#
# Run locally:
# docker run --rm --env-file .env.reborn -p 127.0.0.1:3000:3000 ironclaw-reborn:latest
# docker run --rm --env-file .env.reborn \
# -e IRONCLAW_REBORN_SERVE_HOST=0.0.0.0 \
# -p 127.0.0.1:3000:3000 ironclaw-reborn:latest
#
# Railway:
# Set Dockerfile path to Dockerfile.reborn and IRONCLAW_REBORN_SERVE_HOST=0.0.0.0.
Expand Down Expand Up @@ -33,15 +35,18 @@ WORKDIR /app
FROM chef AS planner

COPY Cargo.toml Cargo.lock ./
COPY build.rs build.rs
COPY src/ src/
COPY crates/ crates/
COPY tools/ironclaw_stress/ tools/ironclaw_stress/
COPY skills/ skills/
COPY tests/ tests/
COPY wit/ wit/
COPY profiles/ profiles/
COPY prompts/ prompts/
COPY channels-src/telegram/telegram.capabilities.json channels-src/telegram/telegram.capabilities.json
COPY channels-src/discord/discord.capabilities.json channels-src/discord/discord.capabilities.json
COPY providers.json providers.json
RUN mkdir -p src \
&& printf 'fn main() {}\n' > src/main.rs \
&& printf '\n' > src/lib.rs

RUN cargo chef prepare --recipe-path recipe.json

Expand All @@ -59,21 +64,29 @@ RUN cargo chef cook \
--profile dist \
--package ironclaw_reborn_cli \
--features webui-v2-beta,slack-v2-host-beta,libsql,postgres,inmemory-turn-state \
--recipe-path recipe.json \
&& cargo chef cook \
--profile dist \
--package ironclaw_reborn_migration \
--features libsql,postgres \
--recipe-path recipe.json

FROM deps AS builder

COPY Cargo.toml Cargo.lock ./
COPY build.rs build.rs
COPY src/ src/
COPY crates/ crates/
COPY tools/ironclaw_stress/ tools/ironclaw_stress/
COPY migrations/ migrations/
COPY skills/ skills/
COPY tests/ tests/
COPY wit/ wit/
COPY profiles/ profiles/
COPY prompts/ prompts/
COPY channels-src/telegram/telegram.capabilities.json channels-src/telegram/telegram.capabilities.json
COPY channels-src/discord/discord.capabilities.json channels-src/discord/discord.capabilities.json
COPY providers.json providers.json
RUN mkdir -p src \
&& printf 'fn main() {}\n' > src/main.rs \
&& printf '\n' > src/lib.rs

WORKDIR /app/crates/ironclaw_webui_v2/frontend
RUN pnpm install --frozen-lockfile
Expand All @@ -83,7 +96,12 @@ RUN cargo build \
--profile dist \
--package ironclaw_reborn_cli \
--features webui-v2-beta,slack-v2-host-beta,libsql,postgres,inmemory-turn-state \
--bin ironclaw-reborn
--bin ironclaw-reborn \
&& cargo build \
--profile dist \
--package ironclaw_reborn_migration \
--features libsql,postgres \
--bin ironclaw-reborn-migration

FROM debian:bookworm-slim AS runtime

Expand All @@ -95,6 +113,7 @@ RUN apt-get -o Acquire::Retries=3 update \
&& rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/target/dist/ironclaw-reborn /usr/local/bin/ironclaw-reborn
COPY --from=builder /app/target/dist/ironclaw-reborn-migration /usr/local/bin/ironclaw-reborn-migration
COPY docker/reborn/config.toml /opt/ironclaw/reborn/config.toml
COPY docker/reborn/config.hosted-single-tenant.toml /opt/ironclaw/reborn/config.hosted-single-tenant.toml
COPY docker/reborn/config.hosted-single-tenant-volume.toml /opt/ironclaw/reborn/config.hosted-single-tenant-volume.toml
Expand Down
34 changes: 29 additions & 5 deletions FEATURE_PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -712,17 +712,41 @@ Trace Commons issuer/TenantCtx note: the server-side `zmanian/tracedao-server` s
| Gmail pub/sub | βœ… | ❌ | P3 | |
| Inferred follow-up commitments | βœ… | ❌ | P3 | Heartbeat-delivered reminders; opt-in batched extraction |

**State migration (v1/engine-v2 β†’ Reborn):** `crates/ironclaw_reborn_migration`
converts persisted automations. Cron routines and cron missions convert to
Reborn `TriggerRecord`s (mission threads land under `ThreadScope.mission_id`).
**State migration (v1/engine-v2 β†’ Reborn):** `ironclaw-reborn migrate v1`
provides an explicit `plan β†’ apply/resume β†’ verify β†’ status` workflow through a
same-version companion in the Docker image and paired source builds. Native
installers do not yet package the pair. Planning uses a read-only source adapter
and does not open the target; apply requires a stopped-source snapshot, a fresh
target, and the sealed source fingerprint. The versioned manifest inventories
known v1 tables/home artifacts from an explicit sealed source-home path and
labels each category as imported, converted,
archive-only, reset, re-auth/reinstall, or unsupported. `archive-only` currently
means the source category and count remain visible in the manifest; it does not
export or retain the source payload. The companion is resolved beside the
primary executable rather than from `PATH`, and database URLs/master keys remain
environment-only. See
`docs/reborn/v1-migration.md` for cutover and rollback.
Both target backends persist an atomic, run-bound migration claim; PostgreSQL
stores it in the shared database so all replicas block activation until
verification succeeds.

The current conversion layer maps cron routines and cron missions to Reborn
`TriggerRecord`s (mission threads land under `ThreadScope.mission_id`).
Because Reborn's `TriggerSourceKind` is `Schedule`-only, **event / system-event /
webhook / manual routines and non-cron mission cadences have no `TriggerRecord`
target** and are recorded in the migration manifest rather than converted β€” even
target** and are recorded in the apply/resume migration report rather than converted β€” even
where the runtime supports the *behavior* via hooks/`event_emit`, the durable
automation row does not carry over. Guardrails, notify config, run counters,
`routine_runs` history (no public run-history insert), and mission-only fields
(focus/approach/success-criteria) likewise have no target. See the crate's
CLAUDE.md for the full mapping + gap catalog.
CLAUDE.md, manifest inventory, and retained apply/resume report for the full
mapping + gap catalog. Current verification checks structural counts for users,
projects, threads, messages, triggers, memory documents, secrets, and identity
records in production durable tables/paths; other domains do not receive an
independent readback, and this does not constitute a full production
cold-boot/readback test. Intermediate `applied` or `verifying`
states remain quarantined, and operators must complete a production canary
before accepting cutover.

### Owner: _Unassigned_

Expand Down
55 changes: 46 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@

## IronClaw Reborn Quick Start

IronClaw Reborn is the standalone runtime on the `reborn-integration` branch.
It uses the separate `ironclaw-reborn` binary from the
IronClaw Reborn is the standalone runtime in this workspace. It uses the
separate `ironclaw-reborn` binary from the
`ironclaw_reborn_cli` package and a separate Reborn state root. It does not use
the legacy `ironclaw` state directory as its config root.

Expand All @@ -62,24 +62,61 @@ cargo build -p ironclaw_reborn_cli --bin ironclaw-reborn
./target/debug/ironclaw-reborn --help
```

To use `migrate`, build the same-version companion into the same target
directory too. Compile the primary CLI with the target backend it must inspect
after migration (libSQL shown; use `--features postgres` for PostgreSQL):

```bash
cargo build -p ironclaw_reborn_cli --features libsql
cargo build -p ironclaw_reborn_migration
./target/debug/ironclaw-reborn migrate v1 --help
```

The default Reborn home is `$HOME/.ironclaw/reborn`. Override it with an
absolute path when you want isolated state:

```bash
export IRONCLAW_REBORN_HOME="$PWD/.reborn-home"
export IRONCLAW_REBORN_HOME="$HOME/.ironclaw-reborn-demo"
cargo run -q -p ironclaw_reborn_cli --bin ironclaw-reborn -- config path
```

`config path` and `doctor` are safe diagnostics; they report the resolved home,
profile, `config.toml`, `providers.json`, and `v1_state: not-used`.
They do not create Reborn state or seed config files.
profile, `config.toml`, `providers.json`, and `v1_state: not-used`. `doctor`
also reports `v1_migration_state`, including detected sources and any local or
durable target quarantine. They do not create Reborn state or seed config
files.

### Migrate an existing v1 installation

The Reborn Docker image includes a same-version migration companion. Source
builds must build both executables into the same target directory. Native
`cargo-dist` installers do not yet package the pair. Use the companion through
the primary binary; normal `run`, `repl`, `serve`, extension lifecycle, and
container startup never import v1 automatically. Runtime and extension
activation refuse quarantined migration states until verification succeeds:

```bash
ironclaw-reborn migrate v1 plan \
--source-libsql /backups/ironclaw-v1.db \
--source-home /srv/ironclaw-v1 \
--manifest /secure/migration-v1.json

ironclaw-reborn migrate v1 status \
--manifest /secure/migration-v1.json
```

Final apply requires v1 to be stopped and a consistent source snapshot. API
tokens and incompatible credentials require re-authentication; unsupported
executables are never enabled as placeholders. Review the complete backup,
apply, verification, and rollback procedure in
[`docs/reborn/v1-migration.md`](docs/reborn/v1-migration.md) before cutover.

### Configure the model route

The CLI-native way to configure Reborn's default model route is:

```bash
export IRONCLAW_REBORN_HOME="$PWD/.reborn-home"
export IRONCLAW_REBORN_HOME="$HOME/.ironclaw-reborn-demo"
cargo run -q -p ironclaw_reborn_cli --bin ironclaw-reborn -- models set-provider openai --model gpt-5-mini
```

Expand Down Expand Up @@ -179,7 +216,7 @@ seeded config does not include `[llm.default]`, so env-only model selection
continues to work:

```bash
export IRONCLAW_REBORN_HOME="$PWD/.reborn-env-only"
export IRONCLAW_REBORN_HOME="$HOME/.ironclaw-reborn-env-only"
export LLM_BACKEND=openai
export OPENAI_API_KEY="sk-..."
cargo run -q -p ironclaw_reborn_cli --bin ironclaw-reborn -- run --message "hello"
Expand Down Expand Up @@ -245,7 +282,7 @@ env-bearer token and a user id at startup. It also needs the model route from
the earlier section, including that provider's credential env var:

```bash
export IRONCLAW_REBORN_HOME="$PWD/.reborn-home"
export IRONCLAW_REBORN_HOME="$HOME/.ironclaw-reborn-demo"
export OPENAI_API_KEY="sk-..." # or the required env var for your configured provider
export IRONCLAW_REBORN_WEBUI_TOKEN="$(openssl rand -hex 32)"
export IRONCLAW_REBORN_WEBUI_USER_ID="reborn-cli"
Expand Down Expand Up @@ -343,7 +380,7 @@ Slack support is compiled behind the `slack-v2-host-beta` Cargo feature. That
feature includes `webui-v2-beta`, so Slack runs on the same `serve` command:

```bash
export IRONCLAW_REBORN_HOME="$PWD/.reborn-home"
export IRONCLAW_REBORN_HOME="$HOME/.ironclaw-reborn-demo"
export OPENAI_API_KEY="sk-..." # or the required env var for your configured provider
export IRONCLAW_REBORN_WEBUI_TOKEN="$(openssl rand -hex 32)"
export IRONCLAW_REBORN_WEBUI_USER_ID="reborn-cli"
Expand Down
3 changes: 2 additions & 1 deletion crates/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ Boundary rule: if you need an upstream crate in a low-level crate, stop and chec
| `ironclaw_first_party_extensions` | `ironclaw_first_party_extensions/AGENTS.md`, `Cargo.toml` | Concrete first-party userland extension implementations and deterministic tool behavior behind scoped handles. | Host runtime composition, loop-facing ports, ambient runtime authority, dispatcher/network/secrets handles. |
| `ironclaw_first_party_extension_ports` | `ironclaw_first_party_extension_ports/AGENTS.md`, `Cargo.toml` | Loop-facing adapters for first-party extensions: skill activation/context/execution ports over loop-host and turn-run contracts. | Concrete tool behavior, host runtime composition, product workflow, raw host authority. |
| `ironclaw_reborn_cli` | `ironclaw_reborn_cli/AGENTS.md` | Standalone Reborn CLI, command files, CLI context, shell completions, doctor/home/profile commands. | V1 runtime imports, root `ironclaw` deps, side effects in pure commands. |
| `ironclaw_reborn_migration` | `ironclaw_reborn_migration/CLAUDE.md` | Same-release offline v1 migration companion: read-only source adapters, sealed manifests, deterministic converters, target quarantine, and structural verification. | Linking legacy source code into the normal Reborn runtime or bypassing the explicit plan/apply/resume/verify lifecycle. |
| `ironclaw_product_adapters` | `ironclaw_product_adapters/AGENTS.md`, `ironclaw_product_adapters/CLAUDE.md` | Product-adapter contracts: adapter trait, auth, egress, identity, workflow, external/projection/inbound, redaction, fakes. | Host runtime internals or specific WASM runner implementation. |
| `ironclaw_product_adapter_registry` | `ironclaw_product_adapter_registry/AGENTS.md`, `ironclaw_product_adapter_registry/CLAUDE.md` | ProductAdapter host-api projection and installation registry. | Adapter execution or product workflow orchestration. |
| `ironclaw_product_workflow` | `ironclaw_product_workflow/AGENTS.md`, `ironclaw_product_workflow/CLAUDE.md` | Product-facing workflow facade: inbound turns, bindings, ledger, workflow/errors, Reborn service bridges, and feature-gated durable ledger adapters. | Low-level runtime lane internals, direct provider-specific transports, or durable ledger access outside the `IdempotencyLedger` port. |
Expand Down Expand Up @@ -174,7 +175,7 @@ Boundary rule: if you need an upstream crate in a low-level crate, stop and chec
- Reborn runtime execution: lane crate (`scripts`, `mcp`, `wasm`) first; `dispatcher` for routing; `host_runtime` for secrets/network/resources/redaction; `processes` for background lifecycle; `ironclaw_wasm_limiter` only for shared limiter mechanics. Use `ironclaw_engine` only for existing v1 engine maintenance.
- Reborn turns/agent loop: `ironclaw_turns` for turn coordination; `ironclaw_agent_loop` for strategy/planner/executor contracts; `ironclaw_loop_host` for host support ports. Use `ironclaw_engine` only for existing v1 CodeAct/thread runtime maintenance.
- Product adapter flow: `ironclaw_product_adapters` contracts -> `ironclaw_product_adapter_registry` installation/projection -> `ironclaw_product_workflow` orchestration -> concrete adapter crate.
- Reborn binary/composition: `ironclaw_reborn_config` for boot config; `ironclaw_reborn_composition` for production wiring; `ironclaw_reborn_cli` for commands; `ironclaw_runner` for standalone adapters/driver registry; `ironclaw_reborn_webui_ingress` for host-owned WebChat v2 listener lifecycle.
- Reborn binary/composition: `ironclaw_reborn_config` for boot config; `ironclaw_reborn_composition` for production wiring; `ironclaw_reborn_cli` for commands; `ironclaw_reborn_migration` for the offline v1 companion; `ironclaw_runner` for standalone adapters/driver registry; `ironclaw_reborn_webui_ingress` for host-owned WebChat v2 listener lifecycle.
- Model/provider behavior: `ironclaw_llm`; do not leak provider auth/cache/retry concerns into engine or product workflow.
- UI presentation: `ironclaw_webui_v2` and `ironclaw_webui_v2_static` for Reborn WebChat v2; `ironclaw_tui` and `ironclaw_gateway` only for existing v1 UI maintenance. Backend API/web channel code remains under root `src/` unless the surface is the Reborn WebChat v2 route crate.

Expand Down
1 change: 1 addition & 0 deletions crates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ A good rule of thumb: if a change adds new authority or persistence, put it in t
| `ironclaw_reborn_composition` | `ironclaw_reborn_composition` | Wiring layer that assembles Reborn services into the host runtime. Composition-only; no policy or persistence logic of its own. |
| `ironclaw_reborn_config` | `ironclaw_reborn_config` | Reborn boot-config boundary: typed configuration, profiles, and validation consumed before services start. |
| `ironclaw_reborn_cli` | `ironclaw_reborn_cli` | Reborn-first CLI surface (command modules, completion, shell entry points). Calls into composition; does not own host policy. |
| `ironclaw_reborn_migration` | `ironclaw_reborn_migration` | Same-release offline companion for manifest-driven v1 planning, conversion, quarantine, resume, and structural verification. It is invoked through `ironclaw-reborn migrate v1`, not linked into the normal runtime. |
| `ironclaw_reborn_webui_ingress` | `ironclaw_reborn_webui_ingress` | Host-owned listener binding, authenticator implementations, and serve loop for the Reborn WebChat v2 HTTP gateway. |
| `ironclaw_reborn_openai_compat` | `ironclaw_reborn_openai_compat` | OpenAI-compatible Chat/Responses DTOs, route descriptors, sanitized errors, fail-closed route fragment, and feature-gated durable ref/idempotency storage. |
| `ironclaw_llm` | `ironclaw_llm` | LLM provider routing and abstraction used by Reborn product surfaces and the agent loop. |
Expand Down
10 changes: 5 additions & 5 deletions crates/ironclaw_memory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ pub use service::{
MEMORY_DISABLED_CONTEXT_ALIASES, MemoryContextProfileId, MemoryInvocation,
MemoryProfileSetStatus, MemoryService, MemoryServiceContextRequest,
MemoryServiceContextSnippet, MemoryServiceError, MemoryServiceErrorKind,
MemoryServiceProfileSetRequest, MemoryServiceProfileSetResponse, MemoryServiceReadRequest,
MemoryServiceReadResponse, MemoryServiceSearchRequest, MemoryServiceSearchResponse,
MemoryServiceSearchResult, MemoryServiceTreeRequest, MemoryServiceTreeResponse,
MemoryServiceWriteRequest, MemoryServiceWriteResponse, MemoryWriteStatus,
memory_context_disabled,
MemoryServiceMetadataResponse, MemoryServiceProfileSetRequest, MemoryServiceProfileSetResponse,
MemoryServiceReadRequest, MemoryServiceReadResponse, MemoryServiceSearchRequest,
MemoryServiceSearchResponse, MemoryServiceSearchResult, MemoryServiceTreeRequest,
MemoryServiceTreeResponse, MemoryServiceWriteRequest, MemoryServiceWriteResponse,
MemoryWriteStatus, memory_context_disabled,
};
21 changes: 21 additions & 0 deletions crates/ironclaw_memory/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,15 @@ pub struct MemoryServiceReadResponse {
pub word_count: usize,
}

/// Result of a metadata-only document read.
///
/// `metadata` is `None` when the scoped document does not exist.
#[derive(Debug, Clone, PartialEq)]
pub struct MemoryServiceMetadataResponse {
/// Parsed document metadata without loading document content.
pub metadata: Option<DocumentMetadata>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemoryServiceTreeRequest {
pub path: String,
Expand Down Expand Up @@ -444,6 +453,18 @@ pub trait MemoryService: Send + Sync {
Err(MemoryServiceError::unavailable())
}

/// Read only a scoped document's metadata, returning `None` for a missing
/// document. Providers that do not implement this operation fail closed
/// with [`MemoryServiceErrorKind::Unavailable`].
async fn read_metadata(
&self,
invocation: MemoryInvocation,
request: MemoryServiceReadRequest,
) -> Result<MemoryServiceMetadataResponse, MemoryServiceError> {
let _ = (invocation, request);
Err(MemoryServiceError::unavailable())
}

async fn tree(
&self,
invocation: MemoryInvocation,
Expand Down
Loading
Loading