Skip to content

refactor(reborn): make host_api::Resolution non-lossy for the CapabilityOutcome collapse (§5.3 Stage 1) - #6254

Merged
ilblackdragon merged 3 commits into
mainfrom
refactor/reborn-resolution-nonlossy
Jul 19, 2026
Merged

ilblackdragon merged 3 commits into
mainfrom
refactor/reborn-resolution-nonlossy

Conversation

@ilblackdragon

Copy link
Copy Markdown
Member

Stage 1 of the result-side collapse (§5.3)

Additive vocabulary work: make host_api::Resolution able to carry everything the
loop needs
so a later stage can delete CapabilityOutcome. CapabilityOutcome is
untouched, the loop_host seam's call to capability_outcome_to_resolution compiles
unchanged, and no loop-facing consumer moves. Nothing produces Resolution in
production yet, so the channel reshapes below are constructed only by host_api and
the mapping.

Fields added + which variant they live on + why they satisfy the charter

New crates/ironclaw_host_api/src/result_meta.rs — every type is plain redacted
vocabulary (bounded enum, fixed-width hash, or bounded validated safe identifier);
none carries a secret, raw HostPath, backend error string, or runtime handle:

Field / type Lives on Was Charter
error_kind: FailureKind (+ FailureKindValue) ToolVerdict::RecoverableFailure { error_kind } CapabilityFailure::error_kind (G1-dropped) bounded failure taxonomy (network/backend/…) + open Unknown; never the raw cause (raw detail stays host-side)
progress: ResultProgress Outcome::progress CapabilityResultMessage::progress (G4-dropped) bounded enum
terminate_hint: TerminateHint Outcome::terminate_hint CapabilityResultMessage::terminate_hint: bool (G4-dropped) bounded enum (bool modeled as named states, per task)
output_digest: Option<OutputDigest> OutcomeRefs::output_digest CapabilityResultMessage::output_digest (G4-dropped) fixed-width u64 Blake3 hash, never content
resume: Option<ResumeToken> GateWaypoint (Blocked::Approval/Auth) resume_token inside approval_resume/auth_resume (G1-dropped) opaque bounded (≤128B), control-free token; carries identity only
origin: Option<LoopRef> GateWaypoint, ProcessWaypoint, OutcomeRefs the loop result:*/gate:*/process:* ref (fresh-uuid-minted away) bounded correlation id; control chars + path delimiters refused; distinct type from the kernel refs

Channel reshapes in resolution.rs: Blocked::{Approval,Auth,Resource} and
Suspension::{DependentRun,ExternalTool} now hold a GateWaypoint (kernel GateRef

  • preserved origin + optional resume); Suspension::Process holds a
    ProcessWaypoint. ToolVerdict loses Copy (a FailureKind::Unknown owns a
    String). All Option additive fields use #[serde(default, skip_serializing_if)]
    so a bare waypoint serializes as just its handle; default-backed with_* builders
    per .claude/rules/default-builders.md.

Mapping enrichment

capability_outcome_to_resolution now populates every new field (deleting the
G1/G4 "dropped" doc comments and the ..-ignored resume tokens). The kernel uuid
handle stays freshly minted — the loop ref is preserved additively on origin, and
the MappedResolution { resolution, gate_record, deny_record, bindings } return
shape is unchanged.

Test-first round-trip coverage

Extended the existing resolution_mapping test module (no parallel suite) with four
cases asserting the new fields survive CapabilityOutcome → Resolution: a Failed
carries its error_kind; a Completed carries progress/terminate_hint/digest/origin;
an approval/auth gate carries its resume token + preserved origin; a spawned process
preserves its loop process ref on the channel. Watched them fail first by
neutralizing the enrichment — all four panicked for the right reason ("the approval
resume token must cross", "the recovery class must ride the verdict (was G1-dropped)",
origin/digest dropped) — then restored the enrichment to green. host_api gained unit
tests for every new type.

Charter tension resolved (in favor of the charter)

  1. Resume replay payload does not cross. CapabilityApprovalResume /
    CapabilityAuthResume bundle raw input: serde_json::Value + estimate +
    input/approval/correlation ids alongside the token. Only the opaque ResumeToken
    crosses; the raw replay is host execution context (charter forbids raw input in
    vocabulary). In the target §3 model input is by-ref and the host reconstitutes the
    replay from its own storage keyed by the token — it never needed to round-trip
    through the loop.
  2. Kept the fresh uuid mint instead of deriving it from the loop string. A
    from_seed(&str) deterministic derivation would have let the mapping "delete the
    fresh-uuid-minting" literally, but adding a text-taking constructor to the kernel
    ref types risks weakening the ids.rs invariant that kernel refs are opaque uuids
    "never composed from a caller string." I preserved the loop ref additively on
    origin instead — completeness without reopening that fork.

Is Resolution now a complete superset?

Yes for everything the loop needs. Recoverable-field coverage is complete: error_kind,
progress, terminate_hint, output_digest, resume token, and the originating loop ref
(gate/result/process) all round-trip. Deliberately not represented (host-side by
charter, not loop vocabulary), which Stage 2 must handle host-side:

  • CapabilityFailure::detail — raw backend cause (rides TurnLifecycleEvent.detail).
  • resume replay payload (raw input/estimate/prior-approval ids) — keyed by the token.
  • SpawnedProcess::safe_summary — no host process-summary channel exists.
  • A loop ref that fails host redaction (path/control chars — safe production refs never
    do) falls back to origin = None and stays reachable via the retained bindings.

Verification (all green)

  • cargo test -p ironclaw_host_api -p ironclaw_turns -p ironclaw_run_state -p ironclaw_loop_host --all-features — 0 failed
  • cargo clippy -p ironclaw_host_api -p ironclaw_turns -p ironclaw_loop_host --all-targets --all-features -- -D warnings — clean
  • cargo test -p ironclaw_architecture — 0 failed
  • cargo check --workspace --all-features — clean
  • scripts/pre-commit-safety.sh — exit 0

🤖 Generated with Claude Code

@ironloopai

ironloopai Bot commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

🔎 IronLoop Review Status

Head: 9ef2c6d5a23f4e5d6aa35cc1800f17d08eb9f347
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-19T02:50:24.515Z

Current reviewers:

Reviewer State Verdict Findings Last update
ironloop/common-reviewer (reviewer) Superseded N/A N/A 2026-07-19T02:24:58.419Z
Reviewer summaries
Reviewer Detail
ironloop/common-reviewer (reviewer) Superseded by a newer PR head. New head: e50c78e. Previous verdict: Changes requested.
Recent activity
Time Reviewer State Detail
2026-07-19T01:58:45.912Z ironloop/common-reviewer (reviewer) Queued Accepted review request for head 6665eb4.
2026-07-19T01:58:45.912Z ironloop/common-reviewer (reviewer) Queued Waiting for this reviewer lane to become available.
2026-07-19T01:58:46.397Z ironloop/common-reviewer (reviewer) Started Reviewer worker started.
2026-07-19T01:58:49.061Z ironloop/common-reviewer (reviewer) Workspace ready Prepared isolated checkout (merge_ref) at d2896bb.
2026-07-19T02:04:07.724Z ironloop/common-reviewer (reviewer) Result captured Changes requested; 1 blocking finding.
2026-07-19T02:04:07.724Z ironloop/common-reviewer (reviewer) Completed Review completed and terminal status was persisted.
2026-07-19T02:24:58.419Z ironloop/common-reviewer (reviewer) Superseded A newer PR head replaced this review (e50c78e).
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.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added richer result metadata, including progress, termination guidance, output identity, failure classifications, and resume tokens.
    • Preserved originating loop references across blocked, suspended, dependent-run, and spawned-process workflows.
    • Added support for resumable approval, authentication, and resource-blocked operations.
    • Improved dependent-run records with optional result-origin tracking.
  • Bug Fixes

    • Prevented outcome and recovery details from being lost during resolution mapping.
    • Maintained compatibility with previously stored records and existing serialized formats.

Walkthrough

The host API adds validated result metadata and waypoint wrappers, updates resolution wire shapes, and preserves loop origins, resume tokens, output metadata, failure kinds, and dependent-run origins through capability mapping and authorization paths.

Changes

Resolution metadata and waypoint propagation

Layer / File(s) Summary
Result metadata contracts
crates/ironclaw_host_api/src/result_meta.rs, crates/ironclaw_host_api/src/lib.rs
Adds validated metadata types for progress, termination, failure classification, output digests, resume tokens, and loop references, with crate-root exports and serde tests.
Waypoint and outcome resolution shapes
crates/ironclaw_host_api/src/resolution.rs
Replaces bare blocked/suspended references with waypoint wrappers and adds typed outcome metadata and recoverable failure classification, including wire-contract coverage.
Capability outcome metadata mapping
crates/ironclaw_turns/src/run_profile/resolution_mapping.rs, crates/ironclaw_host_api/src/gate_record.rs, crates/ironclaw_run_state/tests/gate_record_store_contract.rs
Carries loop origins, resume tokens, progress, termination hints, digests, failure kinds, and dependent-run result origins through resolution mapping and persistence tests.
Authorization blocked-gate construction
crates/ironclaw_capabilities/src/host.rs, crates/ironclaw_host_api/tests/authorized_seal.rs
Wraps approval gate references in GateWaypoint across authorization and resumed-authorization paths and updates blocked-result tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CapabilityOutcome
  participant ResolutionMapping
  participant HostResolution
  participant GateRecord
  CapabilityOutcome->>ResolutionMapping: provide outcome and loop-derived metadata
  ResolutionMapping->>HostResolution: create waypoint or typed outcome resolution
  ResolutionMapping->>GateRecord: persist dependent-run result origin
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: think-in-universe

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Conventional-commit style title accurately summarizes the non-lossy Resolution/CapabilityOutcome work.
Description check ✅ Passed The description is detailed and covers scope, rationale, and validation, but it does not follow the repository template sections exactly.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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: XL 500+ changed lines risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels Jul 19, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a non-lossy carry mechanism for capability outcomes by adding a new result_meta module to ironclaw_host_api. This module defines vocabulary types such as FailureKind, ResultProgress, TerminateHint, ResumeToken, and LoopRef to preserve loop-derived metadata, gate resume tokens, and originating loop references. The Blocked and Suspension enums, along with the ToolVerdict and Outcome structures, have been updated to carry these new waypoint and metadata structures, and the mapping logic in ironclaw_turns has been adjusted accordingly. The review feedback suggests two performance optimizations in result_meta.rs: using .bytes() instead of .chars() for ASCII validation to avoid UTF-8 decoding overhead, and implementing a custom Serde Visitor for FailureKind deserialization to prevent unnecessary memory allocations for static variants.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +425 to +428
if !value
.chars()
.all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | '.' | ':'))
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using .chars() to validate ASCII-only characters introduces unnecessary UTF-8 decoding overhead. Since the allowed character set consists entirely of ASCII characters, we can perform this check more efficiently by iterating over bytes using .bytes().

Suggested change
if !value
.chars()
.all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | '.' | ':'))
{
if !value
.bytes()
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-' | b'.' | b':'))
{

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in e50c78e — validate_safe_tag now checks bytes() (any non-ASCII byte fails is_ascii_alphanumeric, so semantics are unchanged).

Comment on lines +247 to +255
impl<'de> Deserialize<'de> for FailureKind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Ok(Self::from_tag(&value))
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Deserializing FailureKind by first deserializing into an owned String always allocates memory, even for the 18 known static variants that do not hold any inner data. We can avoid these allocations entirely by implementing a custom Serde Visitor that deserializes directly from a borrowed string slice (&str).

impl<'de> Deserialize<'de> for FailureKind {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct Visitor;
        impl<'de> serde::de::Visitor<'de> for Visitor {
            type Value = FailureKind;
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a failure kind string")
            }
            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Ok(FailureKind::from_tag(v))
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in e50c78e via Cow<'_, str>::deserialize — borrows when the input allows it, so the 18 named variants allocate nothing and only an Unknown tag takes an owned copy. (Kept the Cow form over a full Visitor impl: same allocation profile, a tenth of the code.)

@ironloopai ironloopai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ IronLoop Review: reviewer

Review at a glance

Verdict Blocking Notes Inline Head
❌ Changes requested 1 0 1 6665eb43bbc0

Head: 6665eb43bbc0b662739ddaccc8b979a4a7fded90
Next: Fix the blocking findings, push the PR branch, then re-run this reviewer.

Run details

Status: Current
Needs human: no
Needs validation: no

Summary

The dependent-run mapping still loses the staged result's loop origin, so Resolution is not yet non-lossy for that outcome.

Findings

Blocking: 1 / Notes: 0

Blocking findings

1. ❌ [HIGH] Preserve the dependent-run result origin

Location: crates/ironclaw_turns/src/run_profile/resolution_mapping.rs:302
AwaitDependentRun carries both a gate ref and a staged result_ref. This waypoint preserves only the gate origin; the minted ResultRef in GateRecord::DependentRun has no origin. bindings.result is outside Resolution and the current production seam persists only the gate record, so a Resolution-based consumer cannot associate this staged result with its loop result ref. Preserve the result origin alongside GateRecord::DependentRun (or durably persist the binding) and add a round-trip assertion.

Developer follow-up

After fixing this feedback:

  1. Push the fix to this PR branch.
  2. Re-run this reviewer with @ironloopai review --agent reviewer if you only changed this reviewer's findings.
  3. Re-run all reviewers with @ironloopai review when the fix may affect multiple areas.

} => {
let minted_gate = GateRef::new();
let minted_result = ResultRef::new();
let waypoint = gate_waypoint(minted_gate, &gate_ref, None);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This preserves the gate origin only. AwaitDependentRun also has a result_ref, but its minted GateRecord::DependentRun.result has no origin and bindings.result is not part of Resolution or persisted by the current seam. Preserve that result origin/durable binding so the staged child result remains reachable after migration.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and fixed in e50c78e. GateRecord::DependentRun gains result_origin: Option<LoopRef> — the staged result's originating loop ref now rides the durable record a later resume turn renders from (the minted ResultRef is a fresh uuid and RefBindings is transient, exactly as you noted). Serde default keeps rows persisted before the field rehydratable as None. The mapping populates it from result_ref, pinned by the mapping test (dependent_run_record_carries_staged_result_and_byte_len now asserts the origin) and the gate-record wire round-trip.

@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-result-wiring-seam branch from 56a3850 to 2f2d395 Compare July 19, 2026 02:15
ilblackdragon added a commit that referenced this pull request Jul 19, 2026
…origin on the durable record; review perf nits

- IronLoop: AwaitDependentRun's staged result_ref was preserved nowhere
  durable — the minted GateRecord::DependentRun.result is a fresh uuid
  and the RefBindings side-table is transient, so the child output the
  loop staged under its own ref would be unreachable from the record a
  later resume turn renders from. GateRecord::DependentRun gains
  result_origin: Option<LoopRef> (serde default — pre-existing rows
  rehydrate as None), the mapping populates it, and the mapping +
  wire tests pin it.
- Gemini: FailureKind deserializes via Cow<str> (no allocation for the
  18 named variants); validate_safe_tag checks bytes, not chars.

Reported-by: ironloopai, gemini-code-assist (PR #6254 review)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added scope: docs Documentation scope: dependencies Dependency updates labels Jul 19, 2026
ilblackdragon added a commit that referenced this pull request Jul 19, 2026
…origin on the durable record; review perf nits

- IronLoop: AwaitDependentRun's staged result_ref was preserved nowhere
  durable — the minted GateRecord::DependentRun.result is a fresh uuid
  and the RefBindings side-table is transient, so the child output the
  loop staged under its own ref would be unreachable from the record a
  later resume turn renders from. GateRecord::DependentRun gains
  result_origin: Option<LoopRef> (serde default — pre-existing rows
  rehydrate as None), the mapping populates it, and the mapping +
  wire tests pin it.
- Gemini: FailureKind deserializes via Cow<str> (no allocation for the
  18 named variants); validate_safe_tag checks bytes, not chars.

Reported-by: ironloopai, gemini-code-assist (PR #6254 review)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-resolution-nonlossy branch from e50c78e to 5edfc56 Compare July 19, 2026 02:25
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-result-wiring-seam branch from 2f2d395 to c7ba17e Compare July 19, 2026 02:26
@ilblackdragon ilblackdragon reopened this Jul 19, 2026
Base automatically changed from refactor/reborn-result-wiring-seam to main July 19, 2026 02:36
ilblackdragon added a commit that referenced this pull request Jul 19, 2026
…origin on the durable record; review perf nits

- IronLoop: AwaitDependentRun's staged result_ref was preserved nowhere
  durable — the minted GateRecord::DependentRun.result is a fresh uuid
  and the RefBindings side-table is transient, so the child output the
  loop staged under its own ref would be unreachable from the record a
  later resume turn renders from. GateRecord::DependentRun gains
  result_origin: Option<LoopRef> (serde default — pre-existing rows
  rehydrate as None), the mapping populates it, and the mapping +
  wire tests pin it.
- Gemini: FailureKind deserializes via Cow<str> (no allocation for the
  18 named variants); validate_safe_tag checks bytes, not chars.

Reported-by: ironloopai, gemini-code-assist (PR #6254 review)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-resolution-nonlossy branch from 5edfc56 to 7b4de44 Compare July 19, 2026 02:37
ilblackdragon and others added 3 commits July 19, 2026 02:44
…ityOutcome collapse (§5.3 Stage 1)

Additive vocabulary work so a later stage can delete `CapabilityOutcome`.
`host_api::Resolution` can now losslessly represent every recoverable field the
loop-facing `CapabilityOutcome` carried, and `capability_outcome_to_resolution`
populates them.

New host_api vocabulary (`result_meta.rs`, plain redacted vocabulary only —
bounded enums, a hash value, bounded validated identifiers; no secrets, raw
paths, backend error strings, or runtime handles):

- `FailureKind` (+ `FailureKindValue`) — recovery classification, on
  `ToolVerdict::RecoverableFailure { error_kind }` (was G1-dropped).
- `ResultProgress`, `TerminateHint`, `OutputDigest` — loop-derived completion
  signals, on `Outcome`/`OutcomeRefs` (were G4-dropped).
- `ResumeToken` — opaque gate-resume identity, on the gate `GateWaypoint`.
- `LoopRef` — preserved originating loop ref, on `GateWaypoint`/`ProcessWaypoint`
  origin and `OutcomeRefs.origin`.

Channel reshapes (constructed only by host_api + the mapping — no production
consumers yet): `Blocked`/`Suspension` variants now carry `GateWaypoint`/
`ProcessWaypoint` (kernel handle + preserved origin + optional resume token);
`OutcomeRefs` gains `origin`/`output_digest`; `Outcome` gains `progress`/
`terminate_hint`; `ToolVerdict::RecoverableFailure` gains `error_kind`.

The mapping enrichment deletes the G1/G4 "dropped" comments and populates the
new fields; a round-trip test (written test-first, watched fail with the
enrichment neutralized) pins each. The kernel uuid handle stays freshly minted
(its host-owned semantics) — the loop ref is preserved additively in `origin`
rather than smuggled into the uuid, keeping the "kernel refs are opaque uuids,
never caller-composed" invariant intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…origin on the durable record; review perf nits

- IronLoop: AwaitDependentRun's staged result_ref was preserved nowhere
  durable — the minted GateRecord::DependentRun.result is a fresh uuid
  and the RefBindings side-table is transient, so the child output the
  loop staged under its own ref would be unreachable from the record a
  later resume turn renders from. GateRecord::DependentRun gains
  result_origin: Option<LoopRef> (serde default — pre-existing rows
  rehydrate as None), the mapping populates it, and the mapping +
  wire tests pin it.
- Gemini: FailureKind deserializes via Cow<str> (no allocation for the
  18 named variants); validate_safe_tag checks bytes, not chars.

Reported-by: ironloopai, gemini-code-assist (PR #6254 review)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…st to the Blocked waypoint reshape

Semantic rebase fallout: this PR reshaped Blocked to carry GateWaypoint,
and the since-merged W1b/W1c authorize folds on main construct
Blocked::Approval at three kernel sites (plus the authorized_seal test).
Bare waypoints there are correct — the kernel witness's approval resume
rides the lease machinery; origin/resume waypoint fields are loop-mapping
concerns.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ilblackdragon
ilblackdragon force-pushed the refactor/reborn-resolution-nonlossy branch from 5e3b561 to 9ef2c6d Compare July 19, 2026 02:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ironclaw_host_api/src/result_meta.rs`:
- Around line 265-327: Refactor ResumeToken, LoopRef, and FailureKindValue to
follow the validated-newtype convention: add #[serde(try_from = "String")], move
validation into a shared validate(&str) method reused by fallible new, and
remove each hand-written Deserialize implementation. Preserve explicit accessors
and add as_ref or into_inner where required by the convention, without
introducing infallible string conversions or Deref implementations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: eb783006-51d0-4357-9528-0d3760cf7fca

📥 Commits

Reviewing files that changed from the base of the PR and between 9712113 and 5e3b561.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/Cargo.lock
📒 Files selected for processing (11)
  • crates/ironclaw_capabilities/src/host.rs
  • crates/ironclaw_host_api/src/gate_record.rs
  • crates/ironclaw_host_api/src/lib.rs
  • crates/ironclaw_host_api/src/resolution.rs
  • crates/ironclaw_host_api/src/result_meta.rs
  • crates/ironclaw_host_api/tests/authorized_seal.rs
  • crates/ironclaw_host_runtime/src/production.rs
  • crates/ironclaw_loop_host/Cargo.toml
  • crates/ironclaw_loop_host/src/capability_port.rs
  • crates/ironclaw_run_state/tests/gate_record_store_contract.rs
  • crates/ironclaw_turns/src/run_profile/resolution_mapping.rs

Comment on lines +265 to +327
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ResumeToken(String);

impl ResumeToken {
/// Maximum length in bytes — matches the loop's `CapabilityResumeToken` bound,
/// so any loop-minted token is representable losslessly.
pub const MAX_BYTES: usize = 128;

pub fn new(value: impl Into<String>) -> Result<Self, HostApiError> {
let value = value.into();
if value.is_empty() {
return Err(HostApiError::invalid_id(
"resume_token",
value,
"must not be empty",
));
}
if value.len() > Self::MAX_BYTES {
return Err(HostApiError::invalid_id(
"resume_token",
value,
format!("must be at most {} bytes", Self::MAX_BYTES),
));
}
if value.chars().any(|c| c == '\0' || c.is_control()) {
return Err(HostApiError::invalid_id(
"resume_token",
"<redacted>",
"must not contain NUL/control characters",
));
}
Ok(Self(value))
}

pub fn as_str(&self) -> &str {
&self.0
}
}

impl std::fmt::Display for ResumeToken {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.0)
}
}

impl Serialize for ResumeToken {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.0)
}
}

impl<'de> Deserialize<'de> for ResumeToken {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Self::new(value).map_err(serde::de::Error::custom)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Validated newtypes skip the mandated #[serde(try_from = "String")] pattern.

ResumeToken (265-327), LoopRef (343-411), and FailureKindValue (156-169) are new validated newtypes but hand-roll Deserialize and expose only as_str. The convention here is #[serde(try_from = "String")] + a shared validate(&str) + as_str/as_ref/into_inner. Functionally equivalent and secure as written, but aligning keeps the contract-crate boundary types uniform and forecloses an infallible-construction drift later.

As per coding guidelines: "New validated newtypes must use #[serde(try_from = "String")], a shared validate(&str), fallible new, explicit as_str/as_ref/into_inner methods, and must not implement infallible From<String>, From<&str>, or Deref<Target = str>."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_host_api/src/result_meta.rs` around lines 265 - 327, Refactor
ResumeToken, LoopRef, and FailureKindValue to follow the validated-newtype
convention: add #[serde(try_from = "String")], move validation into a shared
validate(&str) method reused by fallible new, and remove each hand-written
Deserialize implementation. Preserve explicit accessors and add as_ref or
into_inner where required by the convention, without introducing infallible
string conversions or Deref implementations.

Source: Coding guidelines

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/ironclaw_turns/src/run_profile/resolution_mapping.rs (1)

264-288: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an origin assertion for SpawnedChildRun.

Every other stage-1 non-lossy field (Completed's origin/digest/progress, Approval/Auth's resume+origin, SpawnedProcess's origin) has a dedicated test. SpawnedChildRun's new refs.origin (Line 277) has none — child_run_identity_is_preserved_on_the_verdict only checks child_run/byte_len.

     #[test]
     fn child_run_identity_is_preserved_on_the_verdict() {
         let child_run_id = TurnRunId::new();
         let mapped = capability_outcome_to_resolution(CapabilityOutcome::SpawnedChildRun {
             child_run_id,
             result_ref: result_ref(),
             safe_summary: "spawned".to_string(),
             byte_len: 64,
             model_observation: None,
         });
         match mapped.resolution {
             Resolution::Done(outcome) => {
                 assert_eq!(
                     outcome.verdict.child_run().map(|run| run.as_uuid()),
                     Some(child_run_id.as_uuid())
                 );
                 assert_eq!(outcome.refs.byte_len, 64);
+                assert_eq!(
+                    outcome.refs.origin.as_ref().map(LoopRef::as_str),
+                    Some(result_ref().as_str())
+                );
             }
             other => panic!("expected Done, got {other:?}"),
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_turns/src/run_profile/resolution_mapping.rs` around lines 264
- 288, The SpawnedChildRun mapping in the resolution mapper lacks test coverage
for preserving refs.origin. Extend the existing
child_run_identity_is_preserved_on_the_verdict test, or add a focused test
nearby, to assert that the mapped Outcome refs.origin matches the original
result_ref identity while retaining the existing child_run and byte_len
assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ironclaw_turns/src/run_profile/resolution_mapping.rs`:
- Around line 36-38: Update the module documentation near the `SpawnedProcess`
summary to state that `AwaitDependentRun`'s `model_observation` is dropped
because `GateRecord::DependentRun` has no preview field; retain the
result-preview behavior only for `SpawnedChildRun`.

---

Outside diff comments:
In `@crates/ironclaw_turns/src/run_profile/resolution_mapping.rs`:
- Around line 264-288: The SpawnedChildRun mapping in the resolution mapper
lacks test coverage for preserving refs.origin. Extend the existing
child_run_identity_is_preserved_on_the_verdict test, or add a focused test
nearby, to assert that the mapped Outcome refs.origin matches the original
result_ref identity while retaining the existing child_run and byte_len
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c4c881c6-0fbc-41b4-94fb-d379b0d7a51d

📥 Commits

Reviewing files that changed from the base of the PR and between 5e3b561 and 9ef2c6d.

📒 Files selected for processing (8)
  • crates/ironclaw_capabilities/src/host.rs
  • crates/ironclaw_host_api/src/gate_record.rs
  • crates/ironclaw_host_api/src/lib.rs
  • crates/ironclaw_host_api/src/resolution.rs
  • crates/ironclaw_host_api/src/result_meta.rs
  • crates/ironclaw_host_api/tests/authorized_seal.rs
  • crates/ironclaw_run_state/tests/gate_record_store_contract.rs
  • crates/ironclaw_turns/src/run_profile/resolution_mapping.rs

Comment on lines +36 to +38
//! `SpawnedProcess`'s `safe_summary` still has no host channel (a process
//! suspension carries a [`ProcessRef`], not a summary). `AwaitDependentRun`'s and
//! `SpawnedChildRun`'s `model_observation` ride the result preview where present.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Doc contradicts the actual AwaitDependentRun mapping.

This doc claims AwaitDependentRun's model_observation "ride[s] the result preview where present," but the match arm (Line 298, ..) drops it entirely, and its own inline comment (Lines 289-292) says it "has no home on DependentRun and is dropped." GateRecord::DependentRun has no preview field to ride. Only SpawnedChildRun actually carries the preview (Line 276). Fix the doc to avoid misleading future readers about what's non-lossy here.

📝 Proposed doc fix
-//! `SpawnedProcess`'s `safe_summary` still has no host channel (a process
-//! suspension carries a [`ProcessRef`], not a summary). `AwaitDependentRun`'s and
-//! `SpawnedChildRun`'s `model_observation` ride the result preview where present.
+//! `SpawnedProcess`'s `safe_summary` still has no host channel (a process
+//! suspension carries a [`ProcessRef`], not a summary). `SpawnedChildRun`'s
+//! `model_observation` rides the result preview where present; `AwaitDependentRun`'s
+//! `model_observation` has no home on `GateRecord::DependentRun` and is dropped.

As per coding guidelines, "Comments and documentation that promise guarantees must match the code and tests."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//! `SpawnedProcess`'s `safe_summary` still has no host channel (a process
//! suspension carries a [`ProcessRef`], not a summary). `AwaitDependentRun`'s and
//! `SpawnedChildRun`'s `model_observation` ride the result preview where present.
//! `SpawnedProcess`'s `safe_summary` still has no host channel (a process
//! suspension carries a [`ProcessRef`], not a summary). `SpawnedChildRun`'s
//! `model_observation` rides the result preview where present; `AwaitDependentRun`'s
//! `model_observation` has no home on `GateRecord::DependentRun` and is dropped.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_turns/src/run_profile/resolution_mapping.rs` around lines 36
- 38, Update the module documentation near the `SpawnedProcess` summary to state
that `AwaitDependentRun`'s `model_observation` is dropped because
`GateRecord::DependentRun` has no preview field; retain the result-preview
behavior only for `SpawnedChildRun`.

Source: Coding guidelines

@ilblackdragon
ilblackdragon merged commit c533e80 into main Jul 19, 2026
64 checks passed
@ilblackdragon
ilblackdragon deleted the refactor/reborn-resolution-nonlossy branch July 19, 2026 03:08
ilblackdragon added a commit that referenced this pull request Jul 19, 2026
…ad (§5.3) (#6256)

* refactor(reborn): make host_api::Resolution non-lossy for the CapabilityOutcome collapse (§5.3 Stage 1)

Additive vocabulary work so a later stage can delete `CapabilityOutcome`.
`host_api::Resolution` can now losslessly represent every recoverable field the
loop-facing `CapabilityOutcome` carried, and `capability_outcome_to_resolution`
populates them.

New host_api vocabulary (`result_meta.rs`, plain redacted vocabulary only —
bounded enums, a hash value, bounded validated identifiers; no secrets, raw
paths, backend error strings, or runtime handles):

- `FailureKind` (+ `FailureKindValue`) — recovery classification, on
  `ToolVerdict::RecoverableFailure { error_kind }` (was G1-dropped).
- `ResultProgress`, `TerminateHint`, `OutputDigest` — loop-derived completion
  signals, on `Outcome`/`OutcomeRefs` (were G4-dropped).
- `ResumeToken` — opaque gate-resume identity, on the gate `GateWaypoint`.
- `LoopRef` — preserved originating loop ref, on `GateWaypoint`/`ProcessWaypoint`
  origin and `OutcomeRefs.origin`.

Channel reshapes (constructed only by host_api + the mapping — no production
consumers yet): `Blocked`/`Suspension` variants now carry `GateWaypoint`/
`ProcessWaypoint` (kernel handle + preserved origin + optional resume token);
`OutcomeRefs` gains `origin`/`output_digest`; `Outcome` gains `progress`/
`terminate_hint`; `ToolVerdict::RecoverableFailure` gains `error_kind`.

The mapping enrichment deletes the G1/G4 "dropped" comments and populates the
new fields; a round-trip test (written test-first, watched fail with the
enrichment neutralized) pins each. The kernel uuid handle stays freshly minted
(its host-owned semantics) — the loop ref is preserved additively in `origin`
rather than smuggled into the uuid, keeping the "kernel refs are opaque uuids,
never caller-composed" invariant intact.

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

* fix(host_api,turns): preserve the dependent-run staged result's loop origin on the durable record; review perf nits

- IronLoop: AwaitDependentRun's staged result_ref was preserved nowhere
  durable — the minted GateRecord::DependentRun.result is a fresh uuid
  and the RefBindings side-table is transient, so the child output the
  loop staged under its own ref would be unreachable from the record a
  later resume turn renders from. GateRecord::DependentRun gains
  result_origin: Option<LoopRef> (serde default — pre-existing rows
  rehydrate as None), the mapping populates it, and the mapping +
  wire tests pin it.
- Gemini: FailureKind deserializes via Cow<str> (no allocation for the
  18 named variants); validate_safe_tag checks bytes, not chars.

Reported-by: ironloopai, gemini-code-assist (PR #6254 review)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(capabilities,host_api): adapt merged W1 authorize folds + seal test to the Blocked waypoint reshape

Semantic rebase fallout: this PR reshaped Blocked to carry GateWaypoint,
and the since-merged W1b/W1c authorize folds on main construct
Blocked::Approval at three kernel sites (plus the authorized_seal test).
Bare waypoints there are correct — the kernel witness's approval resume
rides the lease machinery; origin/resume waypoint fields are loop-mapping
concerns.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(reborn): host-private ReplayPayloadStore for gate/auth resume-read (§5.3)

Adds the persistence slice that unblocks the capability-result collapse
(docs/reborn/2026-07-17-architecture-simplification-dto-dyn-local.md §3/§5.3).
Mirrors the sibling GateRecordStore (#6243) but is unwired: it provides the
port + a Filesystem implementation + contract tests only. A later slice
(Stage 2a) wires write-at-gate-raise + read-on-resume and flips CapabilityOutcome.

Today the raw replay payload rides in-band through the UNTRUSTED loop on
CapabilityApprovalResume/CapabilityAuthResume and is stashed in the loop's own
serialized checkpoint (raw input+estimate). The collapse makes the loop-facing
Resolution carry only an opaque resume token (== InvocationId), so the host must
persist the replay payload itself and reconstitute it on resume. Moving it
host-side ALSO retires a real exposure — raw tool input no longer round-trips
through the loop's checkpoint. Host-privacy is a security requirement here.

Record schema (host-private; the opposite of a model-visible GateRecord — no
SafeSummary): ReplayPayload { input: serde_json::Value, estimate: ResourceEstimate,
prior_approval: Option<AuthResumeApprovalIdentity>, input_ref: CapabilityInputRef,
correlation_id: CorrelationId }. The field types are imported from their owners
(ironclaw_turns for CapabilityInputRef/AuthResumeApprovalIdentity, host_api for
the rest), not re-typed — no lossy re-typing per type-placement.md.

Placement: ironclaw_capabilities, NOT ironclaw_run_state. The run_state charter
(CLAUDE.md line 7) forbids persisting raw replay input in run-state records, and
the ironclaw_turns charter forbids persisting raw tool input in turn state/events
— both candidate "clean" homes are charter-hostile to raw replay input. capabilities
owns the caller-facing invoke/resume/spawn workflow this payload exists to serve and
has no such prohibition (type-placement.md §2). The two new dependency edges
(capabilities -> turns, capabilities -> filesystem) are both permitted by the layer
matrix (kernel -> kernel, kernel -> substrates) and match existing kernel topology
(host_runtime -> turns; run_state -> filesystem); ironclaw_architecture is green.

CAS/mount lane reused from the sibling: ScopedFilesystem<F: RootFilesystem> +
shared cas_update (fail-closed CasUnsupported on non-CAS backends), a
replay_payload_record RecordKind gate rejecting byte-only backends, a private
StoredReplayPayload wrapper carrying the scope for a same_scope_owner
defense-in-depth check, and a new /replay-payloads mount alias (tenant/user in the
MountView, within-tenant axes in the alias-relative path). save() is write-once
(dup InvocationId -> ReplayPayloadAlreadyExists); load() returns Option. No removal
method — the payload is consumed once on resume; a later retention contract can add
deletion (database.md "Data safety").

Test-first: 6 contract tests mirror gate_record_store_contract.rs and were watched
red against a stub before implementing — all-fields round-trip (raw input+estimate
survive), the auth-without-prior-approval shape, missing -> None, write-once dup
rejection (original intact), and cross-tenant + within-tenant scope isolation
(tenant1 vs tenant2; project A vs B look unknown).

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

---------

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

Copy link
Copy Markdown
Contributor

Coverage ratchet

Ratchet mode: ENFORCING

RATCHET PASS: global
  observed: 85.54% (311753 / 364435 lines)
  floor:    85.3% (tolerance 0.5pp -> effective floor 84.8%)
  denominator: 364435 lines now vs 320188 at floor capture (+44247 lines, +13.82%) — material change (>5%)

⚠️ 2 Reborn crate(s) have 0 int-tier coverage (target: 0) — ironclaw_prompt_envelope, ironclaw_scripts

Reborn integration-tier coverage

Line coverage (Reborn crates): 85.54% — 311753 / 364435 lines

Per-crate breakdown (65 crates, lowest-covered first)
Crate Line % Covered / Total
ironclaw_prompt_envelope 0% 0 / 88
ironclaw_scripts 0% 0 / 345
ironclaw_runtime_policy 31.75% 80 / 252
ironclaw_event_projections 43.31% 673 / 1554
ironclaw_observability 61.54% 16 / 26
ironclaw_authorization 62.46% 604 / 967
ironclaw_dispatcher 62.88% 83 / 132
ironclaw_mcp 64.89% 595 / 917
ironclaw_triggers 65.44% 2142 / 3273
ironclaw_filesystem 67.78% 3957 / 5838
ironclaw_channel_host 68.65% 219 / 319
ironclaw_memory 69.2% 773 / 1117
ironclaw_reborn_migration 71.64% 1551 / 2165
ironclaw_trust 72.88% 661 / 907
ironclaw_wasm_limiter 74.6% 47 / 63
ironclaw_reborn_event_store 74.67% 958 / 1283
ironclaw_extractors 74.72% 538 / 720
ironclaw_reborn_cli 75.17% 8946 / 11901
ironclaw_capabilities 75.46% 2026 / 2685
ironclaw_projects 76.48% 400 / 523
ironclaw_llm 78.36% 20306 / 25915
ironclaw_product_context 78.57% 11 / 14
ironclaw_run_state 79.25% 424 / 535
ironclaw_telegram_extension 80.18% 4842 / 6039
ironclaw_wasm_product_adapters 80.36% 1448 / 1802
ironclaw_process_sandbox 80.65% 671 / 832
ironclaw_first_party_extensions 81.06% 5965 / 7359
ironclaw_memory_native 81.17% 3195 / 3936
ironclaw_events 81.95% 1594 / 1945
ironclaw_secrets 82.78% 2827 / 3415
ironclaw_network 82.98% 673 / 811
ironclaw_reborn_identity 83.59% 433 / 518
ironclaw_processes 83.76% 939 / 1121
ironclaw_reborn_config 84.04% 1832 / 2180
ironclaw_wasm 84.44% 1069 / 1266
ironclaw_auth 84.81% 3233 / 3812
ironclaw_product_workflow 84.91% 11031 / 12992
ironclaw_turns 85.65% 14438 / 16856
ironclaw_channel_delivery 85.79% 1383 / 1612
ironclaw_common 86.13% 1714 / 1990
ironclaw_threads 86.93% 4708 / 5416
ironclaw_slack_v2_adapter 87.3% 1491 / 1708
ironclaw_skills 87.58% 4470 / 5104
ironclaw_hooks 87.78% 9921 / 11302
ironclaw_reborn_composition 87.96% 70209 / 79816
ironclaw_product_adapter_registry 88.06% 531 / 603
ironclaw_host_api 88.1% 3894 / 4420
ironclaw_product_adapters 88.1% 3384 / 3841
ironclaw_reborn_traces 88.2% 11946 / 13544
ironclaw_host_runtime 88.68% 18033 / 20334
ironclaw_webui 88.9% 7652 / 8607
ironclaw_extensions 89.38% 2971 / 3324
ironclaw_reborn_openai_compat 89.5% 3778 / 4221
ironclaw_runner 89.64% 17364 / 19370
ironclaw_telegram_v2_adapter 89.7% 2717 / 3029
ironclaw_approvals 90.18% 1598 / 1772
ironclaw_conversations 90.39% 3123 / 3455
ironclaw_event_streams 90.82% 1009 / 1111
ironclaw_resources 91.65% 4476 / 4884
ironclaw_loop_host 92.28% 15567 / 16870
ironclaw_attachments 93.06% 630 / 677
ironclaw_agent_loop 94.88% 9184 / 9680
ironclaw_safety 95.04% 3677 / 3869
ironclaw_outbound 95.52% 3451 / 3613
ironclaw_first_party_extension_ports 95.62% 3672 / 3840

This table itself is informational and never gates the PR on its own — not the percentage, not the per-crate holes, not the 0-coverage callout. A separate coverage ratchet (dry-run until enforce=true; see tests/integration/coverage-floor.toml) can fail the build on specific configured floors.

Exemptions (3 entry/entries excluded from the accounting above)
Module / Crate Reason Issue
crate: ironclaw_embeddings v1-only: consumed only by root ironclaw (src/app.rs, src/tools/builtin/memory.rs, src/workspace/mod.rs, src/config/{mod,embeddings}.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_gateway v1-only: consumed only by root ironclaw (src/channels/web/platform/static_files.rs, src/channels/web/handlers/frontend.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_tui v1-only: consumed only by root ironclaw (src/main.rs, src/channels/tui.rs); no crates/* dependents. Crate's own doc comment confirms it bridges INTO v1, not Reborn. Covered by "Tests (Legacy)". #5657

@ilblackdragon

Copy link
Copy Markdown
Member Author

✅ Ready for merge

Reviewed, all three review comments addressed with fixes, restacked onto main (twice — past the #6245 seam merge and the W1 chain), CI fully green — 58 pass / 0 fail.

  • Stage 1 non-lossy Resolution: charter-clean vocabulary throughout (bounded FailureKind taxonomy with validated open Unknown, opaque control-free ResumeToken, path-refusing LoopRef, u64-only OutputDigest), invariant fields on variants, wire-revalidating deserializes, skip_serializing_if on every additive field.
  • IronLoop's non-lossy gap — confirmed and fixed: the dependent-run staged result's loop ref lived only in the transient bindings side-table; GateRecord::DependentRun now carries result_origin on the durable record (serde default keeps pre-existing rows rehydratable), populated by the mapping and pinned by both the mapping and wire tests.
  • Gemini applied: FailureKind deserializes via Cow<str> (zero alloc for the 18 named variants); tag validation is byte-wise.
  • Semantic-rebase fallout from the restacks handled: the Blocked waypoint reshape adapted at the three merged-W1 kernel sites (bare waypoints — origin/resume are loop-mapping concerns) and the seal test.
  • Verified: 793 + 266 tests across the stack, workspace clippy -D warnings clean.

🤖 Generated with Claude Code

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: low Changes to docs, tests, or low-risk modules scope: dependencies Dependency updates scope: docs Documentation size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant