Skip to content

refactor(reborn): budget-gate store over RootFilesystem, delete InMemoryBudgetGateStore (§4.3) - #6210

Merged
ilblackdragon merged 4 commits into
mainfrom
refactor/reborn-consolidate-budget-gate-store
Jul 18, 2026
Merged

ilblackdragon merged 4 commits into
mainfrom
refactor/reborn-consolidate-budget-gate-store

Conversation

@ilblackdragon

Copy link
Copy Markdown
Member

What

Continues the arch-simplification §4.3 store consolidation (after approvals/authorization/processes/run-state): deletes the hand-written InMemoryBudgetGateStore and uses the one production FilesystemBudgetGateStore over an in-memory backend. The code already flagged this as a TODO ("Production composition can swap in a filesystem-backed store … deferred to a follow-up") — this is that follow-up.

Behavior change — strictly more correct

The deleted in-memory store was a single global HashMap that ignored ResourceScope. FilesystemBudgetGateStore routes each gate under the caller's tenant/user mount via the same cas_update(scoped_fs, &scope, path) mechanism the merged capability-lease store (#6197) uses — so budget gates are now properly tenant-isolated in the no-durable path instead of globally shared.

The open → get gate lifecycle is preserved (both flow from the same user's turn scope). All existing gate tests use a single scope, so none needed A2-style reconciliation. resolve() has no production caller today; only open() (budget accountant) and get() (apply_resolved_budget_gate) are wired.

Changes

  • ironclaw_resources: delete InMemoryBudgetGateStore struct+impl and its pub use; add a test-support feature + test_support.rs with in_memory_backed_budget_gate_store() (terminal retention disabled to match the old retain-forever test semantics). Migrate the 7 gate.rs unit tests onto the helper.
  • Composition factory no-durable path: FilesystemBudgetGateStore::new(wrap_scoped(InMemoryBackend::new())), mirroring the capability-lease wiring. Observability + integration (tests/integration/support/group.rs) test doubles use the test-support helper.
  • R1 ratchet (reborn_inmemory_store_ratchet): drop InMemoryBudgetGateStore from the frozen allowlist (the ratchet forces this trim in the same PR as the deletion — shrinking toward the §10 empty-set goal).

Wire-safe: BudgetApprovalGate/BudgetGateStatus serde shapes unchanged (the filesystem store reuses the same records).

Verification

  • cargo test -p ironclaw_resources --features test-support — 64 pass
  • cargo test -p ironclaw_reborn_composition --lib observability::budget — 5 pass
  • cargo test -p ironclaw_architecture --test reborn_inmemory_store_ratchet — pass
  • cargo build -p ironclaw_reborn_composition (default + libsql) — clean
  • cargo clippy -p ironclaw_resources -p ironclaw_reborn_composition --all-targets --all-features -- -D warnings — clean
  • cargo test --features integration --no-run — rc=0 (integration harness compiles)
  • cargo fmt --check + scripts/pre-commit-safety.sh — clean

Stack

Stacked on #6209. Part of the incremental arch-simplification refactor (docs/reborn/2026-07-17-architecture-simplification-dto-dyn-local.md).

🤖 Generated with Claude Code

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@ironloopai

ironloopai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

🔎 IronLoop Review Status

Head: d5a657f55170dafcde2c7a65658d5bef8fec112a
Result: One or more review results were superseded by a newer PR head.
Next: Run @ironloopai review on the latest PR head.
Updated: 2026-07-18T00:08:30.251Z

Current reviewers:

Reviewer State Verdict Findings Last update
ironloop/common-reviewer (reviewer) Superseded N/A N/A 2026-07-17T22:23:27.813Z
Reviewer summaries
Reviewer Detail
ironloop/common-reviewer (reviewer) Superseded by a newer PR head. New head: 5bd5181. Previous verdict: Needs validation.
Recent activity
Time Reviewer State Detail
2026-07-17T22:17:20.298Z ironloop/common-reviewer (reviewer) Queued Accepted review request for head 728e484.
2026-07-17T22:17:20.298Z ironloop/common-reviewer (reviewer) Queued Waiting for this reviewer lane to become available.
2026-07-17T22:17:20.410Z ironloop/common-reviewer (reviewer) Started Reviewer worker started.
2026-07-17T22:17:23.392Z ironloop/common-reviewer (reviewer) Workspace ready Prepared isolated checkout (merge_ref) at 727fabd.
2026-07-17T22:20:06.978Z ironloop/common-reviewer (reviewer) Superseded A newer PR head replaced this review (5bd5181).
2026-07-17T22:23:27.813Z ironloop/common-reviewer (reviewer) Result captured Needs validation; 0 blocking findings.
2026-07-17T22:23:27.813Z ironloop/common-reviewer (reviewer) Superseded A newer PR head replaced this review (5bd5181).
Available commands
  • @ironloopai help
  • @ironloopai agents
  • @ironloopai review
  • @ironloopai review --agent <agent>
Run metadata

Admission: webhook accepted the request and IronLoop persisted reviewer state before this projection.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6210 July 17, 2026 22:17 Destroyed
@github-actions github-actions Bot added the scope: dependencies Dependency updates label Jul 17, 2026
@coderabbitai

coderabbitai Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • staging
  • reborn-integration

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 80acead8-e6de-4295-b507-99713bf27e0d

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

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.

@github-actions github-actions Bot added size: L 200-499 changed lines risk: medium Business logic, config, or moderate-risk modules contributor: core 20+ merged PRs labels Jul 17, 2026
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-rename-localfilesystem branch from c35af94 to 2d69809 Compare July 17, 2026 22:20
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-consolidate-budget-gate-store branch from 728e484 to 5bd5181 Compare July 17, 2026 22:20
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6210 July 17, 2026 22:20 Destroyed
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-rename-localfilesystem branch from 2d69809 to e2dfea4 Compare July 17, 2026 22:24
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-consolidate-budget-gate-store branch from 5bd5181 to 6c38630 Compare July 17, 2026 22:24
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6210 July 17, 2026 22:24 Destroyed
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-rename-localfilesystem branch from e2dfea4 to 13f8a37 Compare July 17, 2026 22:40
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-consolidate-budget-gate-store branch from 6c38630 to 529ede6 Compare July 17, 2026 22:40
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6210 July 17, 2026 22:40 Destroyed
@railway-app

railway-app Bot commented Jul 17, 2026 •

Copy link
Copy Markdown

🚅 Deployed to the ironclaw-pr-6210 environment in ironclaw-ci-preview

Service Status Web Updated (UTC)
ironclaw ✅ Success (View Logs) Web Jul 18, 2026 at 12:24 am

@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-rename-localfilesystem branch from 13f8a37 to 7263297 Compare July 17, 2026 23:28
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-consolidate-budget-gate-store branch from 529ede6 to 03bc985 Compare July 17, 2026 23:28
ilblackdragon added a commit that referenced this pull request Jul 17, 2026
…MemoryOutboundStateStore (§4.3)

Continues the arch-simplification §4.3 store consolidation: delete the
hand-written `InMemoryOutboundStateStore` (which implemented only
`CommunicationPreferenceRepository` + `OutboundStateStore`) and use the one
production `FilesystemOutboundStateStore` — which implements all four
outbound-store traits — over an in-memory backend.

Gap closed (strict improvement): the durable (libsql/postgres) composition
already shared one `FilesystemOutboundStateStore` across all four outbound
roles, while the no-durable path split `InMemoryOutboundStateStore` (prefs +
state) from separate `InMemory{DeliveredGateRoute,TriggeredRunDelivery}Store`
instances — a documented cross-store "gap". Both `local_dev_outbound_store`
cfg branches now collapse into one that shares a single
`FilesystemOutboundStateStore<CompositeRootFilesystem>` for every role
(`LocalDevRootFilesystem` is `CompositeRootFilesystem` in both builds), closing
the gap.

Changes:
- `ironclaw_outbound`: delete `memory.rs` (`InMemoryOutboundStateStore`, 264
  lines, no other exports); add a `test-support` feature + `test_support.rs`
  with `in_memory_backed_outbound_state_store()`. Own tests use
  local/`crate::` helpers (run_state pattern).
- Composition factory: merge the two `local_dev_outbound_store` cfg branches;
  make the `FilesystemOutboundStateStore` import unconditional (the
  cfg-gated-import trap — A5 lesson) and ungate `local_dev_scoped_filesystem`.
- `projection.rs` (`build_reborn_projection_services`, PRODUCTION): the
  EventStreamManager's throwaway `InMemoryOutboundStateStore::default()` becomes
  a fresh `FilesystemOutboundStateStore::new(wrap_scoped(InMemoryBackend))`
  (the budget-gate factory pattern — no `expect`, behavior-preserving). Store
  stays constructed inside the fn, so its ~65 test callers are untouched.
- Downstream test doubles (event_streams, product_workflow, composition slack/
  outbound/projection tests) repointed to the test-support helper; each
  consumer crate enables `ironclaw_outbound/test-support` in dev-deps.
- `outbound_state_store_contract.rs`: delete the now-redundant `in_memory_*`
  conformance test (the filesystem arm is a strict superset).
- R1 ratchet: drop `InMemoryOutboundStateStore` from the frozen allowlist.

Wire-safe: record schemas unchanged (the filesystem store reuses them).
Verified: outbound 110, event_streams 70, product_workflow 18, composition
projection 143 + slack 387 + outbound 76 tests pass; `cargo build -p
ironclaw_reborn_composition` (default + libsql) clean; clippy `-D warnings`
clean on all four crates; ratchet pass; fmt + pre-commit clean.

Stacked on #6210.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6210 July 17, 2026 23:28 Destroyed
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-rename-localfilesystem branch from 7263297 to 50e47c6 Compare July 17, 2026 23:34
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-consolidate-budget-gate-store branch from 03bc985 to 3e00ebf Compare July 17, 2026 23:34
ilblackdragon added a commit that referenced this pull request Jul 17, 2026
…MemoryOutboundStateStore (§4.3)

Continues the arch-simplification §4.3 store consolidation: delete the
hand-written `InMemoryOutboundStateStore` (which implemented only
`CommunicationPreferenceRepository` + `OutboundStateStore`) and use the one
production `FilesystemOutboundStateStore` — which implements all four
outbound-store traits — over an in-memory backend.

Gap closed (strict improvement): the durable (libsql/postgres) composition
already shared one `FilesystemOutboundStateStore` across all four outbound
roles, while the no-durable path split `InMemoryOutboundStateStore` (prefs +
state) from separate `InMemory{DeliveredGateRoute,TriggeredRunDelivery}Store`
instances — a documented cross-store "gap". Both `local_dev_outbound_store`
cfg branches now collapse into one that shares a single
`FilesystemOutboundStateStore<CompositeRootFilesystem>` for every role
(`LocalDevRootFilesystem` is `CompositeRootFilesystem` in both builds), closing
the gap.

Changes:
- `ironclaw_outbound`: delete `memory.rs` (`InMemoryOutboundStateStore`, 264
  lines, no other exports); add a `test-support` feature + `test_support.rs`
  with `in_memory_backed_outbound_state_store()`. Own tests use
  local/`crate::` helpers (run_state pattern).
- Composition factory: merge the two `local_dev_outbound_store` cfg branches;
  make the `FilesystemOutboundStateStore` import unconditional (the
  cfg-gated-import trap — A5 lesson) and ungate `local_dev_scoped_filesystem`.
- `projection.rs` (`build_reborn_projection_services`, PRODUCTION): the
  EventStreamManager's throwaway `InMemoryOutboundStateStore::default()` becomes
  a fresh `FilesystemOutboundStateStore::new(wrap_scoped(InMemoryBackend))`
  (the budget-gate factory pattern — no `expect`, behavior-preserving). Store
  stays constructed inside the fn, so its ~65 test callers are untouched.
- Downstream test doubles (event_streams, product_workflow, composition slack/
  outbound/projection tests) repointed to the test-support helper; each
  consumer crate enables `ironclaw_outbound/test-support` in dev-deps.
- `outbound_state_store_contract.rs`: delete the now-redundant `in_memory_*`
  conformance test (the filesystem arm is a strict superset).
- R1 ratchet: drop `InMemoryOutboundStateStore` from the frozen allowlist.

Wire-safe: record schemas unchanged (the filesystem store reuses them).
Verified: outbound 110, event_streams 70, product_workflow 18, composition
projection 143 + slack 387 + outbound 76 tests pass; `cargo build -p
ironclaw_reborn_composition` (default + libsql) clean; clippy `-D warnings`
clean on all four crates; ratchet pass; fmt + pre-commit clean.

Stacked on #6210.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6210 July 17, 2026 23:34 Destroyed
ilblackdragon and others added 2 commits July 18, 2026 00:01
…et 2)

The on-disk `RootFilesystem` backend was named `LocalFilesystem`, which
read as a *deployment tier* ("local" dev vs. served) when it is simply the
disk storage medium — a peer of `InMemoryBackend`, `LibSqlRootFilesystem`,
and `PostgresRootFilesystem` that a `DeploymentConfig` may select. Renamed
to `DiskFilesystem` so the type name states the storage medium, per the
architecture-simplification note's §4.4 Bucket 2 (Local* deployment-mode
naming cleanup).

Mechanical, behavior-preserving word-boundary rename across 69 files:
`struct LocalFilesystem` -> `struct DiskFilesystem`, the `pub use` in
`ironclaw_filesystem/src/lib.rs`, and every reference. Wire-safe: the type
has no serde persistence tag keyed on its name; the only renamed strings
are test assertion messages. Verified:

- `cargo build --workspace` — Finished
- `cargo test -p ironclaw_filesystem` — 24 pass
- `cargo test --no-run -p ironclaw_reborn_composition -p ironclaw_webui` — rc=0
- `cargo fmt --check` clean; `scripts/pre-commit-safety.sh` clean

Nine >1500-line files carry a `// arch-exempt: large_file, mechanical
... plan #6168` annotation — the rename adds a handful of lines with no
logic change and nowhere else to land.

Stacked on the LocalTraceSubmission* rename (#6207).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e mapping; refresh filesystem guidance

Addresses the IronLoop finding on #6209 plus a semantic rebase conflict:

- host_runtime_services_contract.rs:574 — the run-state/approval
  classification test landed on main (#6203 follow-up) after this branch
  was cut and still constructed `LocalFilesystem::new()`; repointed to
  `DiskFilesystem::new()` (compile fix).
- reborn_localdev_typename_ratchet.rs — the §4.4 doc comment's rename
  example was over-renamed to `DiskFilesystem`→`DiskFilesystem`; restored
  the historical `LocalFilesystem`→`DiskFilesystem` mapping.
- Live filesystem guidance now names the current backend: ironclaw_filesystem
  CLAUDE.md/AGENTS.md backend lists and the filesystem/live-vertical-slice
  contract docs. The arch-exempt annotations and the `local.rs` "renamed
  from" breadcrumb keep the old name deliberately (they describe the rename).

[skip-regression-check] compile/doc fixes on a behavior-preserving rename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-rename-localfilesystem branch from 50e47c6 to 22a3c12 Compare July 18, 2026 00:04
ilblackdragon and others added 2 commits July 18, 2026 00:05
…oryBudgetGateStore (§4.3)

Continues the arch-simplification §4.3 store consolidation (after
approvals/authorization/processes/run-state): delete the hand-written
`InMemoryBudgetGateStore` parallel implementation and use the one
production `FilesystemBudgetGateStore` over an in-memory backend —
"in-memory" stops being a store and becomes a filesystem backend
(`InMemoryBackend`). The code already flagged this in a TODO
("Production composition can swap in a filesystem-backed store ...
deferred to a follow-up"); this is that follow-up.

Behavior change (strictly more correct): the deleted in-memory store
was a single global HashMap that *ignored* `ResourceScope`. The
filesystem store routes each gate under the caller's tenant/user mount
(the same `cas_update(scoped_fs, &scope, path)` mechanism the merged
capability-lease store #6197 uses), so budget gates are now properly
tenant-isolated in the no-durable path instead of globally shared. The
open→get gate lifecycle is preserved: both flow from the same user's
turn scope. All existing gate tests use a single scope, so none needed
reconciliation. `resolve()` has no production caller today; only
`open()` (budget accountant) and `get()` (`apply_resolved_budget_gate`)
are wired.

Changes:
- `ironclaw_resources`: delete `InMemoryBudgetGateStore` struct+impl and
  its `pub use`; add a `test-support` feature + `test_support.rs` with
  `in_memory_backed_budget_gate_store()` (retention disabled to match the
  old retain-forever semantics for tests). Migrate the 7 gate.rs unit
  tests onto the helper.
- Composition factory no-durable path: `FilesystemBudgetGateStore::new(
  wrap_scoped(InMemoryBackend::new()))`, mirroring the capability-lease
  wiring. Observability + integration (`tests/integration/support/group.rs`)
  test doubles use the `test-support` helper.
- R1 ratchet (`reborn_inmemory_store_ratchet`): drop
  `InMemoryBudgetGateStore` from the frozen allowlist (the ratchet forces
  this trim in the same PR as the deletion).

Wire-safe: `BudgetApprovalGate`/`BudgetGateStatus` serde shapes unchanged
(the filesystem store reuses the same records). Verified:

- `cargo test -p ironclaw_resources --features test-support` — 64 pass
- `cargo test -p ironclaw_reborn_composition --lib observability::budget` — 5 pass
- `cargo test -p ironclaw_architecture --test reborn_inmemory_store_ratchet` — pass
- `cargo build -p ironclaw_reborn_composition` (default + libsql) — clean
- `cargo clippy -p ironclaw_resources -p ironclaw_reborn_composition --all-targets ... -D warnings` — clean
- `cargo test --features integration --no-run` — rc=0
- `cargo fmt --check` + `scripts/pre-commit-safety.sh` — clean

Stacked on #6209.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t-in contract

Same wording CodeRabbit corrected on #6203's run_state test-support: a
production build can technically enable the feature, so describe the gate
as disabled-by-default/opt-in for [dev-dependencies] rather than promising
"never ships"/"zero bytes". Applies to the module doc and the Cargo.toml
feature comment.

[skip-regression-check] doc-comment wording only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-consolidate-budget-gate-store branch from 3e00ebf to d5a657f Compare July 18, 2026 00:08
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-6210 July 18, 2026 00:08 Destroyed
ilblackdragon added a commit that referenced this pull request Jul 18, 2026
…MemoryOutboundStateStore (§4.3)

Continues the arch-simplification §4.3 store consolidation: delete the
hand-written `InMemoryOutboundStateStore` (which implemented only
`CommunicationPreferenceRepository` + `OutboundStateStore`) and use the one
production `FilesystemOutboundStateStore` — which implements all four
outbound-store traits — over an in-memory backend.

Gap closed (strict improvement): the durable (libsql/postgres) composition
already shared one `FilesystemOutboundStateStore` across all four outbound
roles, while the no-durable path split `InMemoryOutboundStateStore` (prefs +
state) from separate `InMemory{DeliveredGateRoute,TriggeredRunDelivery}Store`
instances — a documented cross-store "gap". Both `local_dev_outbound_store`
cfg branches now collapse into one that shares a single
`FilesystemOutboundStateStore<CompositeRootFilesystem>` for every role
(`LocalDevRootFilesystem` is `CompositeRootFilesystem` in both builds), closing
the gap.

Changes:
- `ironclaw_outbound`: delete `memory.rs` (`InMemoryOutboundStateStore`, 264
  lines, no other exports); add a `test-support` feature + `test_support.rs`
  with `in_memory_backed_outbound_state_store()`. Own tests use
  local/`crate::` helpers (run_state pattern).
- Composition factory: merge the two `local_dev_outbound_store` cfg branches;
  make the `FilesystemOutboundStateStore` import unconditional (the
  cfg-gated-import trap — A5 lesson) and ungate `local_dev_scoped_filesystem`.
- `projection.rs` (`build_reborn_projection_services`, PRODUCTION): the
  EventStreamManager's throwaway `InMemoryOutboundStateStore::default()` becomes
  a fresh `FilesystemOutboundStateStore::new(wrap_scoped(InMemoryBackend))`
  (the budget-gate factory pattern — no `expect`, behavior-preserving). Store
  stays constructed inside the fn, so its ~65 test callers are untouched.
- Downstream test doubles (event_streams, product_workflow, composition slack/
  outbound/projection tests) repointed to the test-support helper; each
  consumer crate enables `ironclaw_outbound/test-support` in dev-deps.
- `outbound_state_store_contract.rs`: delete the now-redundant `in_memory_*`
  conformance test (the filesystem arm is a strict superset).
- R1 ratchet: drop `InMemoryOutboundStateStore` from the frozen allowlist.

Wire-safe: record schemas unchanged (the filesystem store reuses them).
Verified: outbound 110, event_streams 70, product_workflow 18, composition
projection 143 + slack 387 + outbound 76 tests pass; `cargo build -p
ironclaw_reborn_composition` (default + libsql) clean; clippy `-D warnings`
clean on all four crates; ratchet pass; fmt + pre-commit clean.

Stacked on #6210.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ilblackdragon

Copy link
Copy Markdown
Member Author

✅ Ready for merge

Reviewed, CI fully green (20 pass / 0 fail) on head d5a657f55. Stacked on #6209 — retargets to main when that merges.

🤖 Generated with Claude Code

Base automatically changed from refactor/reborn-rename-localfilesystem to main July 18, 2026 00:38
@ilblackdragon
ilblackdragon merged commit 879c799 into main Jul 18, 2026
24 checks passed
@ilblackdragon
ilblackdragon deleted the refactor/reborn-consolidate-budget-gate-store branch July 18, 2026 00:39
ilblackdragon added a commit that referenced this pull request Jul 18, 2026
…MemoryOutboundStateStore (§4.3)

Continues the arch-simplification §4.3 store consolidation: delete the
hand-written `InMemoryOutboundStateStore` (which implemented only
`CommunicationPreferenceRepository` + `OutboundStateStore`) and use the one
production `FilesystemOutboundStateStore` — which implements all four
outbound-store traits — over an in-memory backend.

Gap closed (strict improvement): the durable (libsql/postgres) composition
already shared one `FilesystemOutboundStateStore` across all four outbound
roles, while the no-durable path split `InMemoryOutboundStateStore` (prefs +
state) from separate `InMemory{DeliveredGateRoute,TriggeredRunDelivery}Store`
instances — a documented cross-store "gap". Both `local_dev_outbound_store`
cfg branches now collapse into one that shares a single
`FilesystemOutboundStateStore<CompositeRootFilesystem>` for every role
(`LocalDevRootFilesystem` is `CompositeRootFilesystem` in both builds), closing
the gap.

Changes:
- `ironclaw_outbound`: delete `memory.rs` (`InMemoryOutboundStateStore`, 264
  lines, no other exports); add a `test-support` feature + `test_support.rs`
  with `in_memory_backed_outbound_state_store()`. Own tests use
  local/`crate::` helpers (run_state pattern).
- Composition factory: merge the two `local_dev_outbound_store` cfg branches;
  make the `FilesystemOutboundStateStore` import unconditional (the
  cfg-gated-import trap — A5 lesson) and ungate `local_dev_scoped_filesystem`.
- `projection.rs` (`build_reborn_projection_services`, PRODUCTION): the
  EventStreamManager's throwaway `InMemoryOutboundStateStore::default()` becomes
  a fresh `FilesystemOutboundStateStore::new(wrap_scoped(InMemoryBackend))`
  (the budget-gate factory pattern — no `expect`, behavior-preserving). Store
  stays constructed inside the fn, so its ~65 test callers are untouched.
- Downstream test doubles (event_streams, product_workflow, composition slack/
  outbound/projection tests) repointed to the test-support helper; each
  consumer crate enables `ironclaw_outbound/test-support` in dev-deps.
- `outbound_state_store_contract.rs`: delete the now-redundant `in_memory_*`
  conformance test (the filesystem arm is a strict superset).
- R1 ratchet: drop `InMemoryOutboundStateStore` from the frozen allowlist.

Wire-safe: record schemas unchanged (the filesystem store reuses them).
Verified: outbound 110, event_streams 70, product_workflow 18, composition
projection 143 + slack 387 + outbound 76 tests pass; `cargo build -p
ironclaw_reborn_composition` (default + libsql) clean; clippy `-D warnings`
clean on all four crates; ratchet pass; fmt + pre-commit clean.

Stacked on #6210.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ilblackdragon added a commit that referenced this pull request Jul 18, 2026
…MemoryOutboundStateStore (§4.3)

Continues the arch-simplification §4.3 store consolidation: delete the
hand-written `InMemoryOutboundStateStore` (which implemented only
`CommunicationPreferenceRepository` + `OutboundStateStore`) and use the one
production `FilesystemOutboundStateStore` — which implements all four
outbound-store traits — over an in-memory backend.

Gap closed (strict improvement): the durable (libsql/postgres) composition
already shared one `FilesystemOutboundStateStore` across all four outbound
roles, while the no-durable path split `InMemoryOutboundStateStore` (prefs +
state) from separate `InMemory{DeliveredGateRoute,TriggeredRunDelivery}Store`
instances — a documented cross-store "gap". Both `local_dev_outbound_store`
cfg branches now collapse into one that shares a single
`FilesystemOutboundStateStore<CompositeRootFilesystem>` for every role
(`LocalDevRootFilesystem` is `CompositeRootFilesystem` in both builds), closing
the gap.

Changes:
- `ironclaw_outbound`: delete `memory.rs` (`InMemoryOutboundStateStore`, 264
  lines, no other exports); add a `test-support` feature + `test_support.rs`
  with `in_memory_backed_outbound_state_store()`. Own tests use
  local/`crate::` helpers (run_state pattern).
- Composition factory: merge the two `local_dev_outbound_store` cfg branches;
  make the `FilesystemOutboundStateStore` import unconditional (the
  cfg-gated-import trap — A5 lesson) and ungate `local_dev_scoped_filesystem`.
- `projection.rs` (`build_reborn_projection_services`, PRODUCTION): the
  EventStreamManager's throwaway `InMemoryOutboundStateStore::default()` becomes
  a fresh `FilesystemOutboundStateStore::new(wrap_scoped(InMemoryBackend))`
  (the budget-gate factory pattern — no `expect`, behavior-preserving). Store
  stays constructed inside the fn, so its ~65 test callers are untouched.
- Downstream test doubles (event_streams, product_workflow, composition slack/
  outbound/projection tests) repointed to the test-support helper; each
  consumer crate enables `ironclaw_outbound/test-support` in dev-deps.
- `outbound_state_store_contract.rs`: delete the now-redundant `in_memory_*`
  conformance test (the filesystem arm is a strict superset).
- R1 ratchet: drop `InMemoryOutboundStateStore` from the frozen allowlist.

Wire-safe: record schemas unchanged (the filesystem store reuses them).
Verified: outbound 110, event_streams 70, product_workflow 18, composition
projection 143 + slack 387 + outbound 76 tests pass; `cargo build -p
ironclaw_reborn_composition` (default + libsql) clean; clippy `-D warnings`
clean on all four crates; ratchet pass; fmt + pre-commit clean.

Stacked on #6210.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ilblackdragon added a commit that referenced this pull request Jul 18, 2026
…MemoryOutboundStateStore (§4.3)

Continues the arch-simplification §4.3 store consolidation: delete the
hand-written `InMemoryOutboundStateStore` (which implemented only
`CommunicationPreferenceRepository` + `OutboundStateStore`) and use the one
production `FilesystemOutboundStateStore` — which implements all four
outbound-store traits — over an in-memory backend.

Gap closed (strict improvement): the durable (libsql/postgres) composition
already shared one `FilesystemOutboundStateStore` across all four outbound
roles, while the no-durable path split `InMemoryOutboundStateStore` (prefs +
state) from separate `InMemory{DeliveredGateRoute,TriggeredRunDelivery}Store`
instances — a documented cross-store "gap". Both `local_dev_outbound_store`
cfg branches now collapse into one that shares a single
`FilesystemOutboundStateStore<CompositeRootFilesystem>` for every role
(`LocalDevRootFilesystem` is `CompositeRootFilesystem` in both builds), closing
the gap.

Changes:
- `ironclaw_outbound`: delete `memory.rs` (`InMemoryOutboundStateStore`, 264
  lines, no other exports); add a `test-support` feature + `test_support.rs`
  with `in_memory_backed_outbound_state_store()`. Own tests use
  local/`crate::` helpers (run_state pattern).
- Composition factory: merge the two `local_dev_outbound_store` cfg branches;
  make the `FilesystemOutboundStateStore` import unconditional (the
  cfg-gated-import trap — A5 lesson) and ungate `local_dev_scoped_filesystem`.
- `projection.rs` (`build_reborn_projection_services`, PRODUCTION): the
  EventStreamManager's throwaway `InMemoryOutboundStateStore::default()` becomes
  a fresh `FilesystemOutboundStateStore::new(wrap_scoped(InMemoryBackend))`
  (the budget-gate factory pattern — no `expect`, behavior-preserving). Store
  stays constructed inside the fn, so its ~65 test callers are untouched.
- Downstream test doubles (event_streams, product_workflow, composition slack/
  outbound/projection tests) repointed to the test-support helper; each
  consumer crate enables `ironclaw_outbound/test-support` in dev-deps.
- `outbound_state_store_contract.rs`: delete the now-redundant `in_memory_*`
  conformance test (the filesystem arm is a strict superset).
- R1 ratchet: drop `InMemoryOutboundStateStore` from the frozen allowlist.

Wire-safe: record schemas unchanged (the filesystem store reuses them).
Verified: outbound 110, event_streams 70, product_workflow 18, composition
projection 143 + slack 387 + outbound 76 tests pass; `cargo build -p
ironclaw_reborn_composition` (default + libsql) clean; clippy `-D warnings`
clean on all four crates; ratchet pass; fmt + pre-commit clean.

Stacked on #6210.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ilblackdragon added a commit that referenced this pull request Jul 18, 2026
…MemoryOutboundStateStore (§4.3) (#6212)

Continues the arch-simplification §4.3 store consolidation: delete the
hand-written `InMemoryOutboundStateStore` (which implemented only
`CommunicationPreferenceRepository` + `OutboundStateStore`) and use the one
production `FilesystemOutboundStateStore` — which implements all four
outbound-store traits — over an in-memory backend.

Gap closed (strict improvement): the durable (libsql/postgres) composition
already shared one `FilesystemOutboundStateStore` across all four outbound
roles, while the no-durable path split `InMemoryOutboundStateStore` (prefs +
state) from separate `InMemory{DeliveredGateRoute,TriggeredRunDelivery}Store`
instances — a documented cross-store "gap". Both `local_dev_outbound_store`
cfg branches now collapse into one that shares a single
`FilesystemOutboundStateStore<CompositeRootFilesystem>` for every role
(`LocalDevRootFilesystem` is `CompositeRootFilesystem` in both builds), closing
the gap.

Changes:
- `ironclaw_outbound`: delete `memory.rs` (`InMemoryOutboundStateStore`, 264
  lines, no other exports); add a `test-support` feature + `test_support.rs`
  with `in_memory_backed_outbound_state_store()`. Own tests use
  local/`crate::` helpers (run_state pattern).
- Composition factory: merge the two `local_dev_outbound_store` cfg branches;
  make the `FilesystemOutboundStateStore` import unconditional (the
  cfg-gated-import trap — A5 lesson) and ungate `local_dev_scoped_filesystem`.
- `projection.rs` (`build_reborn_projection_services`, PRODUCTION): the
  EventStreamManager's throwaway `InMemoryOutboundStateStore::default()` becomes
  a fresh `FilesystemOutboundStateStore::new(wrap_scoped(InMemoryBackend))`
  (the budget-gate factory pattern — no `expect`, behavior-preserving). Store
  stays constructed inside the fn, so its ~65 test callers are untouched.
- Downstream test doubles (event_streams, product_workflow, composition slack/
  outbound/projection tests) repointed to the test-support helper; each
  consumer crate enables `ironclaw_outbound/test-support` in dev-deps.
- `outbound_state_store_contract.rs`: delete the now-redundant `in_memory_*`
  conformance test (the filesystem arm is a strict superset).
- R1 ratchet: drop `InMemoryOutboundStateStore` from the frozen allowlist.

Wire-safe: record schemas unchanged (the filesystem store reuses them).
Verified: outbound 110, event_streams 70, product_workflow 18, composition
projection 143 + slack 387 + outbound 76 tests pass; `cargo build -p
ironclaw_reborn_composition` (default + libsql) clean; clippy `-D warnings`
clean on all four crates; ratchet pass; fmt + pre-commit clean.

Stacked on #6210.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

This branch was successfully deployed

No deployments
ironclaw-ci-preview / ironclaw-pr-6210 — d5a657f5 Deployed Jul 18, 2026 by railway-app[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: medium Business logic, config, or moderate-risk modules scope: dependencies Dependency updates size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant