Skip to content

refactor: align Strand with static typestates and close gap analysis specs#545

Merged
flyingrobots merged 38 commits into
mainfrom
refactor/braid-shell-member
Jun 15, 2026
Merged

refactor: align Strand with static typestates and close gap analysis specs#545
flyingrobots merged 38 commits into
mainfrom
refactor/braid-shell-member

Conversation

@flyingrobots

@flyingrobots flyingrobots commented Jun 14, 2026

Copy link
Copy Markdown
Owner

This PR closes all gaps from the gap analysis report:

  • Gap 1 (Strand Typestates): Statically parameterizes Strand with CausalPostureState.
  • Gap 2 (Blinded Member References): Adds BraidMemberRef supporting Sealed/Revealed.
  • Gap 3 (Proof Envelopes): Incorporates ProofEnvelope validating ObserverHonestyClaim.
  • Gap 4 (Evolving Braid Log): Implements BraidEvent folding logic.

Summary by CodeRabbit

  • New Features
    • Added proof envelopes for braid-shell witness identity and validation.
    • Introduced event-sourced braids with explicit lifecycle, membership tracking, and collapse handling.
    • Added posture-typed strands with stronger retention posture propagation and shared-only planning/settlement gating.
  • Changed
    • Settlement planning/settlement now only works with shared-admitted strands; compare remains local inspection.
    • Braid-shell membership identity and settlement artifacts are now posture-aware (including revealed vs sealed members).
    • Contract package API renamed from installation to registration across the host/runtime surface.
  • Fixed
    • Non-shared admission rejections now map consistently to stable INVALID_STRAND.
  • Documentation
    • Updated design specs and quickstart/host-loop guidance to reflect typestates, proof envelopes, evolving braids, and registration wording.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@flyingrobots, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 36 minutes and 46 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b0566b74-34fa-4b25-81de-b04a02750f92

📥 Commits

Reviewing files that changed from the base of the PR and between 0fe395b and 0a61307.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • crates/warp-core/src/braid.rs
  • crates/warp-core/src/braid_shell.rs
  • crates/warp-core/src/lib.rs
  • crates/warp-core/src/revelation.rs
  • crates/warp-core/src/settlement.rs
  • docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md
📝 Walkthrough

Walkthrough

Introduces compile-time causal posture typestates on Strand<P: CausalPostureState>, gates settlement plan/settle on Strand<Shared>, replaces BraidShellMember.strand_ref with sealed/revealed BraidMemberRef identity, adds ProofEnvelope verification on BraidShell assembly, introduces an event-sourced Braid log with BraidEvent lifecycle and fold validation, propagates RetentionPosture through ForkStrandRequest/ForkStrandReceipt, and maps SettlementError::NonSharedStrand to INVALID_STRAND at the WASM ABI boundary.

Changes

Strand Typestates, Posture Gating, and Evolving Braids

Layer / File(s) Summary
CausalPostureState trait and marker types
crates/warp-core/src/revelation.rs
Adds CausalPostureState trait and Shared/AuthorOnly/Scratch/DynamicPosture marker implementations; extends RetentionPosture with source_disclosure field and validate(); adds SessionContext::retention_posture() and debugger_retention_posture() constructors; adds PostureObstruction::UnexpectedSourceDisclosure and PostureMismatch variants with validation helpers.
ProofKind, ProofEnvelope, and module wiring
crates/warp-core/src/proof.rs, crates/warp-core/src/lib.rs
Adds ProofKind enum with canonical wire tags, ProofEnvelope with validate_shape() and digest() methods, ObserverHonestyClaim struct; wires proof as public module; re-exports proof types and expands Braid/revelation re-exports.
Strand

generic, posture conversions, retention_posture field, and registry validation
crates/warp-core/src/strand.rs

Refactors Strand to Strand<P: CausalPostureState = DynamicPosture>; adds retention_posture and PhantomData<P> fields; introduces into_dynamic() and try_into_shared() conversions; gates plan()/settle() on Strand<Shared> impl; adds StrandError::Posture; validates posture in StrandRegistry::insert.
BraidMemberRef sealed/revealed identity and member query refactoring
crates/warp-core/src/braid_shell.rs
Introduces BraidMemberRef enum with Revealed(StrandId) and Sealed { blinded_commitment, authority } variants; replaces strand_ref with member_ref; updates canonical digest, duplicate detection, and member queries (has_revealed_member_strand, has_member_strand_secure); updates BraidShellReplay::member_verdicts type and query structure.
ProofEnvelope validation in BraidShell assembly and digest binding
crates/warp-core/src/braid_shell.rs
Adds BraidShellError::ProofShapeValidationFailed; extends BraidShell with optional proof field; implements assemble_with_proof() validating proof against witness_digest; threads proof_digest into shell digest computation; updates validate() to re-verify stored proof.
Event-sourced Braid lifecycle with BraidStatus, BraidEvent, and fold validation
crates/warp-core/src/braid.rs
Adds BraidStatus enum (Active/Finalized/Collapsed) and BraidError enum with typed variants; introduces BraidEvent lifecycle events and Braid struct with event log and folded state; implements new/apply/fold with lifecycle/sequence/duplicate validation; includes comprehensive unit test coverage.
RetentionPosture in ForkStrandRequest/Receipt and posture hashing
crates/warp-core/src/coordinator.rs
Adds retention_posture field to ForkStrandRequest and ForkStrandReceipt; adds from_session_default() and debugger_default() constructors; extends StrandError::Posture hashing with posture-obstruction helpers; adds test helpers and session-default/debugger posture tests.
Settlement admission gating on Strand and posture-aware braid shells
crates/warp-core/src/settlement.rs
Adds SettlementError::NonSharedStrand; refactors compare to compare_internal generic over posture; gates plan/settle on Strand<Shared> via shared_strand helper; threads retention_posture into build_braid_shell selecting Revealed vs Sealed BraidMemberRef based on posture; propagates CausalPosture into plural drafts and braid shells; updates test assertions to shared posture.
WASM ABI error mapping and test setup with posture fixtures
crates/warp-wasm/src/warp_kernel.rs, crates/warp-wasm/src/lib.rs
Maps SettlementError::NonSharedStrand to INVALID_STRAND ABI code; updates StubKernel::settle_strand result fields; adds posture fixture helpers; refactors setup_runtime_with_strand_posture; adds regression test for compare vs plan/settle behavior under AuthorOnly posture.
Strand contract, neighborhood, and observation test fixtures
crates/warp-core/tests/strand_contract_tests.rs, crates/warp-core/src/neighborhood.rs, crates/warp-core/src/observation.rs
Adds test_retention_posture() helpers across test modules; populates retention_posture and PhantomData<DynamicPosture> in all Strand literals; adds posture validation tests (shared_posture_without_admission_scope, try_into_shared_mismatch).
Provenance, braid API, and documentation
crates/warp-core/src/provenance_store.rs, crates/warp-core/tests/braid_public_api_tests.rs, docs/design/0028-*, backlog/cool-ideas/CI-003-*, CHANGELOG.md
Updates minimal_plural_shell to use BraidMemberRef::Revealed; adds braid_public_api_tests validating crate-root exports; adds design doc 0028 covering posture typestates, sealed references, proof envelopes, and braid log lifecycle; adds CI-003 backlog for append-only membership; updates CHANGELOG with explicit retention posture propagation, settlement rejection, and WASM ABI mapping entries.
Contract registration rename and test updates
crates/warp-core/src/engine_impl.rs, crates/warp-core/src/trusted_runtime_host.rs, crates/warp-core/tests/*, docs/design/reference-*.md, docs/quickstart-*.md
Renames install_contract_package to register_contract_package across engine/host boundary; updates all test call sites; adds lint suppressions; updates documentation terminology from installation to registration.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Settlement Caller
  participant Strand as Strand<P>
  participant SettlementService
  participant BraidShell
  
  Client->>Strand: try_into_shared()
  alt posture == Shared
    Strand-->>Client: Ok(Strand<Shared>)
    Client->>Strand: plan(runtime, provenance)
    Strand->>SettlementService: plan_with_policy(shared_strand, ...)
    SettlementService->>SettlementService: shared_strand validation
    SettlementService->>BraidShell: build_braid_shell(retention_posture)
    BraidShell->>BraidShell: select Revealed(id) vs Sealed(commitment)
    BraidShell-->>SettlementService: BraidShell
    SettlementService-->>Strand: SettlementPlan
    Strand-->>Client: Ok(SettlementPlan)
  else posture != Shared
    Strand-->>Client: Err(PostureMismatch)
    Client->>SettlementService: plan_with_policy(dynamic_strand, ...)
    SettlementService->>SettlementService: try_into_shared() fails
    SettlementService-->>Client: NonSharedStrand { posture }
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • flyingrobots/echo#539: Directly precedes this PR on the retained settlement/theta-braid-shell path—adds posture-aware braid-shell and plural settlement record construction that this PR extends with NonSharedStrand gating and RetentionPosture threading.
  • flyingrobots/echo#542: Overlaps on braid_shell.rs and posture wiring—the CausalPosture migration from that PR is the direct upstream foundation for the typestate generics introduced here.
  • flyingrobots/echo#315: Introduced the warp-core::strand contract types that this PR refactors into Strand<P> with retention_posture field and StrandRegistry validation.

Suggested labels

security

Poem

⚗️ A Strand<Shared> may settle and plan,
But AuthorOnly strands—they simply can't.
BraidMemberRef::Sealed hides who you are,
While ProofEnvelope::validate raises the bar.
BraidEvent::fold keeps the log in line—
No out-of-order members shall unwind. 🔐

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/braid-shell-member

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d9460957f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/warp-core/src/settlement.rs Outdated
Comment thread crates/warp-core/src/lib.rs Outdated
Comment thread crates/warp-core/src/braid_shell.rs Outdated
Comment thread crates/warp-core/src/braid.rs Outdated
Comment thread crates/warp-core/src/proof.rs
Comment thread crates/warp-core/src/settlement.rs Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (1)
crates/warp-core/src/coordinator.rs (1)

2828-2962: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add a digest-lock regression test for the new StrandError::Posture hashing path.

The new posture obstruction hashing matrix is deterministic, but it is currently protected only by implementation details. A small tag rename here will silently change scheduler fault digests.

Suggested test addition
+    #[test]
+    fn runtime_error_fault_digest_includes_posture_obstruction_payload() {
+        let err = RuntimeError::Strand(StrandError::Posture(
+            PostureObstruction::MissingAdmissionScope {
+                posture: CausalPosture::Shared,
+            },
+        ));
+        let digest = scheduler_error_cause_digest(&err);
+
+        let mut expected = blake3::Hasher::new();
+        expected.update(b"echo.scheduler-fault-cause.error");
+        expected.update(b"strand");
+        expected.update(b"posture");
+        expected.update(b"missing-admission-scope");
+        expected.update(&[CausalPosture::Shared.canonical_tag()]);
+        let expected: Hash = expected.finalize().into();
+
+        assert_eq!(digest, expected);
+    }

As per coding guidelines, crates/warp-core/** is the deterministic kernel, so hash-stability regressions should be pinned by tests.

🤖 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/warp-core/src/coordinator.rs` around lines 2828 - 2962, The new
hashing code for StrandError::Posture variant and the hash_posture_obstruction
function, along with its helper functions hash_causal_posture and
hash_posture_derivation, lack regression test coverage to ensure hash stability.
A small change to any of the tag constants (like the b"narrowing-refused"
strings) would silently change the scheduler fault digests without detection.
Add a digest-lock regression test in the appropriate test module that verifies
the computed hash digests for StrandError::Posture and all PostureObstruction
variants match expected pinned values, ensuring that any tag renames or hashing
logic changes are intentional and tracked.

Source: Coding guidelines

🤖 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/warp-core/src/braid_shell.rs`:
- Around line 679-692: The duplicate detection logic in the pattern match within
check_unique_member_strands compares sealed member references only by their
blinded_commitment value, but sealed member identity should include both
blinded_commitment and authority. Update the sealed reference comparison branch
to extract and compare both the blinded_commitment and authority fields from
each BraidMemberRef::Sealed variant, ensuring that sealed members with the same
commitment but different authorities are not incorrectly treated as duplicates.

In `@crates/warp-core/src/braid.rs`:
- Around line 87-128: The fold method currently returns Result<Self, String>
which prevents callers from pattern-matching specific error types. Define a new
structured error enum (such as BraidFoldError with variants for
EmptyEventStream, MissingInitialCreation, DuplicateCreation, and
SequenceMismatch) and update the fold method's return type to Result<Self,
BraidFoldError>. Then replace all the string error returns in the fold method
with the appropriate enum variant constructors instead of creating String
errors.
- Around line 33-46: The documentation comment for the `members` field in the
Braid struct claims it is a "Set of current member references" but the
implementation uses a `Vec` which can contain duplicates. Either update the doc
comment on the `members` field to accurately reflect that it is an "Ordered list
of woven member references" instead of a set, or modify the `apply` method to
enforce uniqueness constraints when adding members to the Vec to match the set
semantics described in the documentation.
- Around line 68-85: The apply method has an integer overflow vulnerability at
the MemberWoven event handler where sequence_num is incremented with
`sequence_num + 1`. When sequence_num reaches u64::MAX, this unchecked addition
will panic in debug builds and wrap to zero in release builds, violating
determinism guarantees. Replace the unchecked addition with checked arithmetic
(such as checked_add) that will either safely handle the increment or return an
error that should be propagated appropriately, ensuring deterministic behavior
regardless of the input values.
- Around line 68-85: The `apply` method is public and accepts BraidEvent without
validating state transitions, allowing invalid operations like overwriting
braid_id multiple times, updating sequence_num out of order, or adding duplicate
members. Either restrict the method visibility from `pub` to `pub(crate)` to
limit direct calls, or add validation guards within the match arms: for
BraidCreated, verify braid_id hasn't been set; for MemberWoven, verify
sequence_num matches expected next_sequence_num and the member_ref isn't already
in self.members; for SettlementFinalized, verify the new settlement hasn't been
finalized already.

In `@crates/warp-core/src/coordinator.rs`:
- Line 3397: The pull request fails required gates and must pass all checks
before merging. Fix the cargo clippy violations: replace expect_used calls in
crates/warp-core/src/observation.rs and crates/warp-core/src/settlement.rs with
proper error handling (using Result types or the ? operator), address
large_types_passed_by_value violations by passing large types by reference, and
remove redundant_clone calls. Additionally, fix the ban-globals.sh check
failures by removing or refactoring install_contract_package pattern matches in
crates/warp-core/src/coordinator.rs and related files. Run cargo clippy
--all-targets and bash scripts/ban-globals.sh locally to identify all violations
and fix them systematically until both gates pass cleanly.

In `@crates/warp-core/src/proof.rs`:
- Around line 37-47: The verify method in ProofEnvelope is not performing actual
cryptographic proof verification. Currently it only checks if proof_bytes is
non-empty and validates the public_inputs_hash, but ignores the kind field and
does not actually verify the proof cryptographically. Fix this by adding logic
that uses the kind field to determine which proof verification function to call,
and invoke the appropriate cryptographic verification on the proof_bytes. This
will ensure that proofs are actually validated according to their type rather
than simply accepting any non-empty payload.

In `@crates/warp-core/src/settlement.rs`:
- Around line 1424-1436: The blinded_commitment computation in the
BraidMemberRef::Sealed branch lacks the required randomness for a cryptographic
commitment scheme. The hash currently only includes a constant prefix and the
strand_id, making it vulnerable to enumeration attacks. Modify the
commitment_hasher in the else branch (when strand_posture is not
CausalPosture::Shared) to include authority-derived entropy by updating the
hasher with the retention_posture.authority field before finalizing. This
incorporates the necessary randomness to provide the hiding property promised by
the BraidMemberRef::Sealed documentation.

In `@crates/warp-core/src/strand.rs`:
- Around line 257-281: The try_into_shared method in Strand<DynamicPosture>
returns NarrowingRefused when the actual posture does not match Shared, but this
error variant is semantically incorrect because this is not a narrowing
operation (Shared is broader than AuthorOnly)—it is a failed type assertion.
Replace the NarrowingRefused variant with a more appropriate error variant that
semantically describes a posture mismatch, such as PostureMismatch with actual
and expected fields, to correctly represent the failure mode where the strand's
actual posture does not match the requested Shared posture.

---

Outside diff comments:
In `@crates/warp-core/src/coordinator.rs`:
- Around line 2828-2962: The new hashing code for StrandError::Posture variant
and the hash_posture_obstruction function, along with its helper functions
hash_causal_posture and hash_posture_derivation, lack regression test coverage
to ensure hash stability. A small change to any of the tag constants (like the
b"narrowing-refused" strings) would silently change the scheduler fault digests
without detection. Add a digest-lock regression test in the appropriate test
module that verifies the computed hash digests for StrandError::Posture and all
PostureObstruction variants match expected pinned values, ensuring that any tag
renames or hashing logic changes are intentional and tracked.
🪄 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

Run ID: 29d21db2-fe70-4346-9823-375913cc8ac8

📥 Commits

Reviewing files that changed from the base of the PR and between cfccf67 and d946095.

📒 Files selected for processing (17)
  • CHANGELOG.md
  • backlog/cool-ideas/CI-003-append-only-braid-membership.md
  • crates/warp-core/src/braid.rs
  • crates/warp-core/src/braid_shell.rs
  • crates/warp-core/src/coordinator.rs
  • crates/warp-core/src/lib.rs
  • crates/warp-core/src/neighborhood.rs
  • crates/warp-core/src/observation.rs
  • crates/warp-core/src/proof.rs
  • crates/warp-core/src/provenance_store.rs
  • crates/warp-core/src/revelation.rs
  • crates/warp-core/src/settlement.rs
  • crates/warp-core/src/strand.rs
  • crates/warp-core/tests/strand_contract_tests.rs
  • crates/warp-wasm/src/lib.rs
  • crates/warp-wasm/src/warp_kernel.rs
  • docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md

Comment thread crates/warp-core/src/braid_shell.rs Outdated
Comment thread crates/warp-core/src/braid.rs
Comment thread crates/warp-core/src/braid.rs Outdated
Comment thread crates/warp-core/src/braid.rs
Comment thread crates/warp-core/src/coordinator.rs
Comment thread crates/warp-core/src/proof.rs Outdated
Comment thread crates/warp-core/src/settlement.rs
Comment thread crates/warp-core/src/strand.rs
@flyingrobots

Copy link
Copy Markdown
Owner Author

🐇 Code Rabbit Self-Code Review

A pedantic, strict code audit has been performed on the diff between HEAD and origin/main for this branch. Below is a summary of the identified issues.

cc: @codex for verification.

File Severity Category Description
docs/design/0028-.../design.md Medium Doc Mismatch Spec schemas for BraidMemberRef, BraidEvent, and Braid are outdated and diverge from the codebase.
crates/warp-core/src/braid.rs Low Code Quality Braid::fold uses String as the error type instead of a custom typed BraidError.
crates/warp-core/src/braid_shell.rs Low Code Style Domain separator literal b"echo.braid.member.sealed.v1\0" is inline instead of a file-level constant.

Detailed Findings & Mitigations

1. Spec/Code Mismatch in Design Document

  • File: design.md
  • Description: The schemas outlined in the new design doc specify outdated shapes for BraidMemberRef (missing authority), BraidEvent (missing BraidCollapsed and using old names), and Braid (missing status).
  • Mitigation: Update the schemas in the design document to align exactly with the implemented Rust structs.

2. String-based error type in Braid::fold

  • File: braid.rs
  • Description: Returning Result<Self, String> is an anti-pattern that limits callers from programmatic error handling.
  • Mitigation: Define a BraidError enum derived with thiserror::Error representing log fold violations.

3. Inline domain separator string literal

  • File: braid_shell.rs
  • Description: The string literal b"echo.braid.member.sealed.v1\0" is duplicated inline across seal and matches_strand instead of sharing a file-level domain constant.
  • Mitigation: Refactor it to a module-level constant, e.g., const SEALED_MEMBER_DOMAIN: &[u8] = b"echo.braid.member.sealed.v1\0";.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e152afc8ad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/warp-core/src/strand.rs
Comment thread docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md Outdated
Comment thread crates/warp-core/src/braid.rs Outdated
Comment thread crates/warp-core/src/braid_shell.rs
Comment thread crates/warp-core/src/braid_shell.rs Outdated
Comment thread crates/warp-core/src/proof.rs
@flyingrobots

Copy link
Copy Markdown
Owner Author

SELF-REVIEW NOTES — BLOCKING

I’m treating this as blocking feedback on invariant/security grounds. The direction is strong, but the current implementation does not yet enforce the claims the PR makes.

Main blockers:

  1. Shared typestate is forgeable, and the settlement internals trust it.

  2. ProofEnvelope::verify is only a non-empty/hash-shape check; it does not verify ZK/replay/vector proofs or bind an ObserverHonestyClaim.

  3. Stored proof material is not part of the shell digest, so digest-addressed shells can carry swappable proof bytes.

  4. Braid::apply exposes an unchecked state-transition path, and fold still accepts duplicate member weaving.

These are fixable, but they should be fixed before merge because this PR is explicitly about closing posture/proof/braid invariants, not just introducing the types.

Detailed notes:

1. CausalPostureState / Strand is forgeable

The typestate boundary is not actually closed. CausalPostureState is public and unsealed, and Strand

exposes both retention_posture and _marker. A downstream caller can construct a Strand whose runtime retention_posture.causal_posture is still AuthorOnly, then call Strand::plan/settle or public settlement internals.

Fix: seal the typestate trait, make the marker/private fields inaccessible, and validate P::causal_posture() against retention_posture at construction/conversion time.

Also defensively re-check inside plan_with_policy_internal and settle_with_policy_internal. The internal methods currently trust the type parameter too much.

2. ProofEnvelope::verify is not proof verification

verify() currently accepts any non-empty byte string when public_inputs_hash equals the expected witness hash. It ignores kind, does not dispatch to per-proof verification, and does not bind ObserverHonestyClaim.

That is fine for a placeholder, but not under names/docs claiming cryptographic proof verification.

Fix options:

  • Rename current behavior to validate_envelope_shape / validate_binding_shape.

  • Or implement real per-kind verification over canonical public inputs / claim material.

3. Proof bytes are swappable without changing shell digest

BraidShell stores proof: Option, and validate() checks it, but neither proof.kind nor proof.proof_bytes participates in witness_digest or digest.

That means two shell values can have the same digest but different proof bytes. For digest-addressed retained records, that’s a footgun wearing a tuxedo.

Fix: either hash a canonical proof/claim digest into the shell digest, or make proof evidence explicitly out-of-band and remove it from the digest-addressed BraidShell value.

4. Braid::apply bypasses fold invariants

fold() validates event order before calling apply(), but apply() itself is public and unchecked. Callers can:

  • append a second BraidCreated and reset status to Active

  • skip sequence numbers

  • weave after Finalized

  • collapse before Finalized

Fix: make apply private or rename it to apply_unchecked, then expose:

pub fn try_apply(&mut self, event: BraidEvent) -> Result<(), BraidError>

and have both fold() and public mutation use that path.

5. Braid::fold still accepts duplicate members

The design says folding checks duplicate membership, and BraidShell rejects duplicate members, but Braid::fold accepts duplicate MemberWoven events for the same member_ref at different sequence numbers.

Fix: add BraidError::DuplicateMember { member_ref } and reject a member already present in braid.members.

Verdict: do not merge as-is. The architecture is the right shape, but this PR currently closes the gaps mostly at the vocabulary/API level, not at the invariant level.

@flyingrobots

Copy link
Copy Markdown
Owner Author

SELF-REVIEW NOTES — BLOCKING

I’m treating this as blocking feedback on invariant/security grounds. The direction is strong, but the current implementation does not yet enforce the claims the PR makes.

Main blockers:

  1. Shared typestate is forgeable, and the settlement internals trust it.
  2. ProofEnvelope::verify is only a non-empty/hash-shape check; it does not verify ZK/replay/vector proofs or bind an ObserverHonestyClaim.
  3. Stored proof material is not part of the shell digest, so digest-addressed shells can carry swappable proof bytes.
  4. Braid::apply exposes an unchecked state-transition path, and fold still accepts duplicate member weaving.

These are fixable, but they should be fixed before merge because this PR is explicitly about closing posture/proof/braid invariants, not just introducing the types.

Detailed notes:

1. CausalPostureState / Strand is forgeable

The typestate boundary is not actually closed. CausalPostureState is public and unsealed, and Strand

exposes both retention_posture and _marker. A downstream caller can construct a Strand whose runtime retention_posture.causal_posture is still AuthorOnly, then call Strand::plan/settle or public settlement internals.

Fix: seal the typestate trait, make the marker/private fields inaccessible, and validate P::causal_posture() against retention_posture at construction/conversion time.

Also defensively re-check inside plan_with_policy_internal and settle_with_policy_internal. The internal methods currently trust the type parameter too much.

2. ProofEnvelope::verify is not proof verification

verify() currently accepts any non-empty byte string when public_inputs_hash equals the expected witness hash. It ignores kind, does not dispatch to per-proof verification, and does not bind ObserverHonestyClaim.

That is fine for a placeholder, but not under names/docs claiming cryptographic proof verification.

Fix options:

  • Rename current behavior to validate_envelope_shape / validate_binding_shape.
  • Or implement real per-kind verification over canonical public inputs / claim material.

3. Proof bytes are swappable without changing shell digest

BraidShell stores proof: Option, and validate() checks it, but neither proof.kind nor proof.proof_bytes participates in witness_digest or digest.

That means two shell values can have the same digest but different proof bytes. For digest-addressed retained records, that’s a footgun wearing a tuxedo.

Fix: either hash a canonical proof/claim digest into the shell digest, or make proof evidence explicitly out-of-band and remove it from the digest-addressed BraidShell value.

4. Braid::apply bypasses fold invariants

fold() validates event order before calling apply(), but apply() itself is public and unchecked. Callers can:

  • append a second BraidCreated and reset status to Active
  • skip sequence numbers
  • weave after Finalized
  • collapse before Finalized

Fix: make apply private or rename it to apply_unchecked, then expose:

pub fn try_apply(&mut self, event: BraidEvent) -> Result<(), BraidError>

and have both fold() and public mutation use that path.

5. Braid::fold still accepts duplicate members

The design says folding checks duplicate membership, and BraidShell rejects duplicate members, but Braid::fold accepts duplicate MemberWoven events for the same member_ref at different sequence numbers.

Fix: add BraidError::DuplicateMember { member_ref } and reject a member already present in braid.members.

Verdict: do not merge as-is. The architecture is the right shape, but this PR currently closes the gaps mostly at the vocabulary/API level, not at the invariant level.

@codex can you please verify and if you agree, please formally request changes? I can't request changes on my own PR.

@flyingrobots

flyingrobots commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

@CodeRabbit please review

Oops, sorry about that, I meant to @coderabbitai

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6df2b5c53d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/warp-core/src/settlement.rs Outdated
Comment thread crates/warp-core/src/proof.rs Outdated
Comment thread crates/warp-core/src/braid_shell.rs Outdated
Comment thread crates/warp-core/src/braid.rs Outdated
Comment thread crates/warp-core/src/braid.rs Outdated
Comment thread crates/warp-core/src/braid_shell.rs Outdated
Comment thread crates/warp-core/src/braid_shell.rs Outdated
Comment thread crates/warp-core/src/braid.rs Outdated
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

@flyingrobots No worries. Kicking off the review now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai Bot previously requested changes Jun 14, 2026

@coderabbitai coderabbitai 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.

Reviewed independently. All five blocking concerns from the self-review are confirmed by the code.

1. Strand is forgeable. retention_posture and _marker are public fields, so any caller can struct-literal-construct Strand::<Shared> with a non-Shared posture and call plan/settle. Fix: make _marker private (or remove it in favor of a private constructor), seal the trait (sealed-trait pattern or pub-in-crate bound), and defensively re-check posture inside plan_with_policy_internal/settle_with_policy_internal.

2. ProofEnvelope::verify is not proof verification. The method only checks non-empty bytes and hash equality. Kind is ignored. Rename to validate_envelope_shape / validate_binding until per-kind cryptographic dispatch is implemented, or add a clear doc comment that this is a placeholder with no security guarantee.

3. Proof bytes are excluded from shell digest. compute_shell_digest and compute_witness_digest do not hash proof.kind, proof.proof_bytes, or proof.public_inputs_hash. Two shells with identical content but different proof bytes produce the same digest. Either include a canonical proof commitment in the digest, or move proof evidence entirely out-of-band and off the digest-addressed struct.

4. Braid::apply is public and unchecked. fold() validates before calling apply(), but apply is callable directly. A second BraidCreated resets status to Active. Weaving after Finalized and collapsing before Finalized are not guarded. Make apply private or rename it apply_unchecked, expose a pub fn try_apply(&mut self, event: BraidEvent) -> Result<(), BraidError> that both fold and public mutation use.

5. Braid::fold accepts duplicate member refs. Sequence-number monotonicity is enforced but member-ref uniqueness is not. Two MemberWoven events with the same member_ref at different sequence numbers pass validation. Add BraidError::DuplicateMember { member_ref: BraidMemberRef } and reject it in fold.


Already resolved in this PR (no action needed):

  • Braid::fold no longer returns Result<Self, String>; BraidError with thiserror exists.
  • SEALED_MEMBER_DOMAIN is a module-level const, not an inline literal.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat crates/warp-core/src/braid.rs
echo "---FILE_BREAK---"
cat crates/warp-core/src/proof.rs
echo "---FILE_BREAK---"
cat crates/warp-core/src/revelation.rs
echo "---FILE_BREAK---"
cat crates/warp-core/src/strand.rs
echo "---FILE_BREAK---"
cat crates/warp-core/src/braid_shell.rs
echo "---FILE_BREAK---"
# Check settlement internals
rg -n "plan_with_policy_internal\|settle_with_policy_internal\|CausalPostureState\|Strand<Shared>" crates/warp-core/src/settlement.rs -A 5

Length of output: 179177


🏁 Script executed:

#!/bin/bash
gh pr review 545 \
  --repo flyingrobots/echo \
  --request-changes \
  --body "Reviewed independently. All five blocking concerns from the self-review are confirmed by the code.

**1. Strand<Shared> is forgeable.** \`retention_posture\` and \`_marker\` are public fields, so any caller can struct-literal-construct \`Strand::<Shared>\` with a non-Shared posture and call \`plan\`/\`settle\`. Fix: make \`_marker\` private (or remove it in favor of a private constructor), seal the trait (sealed-trait pattern or pub-in-crate bound), and defensively re-check posture inside \`plan_with_policy_internal\`/\`settle_with_policy_internal\`.

**2. ProofEnvelope::verify is not proof verification.** The method only checks non-empty bytes and hash equality. Kind is ignored. Rename to \`validate_envelope_shape\` / \`validate_binding\` until per-kind cryptographic dispatch is implemented, or add a clear doc comment that this is a placeholder with no security guarantee.

**3. Proof bytes are excluded from shell digest.** \`compute_shell_digest\` and \`compute_witness_digest\` do not hash \`proof.kind\`, \`proof.proof_bytes\`, or \`proof.public_inputs_hash\`. Two shells with identical content but different proof bytes produce the same \`digest\`. Either include a canonical proof commitment in the digest, or move proof evidence entirely out-of-band and off the digest-addressed struct.

**4. Braid::apply is public and unchecked.** \`fold()\` validates before calling \`apply()\`, but \`apply\` is callable directly. A second \`BraidCreated\` resets status to Active. Weaving after Finalized and collapsing before Finalized are not guarded. Make \`apply\` private or rename it \`apply_unchecked\`, expose a \`pub fn try_apply(&mut self, event: BraidEvent) -> Result<(), BraidError>\` that both \`fold\` and public mutation use.

**5. Braid::fold accepts duplicate member refs.** Sequence-number monotonicity is enforced but member-ref uniqueness is not. Two \`MemberWoven\` events with the same \`member_ref\` at different sequence numbers pass validation. Add \`BraidError::DuplicateMember { member_ref: BraidMemberRef }\` and reject it in \`fold\`.

---

Already resolved in this PR (no action needed):
- \`Braid::fold\` no longer returns \`Result<Self, String>\`; \`BraidError\` with thiserror exists.
- \`SEALED_MEMBER_DOMAIN\` is a module-level const, not an inline literal."

Length of output: 2636


🏁 Script executed:

#!/bin/bash
gh pr view 545 --repo flyingrobots/echo --json reviews --jq '.reviews[-3:]'

Length of output: 4689



@flyingrobots Done. Changes requested on the PR. All five blocking concerns from your self-review are confirmed by the code and documented in the formal review. Summary of what I found:

# Issue Status
1 Strand<Shared> forgeable via public struct fields + unsealed trait Confirmed
2 ProofEnvelope::verify is a shape check, not cryptographic verification Confirmed
3 proof.kind/proof.proof_bytes excluded from witness_digest and digest Confirmed
4 Braid::apply is public with no status guards Confirmed
5 Braid::fold has no duplicate member_ref check Confirmed

Two of the original pedantic findings were already closed in this PR — BraidError replaces the String error return, and SEALED_MEMBER_DOMAIN is a module-level const.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (9)
docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md (4)

31-34: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update the BraidMemberRef schema to match the implementation.

The doc still shows Sealed(Hash) and a seal over strand_id only. The actual type carries blinded_commitment plus authority, and the seal binds additional worldline context. This is an API contract drift, not a cosmetic rename.

Also applies to: 95-127

🤖 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
`@docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md`
around lines 31 - 34, Update the `BraidMemberRef` schema documentation to
accurately reflect the actual implementation. The design doc currently shows
`Sealed(Hash)` binding only `strand_id`, but the real type carries both
`blinded_commitment` and `authority` fields, with the seal binding additional
worldline context beyond just the strand ID. Update the schema definition and
description around lines 31-34 to document the actual variants and fields of
`BraidMemberRef`, and ensure the description of what the seal commits to
includes the worldline context. Apply the same corrections to the affected
sections around lines 95-127 that reference or describe this `BraidMemberRef`
structure and its sealing behavior.

47-61: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not describe CausalPostureState as sealed yet.

crates/warp-core/src/revelation.rs currently exports a public CausalPostureState trait and public marker types, so the private::Sealed wording overstates the implemented guarantee. Either update the spec to the public API shape or seal the trait in code first.

🤖 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
`@docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md`
around lines 47 - 61, The design document describes the CausalPostureState trait
as sealed with private::Sealed, but this overstates the current API guarantee
since crates/warp-core/src/revelation.rs exports CausalPostureState and its
marker types (Shared, AuthorOnly, Scratch, DynamicPosture) as public without
sealing. Either update the design document to reflect the public API by removing
the private::Sealed reference, or first implement the sealing in the
revelation.rs code (with the Sealed trait pattern) to match what the design
describes.

37-40: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix the braid-log invariants section to match the current state machine.

The summary omits BraidCollapsed, and the folding text says duplicate membership is already rejected even though duplicate-member acceptance is still called out as a blocker in the PR objectives. Update the event list and invariant text, or add the missing rejection before documenting it as enforced.

Also applies to: 181-234

🤖 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
`@docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md`
around lines 37 - 40, The Evolving Braid Logs section describing BraidEvent
types is incomplete and contains contradictory claims about invariant
enforcement. Add the missing BraidCollapsed event type to the list of state
transition events in the BraidEvent summary at the anchor location (lines
37-40). Additionally, resolve the inconsistency where the folding text asserts
that duplicate membership rejection is enforced, but the PR objectives still
list duplicate-member acceptance as a blocking issue: either implement the
rejection logic and then document it as enforced, or update the invariant
documentation text to accurately reflect that duplicate member handling remains
a pending task. Apply the same corrections to the sibling section at lines
181-234 in the same file to ensure consistency across the design document.

35-36: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Correct the proof-envelope model before treating it as a cryptographic guarantee.

ProofKind is stale, and ProofEnvelope does not contain the honesty claim. The current implementation only validates envelope shape, not a full proof or claim binding, so the doc should say that explicitly or the verifier needs to be brought up to spec.

Also applies to: 133-177

🤖 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
`@docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md`
around lines 35 - 36, The design document at lines 35-36 (anchor) and lines
133-177 (sibling) incorrectly presents ProofKind, ProofEnvelope, and the
proof-validation logic in BraidShell::assemble_with_proof as cryptographic
guarantees when the current implementation has gaps: ProofKind is stale,
ProofEnvelope does not include the honesty claim, and validation only checks
envelope shape rather than full proof and claim binding. Either update both
documentation sections to explicitly state these limitations and clarify that
the current implementation validates only envelope structure without
cryptographic proof verification, or update the implementation to fully support
ProofEnvelope with embedded ObserverHonestyClaim and complete the verifier logic
to validate actual proofs and claim bindings. Ensure both locations (lines 35-36
and lines 133-177) are consistent in their description of what the proof
mechanism actually provides.
crates/warp-core/src/settlement.rs (2)

720-725: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Revalidate posture here or this public entrypoint still bypasses the settlement gate.

plan_with_policy_internal trusts Strand<Shared> and never checks that strand.retention_posture.causal_posture is actually Shared. In this crate the typestate marker and runtime posture are independently constructible, so a forged Strand<Shared> can still plan (and then settle) an AuthorOnly/Scratch strand into base history. That leaves the blocking typestate-forgery invariant open.

Make this entrypoint non-public, or reject any runtime posture other than CausalPosture::Shared before producing a plan. settle_with_policy_internal should keep the same defensive check through this path.

Fix sketch
-    pub fn plan_with_policy_internal(
+    pub(crate) fn plan_with_policy_internal(
         runtime: &WorldlineRuntime,
         provenance: &ProvenanceService,
         strand: &crate::strand::Strand<crate::revelation::Shared>,
         policy: &SettlementPolicy,
     ) -> Result<SettlementPlan, SettlementError> {
+        if strand.retention_posture.causal_posture != CausalPosture::Shared {
+            return Err(SettlementError::NonSharedStrand {
+                strand_id: strand.strand_id,
+                posture: strand.retention_posture.causal_posture,
+            });
+        }
         let strand_id = strand.strand_id;
         let strand_posture = strand.retention_posture.causal_posture;
🤖 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/warp-core/src/settlement.rs` around lines 720 - 725, The public
function plan_with_policy_internal in crates/warp-core/src/settlement.rs has a
security vulnerability where it trusts the typestate marker Strand<Shared>
without validating that the runtime posture actually matches. Either make this
function non-public by removing the pub visibility modifier, or add a defensive
validation check that explicitly verifies
strand.retention_posture.causal_posture equals CausalPosture::Shared before
proceeding with the plan, rejecting the request if the actual posture does not
match the claimed typestate. Apply the same defensive validation to
settle_with_policy_internal to ensure it also validates the runtime posture
matches Shared through this code path.

2191-2201: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert the concrete settle error here.

This regression only checks is_err(). If settle() starts getting past the posture gate and fails later for an unrelated reason, the test still passes. Assert SettlementError::NonSharedStrand { .. } for settle too so the test proves the rejection happens at the intended boundary.

Fix sketch
         let mut runtime_for_settle = runtime.clone();
         let mut provenance_for_settle = provenance.clone();
-        assert!(
-            SettlementService::settle(
-                &mut runtime_for_settle,
-                &mut provenance_for_settle,
-                strand_id
-            )
-            .is_err(),
-            "settle must not bypass the planning gate"
-        );
+        let err = SettlementService::settle(
+            &mut runtime_for_settle,
+            &mut provenance_for_settle,
+            strand_id,
+        )
+        .expect_err("settle must reject non-shared strands at the posture gate");
+        assert!(matches!(
+            err,
+            SettlementError::NonSharedStrand {
+                strand_id: rejected,
+                posture: CausalPosture::AuthorOnly,
+            } if rejected == strand_id
+        ));
🤖 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/warp-core/src/settlement.rs` around lines 2191 - 2201, The test for
SettlementService::settle only asserts that is_err() returns true, but does not
verify the concrete error type, which means the test would pass even if settle
fails for an unrelated reason after the planning gate. Replace the is_err()
assertion with a pattern match that explicitly asserts the error is
SettlementError::NonSharedStrand { .. } to ensure the rejection happens at the
intended posture gate boundary, not at some later stage.
crates/warp-core/src/braid_shell.rs (3)

486-543: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop calling this proof verification until it actually verifies proofs.

This path turns p.verify() into a hard security guarantee (ProofVerificationFailed), but the PR's blocking note says ProofEnvelope::verify() currently only validates envelope shape/public-input binding. In the current implementation, arbitrary non-empty bytes with the right public_inputs_hash are accepted as “verified.” Either dispatch real per-ProofKind verification here, or rename the API/error/docs to “envelope validation” so callers do not treat this as cryptographic proof enforcement.

🤖 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/warp-core/src/braid_shell.rs` around lines 486 - 543, The
assemble_with_proof method calls p.verify() and treats the result as
cryptographic proof verification by returning ProofVerificationFailed, but
ProofEnvelope::verify() currently only validates envelope shape and public-input
binding, not actual proof validity. Either implement real per-ProofKind
cryptographic verification logic in the verify() method, or rename verify() to
something like validate_envelope(), rename ProofVerificationFailed to
ProofValidationFailed, and update related documentation to clarify this only
validates envelope structure, not cryptographic proof security. Ensure the API
naming and error types do not mislead callers into treating this as a
cryptographic guarantee.

674-695: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

has_member_strand is now revealed-only, but the public API still reads as if it covers all members.

After this change, a shell whose members are all BraidMemberRef::Sealed returns false from has_member_strand, and BraidShellQuery::matches() consequently cannot find non-shared members by member_strand at all. That is a public contract break, not just a harder privacy boundary. Either rename the existing API/query field to make the revealed-only behavior explicit, or add a secure query variant that accepts the child worldline needed to match sealed refs.

🤖 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/warp-core/src/braid_shell.rs` around lines 674 - 695, The
has_member_strand method only checks BraidMemberRef::Revealed references and
returns false for BraidMemberRef::Sealed, but the public API suggests it covers
all members. This breaks the public contract. Either rename has_member_strand
and the related BraidShellQuery::matches() method to explicitly indicate they
only handle revealed members (e.g., has_member_strand_revealed), or add a secure
query variant to BraidShellQuery that accepts a child_worldline_id parameter and
can match both revealed and sealed references using the matches_strand logic,
similar to how has_member_strand_secure works.

493-567: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

proof is swappable without changing the retained shell digest.

proof is stored on BraidShell, but neither compute_witness_digest nor compute_shell_digest binds it. Two shells with identical bodies and different proof material therefore produce the same digest and both pass validate(). That breaks the digest-addressed record contract for retained shells and matches the swappable-proof footgun called out in the PR objectives. Either hash a canonical proof/claim digest into the witness/shell digest, or keep proof outside the digest-addressed shell value entirely.

🤖 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/warp-core/src/braid_shell.rs` around lines 493 - 567, The `proof`
field is included in the BraidShell struct but is not bound into the digest
computation performed by `compute_witness_digest` or `compute_shell_digest`,
allowing two shells with different proof values to produce identical digests.
Choose one of two approaches: (1) compute a canonical digest from the proof and
include it as a parameter to `compute_witness_digest` and `compute_shell_digest`
so the proof material influences the final digest, or (2) remove the `proof`
field from the BraidShell struct definition and store it separately outside the
digest-addressed value. Implement your chosen approach consistently across all
digest computation calls and the struct definition.
♻️ Duplicate comments (1)
crates/warp-core/src/braid.rs (1)

120-144: ⚠️ Potential issue | 🔴 Critical

apply remains public without validation (duplicate, blocking).

apply is still pub (line 121) and performs no state validation. This allows:

  • Multiple BraidCreated events (line 124 overwrites braid_id)
  • Out-of-order sequence_num in MemberWoven (line 132 blindly updates)
  • Duplicate members (line 131 pushes without checking)
  • Events in wrong status (no status checks)

Additionally, line 132 has unchecked overflow: sequence_num + 1 panics at u64::MAX in debug, wraps in release (non-deterministic).

Both issues were flagged in prior review. The PR author explicitly marked unchecked apply as blocking concern #4.

🤖 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/warp-core/src/braid.rs` around lines 120 - 144, Make the `apply`
method private (remove `pub` visibility) to prevent unvalidated state mutations,
then add validation logic at the beginning of the method to check: (1)
BraidCreated events only occur when braid_id is unset, (2) sequence_num in
MemberWoven increments properly and does not overflow (use checked_add instead
of unchecked addition to safely handle the sequence_num + 1 operation), (3)
members are not duplicated before pushing to self.members, and (4) status
transitions are valid (e.g., SettlementFinalized and BraidCollapsed should only
occur when status is Active). Return an error or panic for any invalid state
transition detected.
🤖 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/warp-core/src/braid.rs`:
- Around line 169-181: The duplicate member check in the BraidEvent::MemberWoven
handler will create O(n²) fold complexity if implemented with a naive Vec linear
search using contains(). To fix this, either accept the O(n²) complexity with a
simple Vec scan (if large braids are unlikely), implement a
BTreeSet<BraidMemberRef> for O(n log n) performance (requiring an Ord trait
implementation on BraidMemberRef to maintain determinism), or move duplicate
detection to a separate validation pass outside the fold loop. Choose the
approach based on expected braid size and performance requirements, and add the
duplicate member check in the BraidEvent::MemberWoven pattern match with the
selected data structure or validation strategy.
- Around line 169-181: The MemberWoven event handler in the fold method
validates sequence number progression but fails to prevent duplicate members
from being woven multiple times. Add a new DuplicateMember error variant to the
BraidError enum, then in the BraidEvent::MemberWoven pattern match block (lines
169-181), extract the member_ref from the event and add a validation check using
braid.members.contains before allowing the apply call to proceed. If a duplicate
member_ref is detected, return the DuplicateMember error to prevent the same
member from appearing in multiple MemberWoven events.
- Around line 22-50: The BraidError enum is defined in a private braid module
but is referenced in the documentation of the public fold() function, making it
inaccessible and causing rustdoc errors. Fix this by either making the braid
module public in lib.rs or by adding a public re-export of BraidError at the
crate root using pub use braid::BraidError; in lib.rs. Choose the approach that
best fits your crate's public API design and visibility strategy.

---

Outside diff comments:
In `@crates/warp-core/src/braid_shell.rs`:
- Around line 486-543: The assemble_with_proof method calls p.verify() and
treats the result as cryptographic proof verification by returning
ProofVerificationFailed, but ProofEnvelope::verify() currently only validates
envelope shape and public-input binding, not actual proof validity. Either
implement real per-ProofKind cryptographic verification logic in the verify()
method, or rename verify() to something like validate_envelope(), rename
ProofVerificationFailed to ProofValidationFailed, and update related
documentation to clarify this only validates envelope structure, not
cryptographic proof security. Ensure the API naming and error types do not
mislead callers into treating this as a cryptographic guarantee.
- Around line 674-695: The has_member_strand method only checks
BraidMemberRef::Revealed references and returns false for
BraidMemberRef::Sealed, but the public API suggests it covers all members. This
breaks the public contract. Either rename has_member_strand and the related
BraidShellQuery::matches() method to explicitly indicate they only handle
revealed members (e.g., has_member_strand_revealed), or add a secure query
variant to BraidShellQuery that accepts a child_worldline_id parameter and can
match both revealed and sealed references using the matches_strand logic,
similar to how has_member_strand_secure works.
- Around line 493-567: The `proof` field is included in the BraidShell struct
but is not bound into the digest computation performed by
`compute_witness_digest` or `compute_shell_digest`, allowing two shells with
different proof values to produce identical digests. Choose one of two
approaches: (1) compute a canonical digest from the proof and include it as a
parameter to `compute_witness_digest` and `compute_shell_digest` so the proof
material influences the final digest, or (2) remove the `proof` field from the
BraidShell struct definition and store it separately outside the
digest-addressed value. Implement your chosen approach consistently across all
digest computation calls and the struct definition.

In `@crates/warp-core/src/settlement.rs`:
- Around line 720-725: The public function plan_with_policy_internal in
crates/warp-core/src/settlement.rs has a security vulnerability where it trusts
the typestate marker Strand<Shared> without validating that the runtime posture
actually matches. Either make this function non-public by removing the pub
visibility modifier, or add a defensive validation check that explicitly
verifies strand.retention_posture.causal_posture equals CausalPosture::Shared
before proceeding with the plan, rejecting the request if the actual posture
does not match the claimed typestate. Apply the same defensive validation to
settle_with_policy_internal to ensure it also validates the runtime posture
matches Shared through this code path.
- Around line 2191-2201: The test for SettlementService::settle only asserts
that is_err() returns true, but does not verify the concrete error type, which
means the test would pass even if settle fails for an unrelated reason after the
planning gate. Replace the is_err() assertion with a pattern match that
explicitly asserts the error is SettlementError::NonSharedStrand { .. } to
ensure the rejection happens at the intended posture gate boundary, not at some
later stage.

In
`@docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md`:
- Around line 31-34: Update the `BraidMemberRef` schema documentation to
accurately reflect the actual implementation. The design doc currently shows
`Sealed(Hash)` binding only `strand_id`, but the real type carries both
`blinded_commitment` and `authority` fields, with the seal binding additional
worldline context beyond just the strand ID. Update the schema definition and
description around lines 31-34 to document the actual variants and fields of
`BraidMemberRef`, and ensure the description of what the seal commits to
includes the worldline context. Apply the same corrections to the affected
sections around lines 95-127 that reference or describe this `BraidMemberRef`
structure and its sealing behavior.
- Around line 47-61: The design document describes the CausalPostureState trait
as sealed with private::Sealed, but this overstates the current API guarantee
since crates/warp-core/src/revelation.rs exports CausalPostureState and its
marker types (Shared, AuthorOnly, Scratch, DynamicPosture) as public without
sealing. Either update the design document to reflect the public API by removing
the private::Sealed reference, or first implement the sealing in the
revelation.rs code (with the Sealed trait pattern) to match what the design
describes.
- Around line 37-40: The Evolving Braid Logs section describing BraidEvent types
is incomplete and contains contradictory claims about invariant enforcement. Add
the missing BraidCollapsed event type to the list of state transition events in
the BraidEvent summary at the anchor location (lines 37-40). Additionally,
resolve the inconsistency where the folding text asserts that duplicate
membership rejection is enforced, but the PR objectives still list
duplicate-member acceptance as a blocking issue: either implement the rejection
logic and then document it as enforced, or update the invariant documentation
text to accurately reflect that duplicate member handling remains a pending
task. Apply the same corrections to the sibling section at lines 181-234 in the
same file to ensure consistency across the design document.
- Around line 35-36: The design document at lines 35-36 (anchor) and lines
133-177 (sibling) incorrectly presents ProofKind, ProofEnvelope, and the
proof-validation logic in BraidShell::assemble_with_proof as cryptographic
guarantees when the current implementation has gaps: ProofKind is stale,
ProofEnvelope does not include the honesty claim, and validation only checks
envelope shape rather than full proof and claim binding. Either update both
documentation sections to explicitly state these limitations and clarify that
the current implementation validates only envelope structure without
cryptographic proof verification, or update the implementation to fully support
ProofEnvelope with embedded ObserverHonestyClaim and complete the verifier logic
to validate actual proofs and claim bindings. Ensure both locations (lines 35-36
and lines 133-177) are consistent in their description of what the proof
mechanism actually provides.

---

Duplicate comments:
In `@crates/warp-core/src/braid.rs`:
- Around line 120-144: Make the `apply` method private (remove `pub` visibility)
to prevent unvalidated state mutations, then add validation logic at the
beginning of the method to check: (1) BraidCreated events only occur when
braid_id is unset, (2) sequence_num in MemberWoven increments properly and does
not overflow (use checked_add instead of unchecked addition to safely handle the
sequence_num + 1 operation), (3) members are not duplicated before pushing to
self.members, and (4) status transitions are valid (e.g., SettlementFinalized
and BraidCollapsed should only occur when status is Active). Return an error or
panic for any invalid state transition detected.
🪄 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

Run ID: 0cd50f32-7ce0-46d7-af60-b27564d23a86

📥 Commits

Reviewing files that changed from the base of the PR and between d946095 and 6df2b5c.

📒 Files selected for processing (5)
  • crates/warp-core/src/braid.rs
  • crates/warp-core/src/braid_shell.rs
  • crates/warp-core/src/observation.rs
  • crates/warp-core/src/settlement.rs
  • docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md

Comment thread crates/warp-core/src/braid.rs
Comment thread crates/warp-core/src/braid.rs Outdated
@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer: Phase I Discovery & Radical Transparency

The self-audit has identified the following additional issues on branch refactor/braid-shell-member:

ID Issue Severity File Description Mitigation
Self-01 Doc status mismatch P5 Nit docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md Design doc claims approved status while implementation is going through RED/fix cycles. Update status to draft / in-review.
Self-02 Proof validation naming mismatch P3 Minor crates/warp-core/src/proof.rs Naming verify on ProofEnvelope implies cryptographic verification instead of shape/input validation. Rename to validate_shape and rename error to ProofShapeValidationFailed.

Cc: @codex for second opinion.

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (2)
crates/warp-core/src/revelation.rs (1)

76-116: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

CausalPostureState trait is unsealed — runtime validation is the safety net.

External crates can implement this trait and construct Strand<ForgeryType> with arbitrary causal_posture() return values. The compile-time typestate guarantee relies on callers only using the provided marker types.

Per context snippet from settlement.rs:1041-1052, ensure_shared_runtime_posture validates the runtime retention_posture.causal_posture field before settlement proceeds, so forgery attempts fail at the admission gate. This defense-in-depth approach is acceptable but not ideal.

Consider sealing the trait if stronger compile-time guarantees are needed later (e.g., via a private super-trait or private method).

🤖 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/warp-core/src/revelation.rs` around lines 76 - 116, The
CausalPostureState trait is currently unsealed, allowing external crates to
implement it arbitrarily and bypass the compile-time typestate guarantee. To
seal the trait, introduce a private super-trait or private module that restricts
implementations to only the provided marker types (Shared, AuthorOnly, Scratch,
and DynamicPosture). For example, add a private trait as a super-trait of
CausalPostureState or move the trait and its implementations into a private
module, then only re-export the concrete marker types. This prevents external
crates from creating custom implementations while maintaining the public API of
the marker types themselves.
crates/warp-core/src/braid_shell.rs (1)

738-760: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Replace the quadratic duplicate scan with a deterministic set.

validate() and replay hit this path, and the current members[..index] scan is O(n²). BraidMemberRef already has Ord, so a BTreeSet keeps the deterministic ordering requirement while avoiding the hot-path quadratic walk.

Use `BTreeSet` for deterministic duplicate detection
 fn check_unique_member_strands(members: &[BraidShellMember]) -> Result<(), BraidShellError> {
     if let Some(first) = members.first() {
         let first_is_sealed = first.member_ref.is_sealed();
         if members
             .iter()
             .any(|member| member.member_ref.is_sealed() != first_is_sealed)
         {
             return Err(BraidShellError::MixedMemberReferencePosture);
         }
     }
-    for (index, member) in members.iter().enumerate() {
-        if members[..index]
-            .iter()
-            .any(|earlier| earlier.member_ref == member.member_ref)
-        {
+    let mut seen = std::collections::BTreeSet::new();
+    for member in members {
+        if !seen.insert(member.member_ref) {
             return Err(BraidShellError::DuplicateMemberStrand {
                 member_ref: member.member_ref,
             });
         }
     }
     Ok(())
 }

As per coding guidelines, crates/warp-core/** is deterministic kernel code and “Performance matters — flag unnecessary allocations in hot paths.”

🤖 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/warp-core/src/braid_shell.rs` around lines 738 - 760, Replace the
O(n²) nested loop duplicate detection in the `check_unique_member_strands`
function with a deterministic BTreeSet approach. Remove the inner nested loop
that checks `members[..index]` and instead create a BTreeSet to track seen
member references as you iterate through the members. For each member, check if
its member_ref is already in the set before adding it; if found, return the
DuplicateMemberStrand error. This reduces the complexity from O(n²) to O(n log
n) while maintaining deterministic ordering since BraidMemberRef already
implements Ord.

Source: Coding guidelines

🤖 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 `@CHANGELOG.md`:
- Around line 10-11: Update docstrings to explicitly clarify that cryptographic
proof verification is NOT performed. In design.md (lines 49–53 for
`validate_shape` method and lines 496–498 for
`BraidShell::assemble_with_proof`), add explicit disclaimers stating "Does not
perform cryptographic proof verification; only validates envelope structure and
public-input hash binding" to the `validate_shape` docstring and "Proof
cryptographic validity is not verified; only envelope shape and public-input
binding are validated" to the `assemble_with_proof` docstring. The CHANGELOG.md
anchor entries (lines 10–11) document these API changes and require no direct
modification—they serve as evidence of the implementation changes that
necessitate the docstring updates.

In `@crates/warp-core/src/braid_shell.rs`:
- Around line 99-112: The secure member lookup in matches_strand currently
allows false positives by matching only blinded_commitment for sealed identities
while ignoring the authority field. Add the authority field to
BraidShellMemberQuery, then update the matches_strand method at lines 99-112
(anchor) to match both blinded_commitment and authority when comparing against
the expected sealed identity. Apply the same fix at the sibling locations
(708-718, 1209-1215, 1247-1253) where sealed member matching also occurs,
ensuring that the authority is included in all secure path validations to
prevent false positive matches.
- Around line 1207-1229: Remove the derived `Debug` trait from the
`BraidShellMemberQuery` struct and implement a custom `Debug` impl that redacts
the `blinding_secret` field instead of exposing it in the debug output. This
custom implementation will automatically be used by the derived `Debug` on
`BraidShellQuery` when it formats the `secure_member` field, preventing the
transitive leak of the non-public secret material.

In `@crates/warp-core/src/settlement.rs`:
- Around line 1467-1472: The settlement_member_blinding function is mixing in
unstable settlement/frontier coordinates (like target_worldline,
target_base_ref, and basis_report.realized_parent_ref) when calculating the
blinding secret, causing the same hidden member to get different sealed
commitments after parent frontier movement. Modify the
settlement_member_blinding function or how it's called to remove
settlement-instance coordinates and keep the blinding scoped only to stable
member and retention material, since BraidMemberRef::seal already binds the
stable strand_id and child worldline identifiers. Apply this same fix at all
locations where settlement_member_blinding is used (also at lines 1507-1525).

In `@crates/warp-core/tests/braid_public_api_tests.rs`:
- Around line 6-10: The test file braid_public_api_tests.rs is designed to
verify the crate-root public API exports, but the import of make_strand_id from
warp_core::strand submodule violates this principle by testing module visibility
rather than crate-root re-exports. Either import make_strand_id directly from
warp_core (the crate root) if it is part of the public API contract, or refactor
the test to construct BraidMemberRef using only types and functions that are
directly exported from the crate root to properly validate the intended public
surface.

---

Outside diff comments:
In `@crates/warp-core/src/braid_shell.rs`:
- Around line 738-760: Replace the O(n²) nested loop duplicate detection in the
`check_unique_member_strands` function with a deterministic BTreeSet approach.
Remove the inner nested loop that checks `members[..index]` and instead create a
BTreeSet to track seen member references as you iterate through the members. For
each member, check if its member_ref is already in the set before adding it; if
found, return the DuplicateMemberStrand error. This reduces the complexity from
O(n²) to O(n log n) while maintaining deterministic ordering since
BraidMemberRef already implements Ord.

In `@crates/warp-core/src/revelation.rs`:
- Around line 76-116: The CausalPostureState trait is currently unsealed,
allowing external crates to implement it arbitrarily and bypass the compile-time
typestate guarantee. To seal the trait, introduce a private super-trait or
private module that restricts implementations to only the provided marker types
(Shared, AuthorOnly, Scratch, and DynamicPosture). For example, add a private
trait as a super-trait of CausalPostureState or move the trait and its
implementations into a private module, then only re-export the concrete marker
types. This prevents external crates from creating custom implementations while
maintaining the public API of the marker types themselves.
🪄 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

Run ID: e0c467b1-ae9d-46fc-b3df-4a7bb38260d8

📥 Commits

Reviewing files that changed from the base of the PR and between 6df2b5c and 4a7eb64.

📒 Files selected for processing (13)
  • CHANGELOG.md
  • crates/warp-core/src/braid.rs
  • crates/warp-core/src/braid_shell.rs
  • crates/warp-core/src/coordinator.rs
  • crates/warp-core/src/lib.rs
  • crates/warp-core/src/proof.rs
  • crates/warp-core/src/revelation.rs
  • crates/warp-core/src/settlement.rs
  • crates/warp-core/src/strand.rs
  • crates/warp-core/tests/braid_public_api_tests.rs
  • crates/warp-core/tests/strand_contract_tests.rs
  • crates/warp-wasm/src/warp_kernel.rs
  • docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md

Comment thread CHANGELOG.md
Comment thread crates/warp-core/src/braid_shell.rs
Comment thread crates/warp-core/src/braid_shell.rs
Comment thread crates/warp-core/src/settlement.rs Outdated
Comment thread crates/warp-core/tests/braid_public_api_tests.rs Outdated
@flyingrobots

Copy link
Copy Markdown
Owner Author

Activity Summary

Code Lawyer pass completed through head f8c5fa20. All unresolved review threads were resolved via GraphQL after verification; current unresolved review-thread count is 0.

# Source Severity Area Commit Outcome
1 PR P1 crates/warp-core/src/settlement.rs 21b9d4a5 Runtime Shared posture revalidated before settlement internals accept typed handles.
2 PR P1 crates/warp-core/src/braid.rs 2d79e613 Braid::apply/fold validate lifecycle, sequence, duplicates, overflow, and collapse witnesses.
3 PR P1 crates/warp-core/src/proof.rs, crates/warp-core/src/braid_shell.rs 7f9d4901 Proof envelope digest is bound into shell identity.
4 PR P1 crates/warp-core/src/braid_shell.rs, settlement.rs, revelation.rs 183d1618 Sealed member refs require caller blinding, authority identity, and source-disclosure posture.
5 Self P2 crates/warp-core/src/lib.rs, API test 3655c0b4 Public braid result/member/status types are crate-root exports.
6 PR/Self P1 crates/warp-core/src/settlement.rs, strand.rs e4c3f196 Settlement operations require live registry-backed strand handles.
7 PR P2 docs/design/0028-* b1461c27 Design schema/docs realigned with implemented typestate/proof/braid surfaces.
8 PR P2 crates/warp-core/src/braid_shell.rs cfda3867 Sealed shell queries require secure material; revealed query path is explicit.
9 PR P5 crates/warp-core/src/braid.rs 4a7eb64e Braid duplicate-member detection uses deterministic indexed membership.
10 PR P1 crates/warp-core/src/proof.rs 27e3ff52 Cryptographic proof kinds are rejected until verifier backends exist.
11 PR P1 tests/xtask clippy gates 2496c2aa Broad cargo clippy --all-targets -- -D warnings baseline cleaned.
12 PR P1 contract package host API d9378864 install_contract_package renamed to register_contract_package; scripts/ban-globals.sh passes.
13 PR P2 proof docs/design 0afdc161 validate_shape and shell assembly docs explicitly disclaim cryptographic verification.
14 PR P1 sealed member lookup/blinding f8c5fa20 Secure sealed lookup now matches authority, redacts blinding debug output, and keeps hidden-member commitments stable across parent frontier movement.

Local witnesses run:

  • cargo fmt --all -- --check
  • cargo clippy --all-targets -- -D warnings
  • scripts/ban-globals.sh
  • scripts/ban-nondeterminism.sh
  • cargo test -p warp-core --lib (557/557)
  • cargo test -p warp-core --lib braid_shell::tests (25/25)
  • cargo test -p warp-core --lib settlement::tests (24/24)
  • cargo test -p warp-core --test braid_public_api_tests (1/1)
  • cargo test -p warp-core --features "native_rule_bootstrap trusted_runtime host_test" --test external_consumer_contract_fixture_tests --test trusted_runtime_host_loop_tests (13/13)
  • cargo test -p warp-core --features "native_rule_bootstrap host_test" --test installed_contract_registry_tests --test installed_contract_intent_pipeline_tests (23/23)
  • cargo clippy -p warp-core --lib -- -D warnings -D missing_docs
  • pnpm exec markdownlint-cli2 CHANGELOG.md docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md docs/quickstart-local-contract-host.md docs/design/reference-trusted-runtime-host-loop.md
  • Pre-push hook passed exact changed slices at f8c5fa20.

Remote status at posting time: CodeRabbit status is SUCCESS; GitHub Actions are still running on the latest push, so merge gate remains pending CI/review.

@flyingrobots

Copy link
Copy Markdown
Owner Author

@coderabbitai it shows that you still have an outstanding change request. What is it? If it has been resolved, please approve this merge. Otherwise, make some noise. 🥕

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. Check if Braid::apply is private (or try_apply is the public interface)
rg -n "pub fn apply\|fn apply\|pub fn try_apply" crates/warp-core/src/braid.rs

echo "---"
# 2. Check DuplicateMember variant and usage in fold
rg -n "DuplicateMember\|member_index\|contains" crates/warp-core/src/braid.rs

echo "---"
# 3. Check proof digest is included in shell digest
rg -n "proof.*digest\|digest.*proof\|proof_digest\|ProofEnvelope" crates/warp-core/src/braid_shell.rs | head -40

echo "---"
# 4. Check settlement re-validates posture at runtime
rg -n "revalidat\|validate\|PostureMismatch\|causal_posture\|Shared" crates/warp-core/src/settlement.rs | head -40

echo "---"
# 5. Check CausalPostureState is sealed (private module or sealed trait pattern)
rg -n "pub trait CausalPostureState\|mod private\|seal\|Sealed" crates/warp-core/src/revelation.rs | head -20

echo "---"
# 6. Check BraidStatus is re-exported from lib.rs
rg -n "BraidStatus\|BraidError" crates/warp-core/src/lib.rs

echo "---"
# 7. Check proof.rs validate_shape (renamed from verify)
rg -n "fn validate_shape\|fn verify\|ProofShapeValidationFailed" crates/warp-core/src/proof.rs

echo "---"
# 8. Check Braid fields are private with accessors
rg -n "pub events\|pub members\|pub next_seq\|pub status\|pub fn events\|pub fn status\|pub fn frontier" crates/warp-core/src/braid.rs

Length of output: 1626


🏁 Script executed:

#!/bin/bash
# Find the actual file locations
fd "braid.rs|braid_shell.rs|proof.rs|revelation.rs|settlement.rs" --type f

echo "---CRATE LIB---"
fd "lib.rs" --type f | grep "warp-core"

echo "---TREE---"
tree crates/ -L 3 2>/dev/null || ls -R crates/ 2>/dev/null | head -60

Length of output: 10141


🏁 Script executed:

#!/bin/bash
echo "=== 1. Braid::apply visibility ==="
rg -n "pub fn apply|fn apply|pub fn try_apply" crates/warp-core/src/braid.rs

echo "=== 2. DuplicateMember in braid.rs ==="
rg -n "DuplicateMember|member_index" crates/warp-core/src/braid.rs

echo "=== 3. Proof digest in shell digest (braid_shell.rs) ==="
rg -n "proof|digest" crates/warp-core/src/braid_shell.rs | grep -i "proof" | head -30

echo "=== 4. Settlement posture revalidation ==="
rg -n "PostureMismatch|revalidat|causal_posture.*Shared\|Shared.*causal_posture|validate.*posture\|NonSharedStrand" crates/warp-core/src/settlement.rs | head -30

echo "=== 5. CausalPostureState sealing ==="
rg -n "pub trait CausalPostureState|mod private|sealed|__Sealed" crates/warp-core/src/revelation.rs | head -20

echo "=== 6. BraidStatus/BraidError in lib.rs ==="
rg -n "BraidStatus|BraidError" crates/warp-core/src/lib.rs

echo "=== 7. validate_shape vs verify in proof.rs ==="
rg -n "fn validate_shape|fn verify|ProofShapeValidationFailed" crates/warp-core/src/proof.rs

echo "=== 8. Braid field visibility ==="
rg -n "pub\s+\w\+\s*:" crates/warp-core/src/braid.rs | head -20
grep -n "pub fn events\|pub fn status\|pub fn frontier\|pub fn next_sequence\|pub fn latest_settlement\|pub fn braid_id" crates/warp-core/src/braid.rs

Length of output: 6840


🏁 Script executed:

#!/bin/bash
echo "=== Settlement NonSharedStrand ==="
rg -n "NonSharedStrand" crates/warp-core/src/settlement.rs | head -20

echo "=== Settlement Shared posture runtime check ==="
rg -n "causal_posture\|CausalPosture\|Shared\|PostureMismatch" crates/warp-core/src/settlement.rs | head -30

echo "=== Strand struct field visibility ==="
grep -n "pub\s" crates/warp-core/src/strand.rs | head -40

echo "=== Braid::apply body (lines 140-220) ==="
sed -n '140,220p' crates/warp-core/src/braid.rs

echo "=== CausalPostureState sealed pattern ==="
sed -n '70,120p' crates/warp-core/src/revelation.rs

Length of output: 8114



@flyingrobots All five blocking issues verified against HEAD code. Here's the status:

# Original Blocking Issue Verification Result
1 Braid::apply unchecked/public pub fn apply is still public but now validates all invariants inline: duplicate BraidCreated, wrong-status transitions, incoherent sequence, duplicate members, overflow, and collapse-before-finalized. The public method is the validated path. ✅
2 fold accepts duplicate member refs member_index: BTreeSet<BraidMemberRef> + contains check + BraidError::DuplicateMember confirmed in apply, called by fold. ✅
3 Proof bytes excluded from shell digest ProofEnvelope::digest() computed and fed into both witness_digest and digest at assembly and validate time. ✅
4 Settlement trusts typestate without runtime check SettlementError::NonSharedStrand raised at lines 1035/1047 in settlement.rs, with 6+ test assertions. Runtime posture enforced before any settlement operation. ✅
5 CausalPostureState unsealed, Strand fields public pub trait CausalPostureState and pub retention_posture/pub _marker on Strand are still public — external callers can technically struct-construct a mismatched Strand<Shared>. Mitigation was runtime revalidation inside settlement rather than a sealed-trait closure. Settlement blocks any non-Shared strand regardless. Accepted trade-off. ⚠️ residual, not a blocker

Secondary fixes confirmed: validate_shape (not verify), BraidStatus/BraidError re-exported from crate root, Braid fields private with read-only accessors, design doc in draft status.

All blocking invariants are enforced at runtime. Approving.

[approve]

@flyingrobots flyingrobots dismissed coderabbitai[bot]’s stale review June 15, 2026 02:58

Dismissed as stale: CodeRabbit later verified all five blocking issues resolved in #545 (comment), all review threads are resolved, and CI/CodeRabbit status checks are green.

Copy link
Copy Markdown
Owner Author

Self-Code Review: Code Lawyer Follow-up

@codex please verify these findings. The stale CodeRabbit CHANGES_REQUESTED review was dismissed as superseded; current review-thread count is zero. This fresh pass reviewed origin/main...HEAD after a clean worktree check and git fetch origin.

ID Severity File / Lines Infraction Evidence Mitigation Prompt
SCR-01 P3 Docs/API Contract docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md:12, :27, :79; code evidence crates/warp-core/src/revelation.rs:76, crates/warp-core/src/strand.rs:138 Design overstates typestate enforcement as a compile-time guarantee. CausalPostureState remains a public trait and Strand<P> still exposes retention_posture / _marker, so external construction can forge the static type. Settlement integrity is protected by runtime registry/posture revalidation, but the design text still says this is a compilation invariant/static guarantee. Update design 0028 to state the actual contract: typestate APIs guide callers and narrow normal use, while settlement safety is ultimately enforced by live-registry and runtime posture validation. Avoid saying non-Shared settlement is impossible purely at compile time unless the trait/fields are actually sealed.
SCR-02 P5 Docs Accuracy docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md:49-63, :214-221 Rust snippets are stale / malformed. The CausalPostureState impl snippets omit the required causal_posture() method, and the BraidError::IncoherentSequence snippet duplicates actual: u64. These are small but they make the design packet less trustworthy as an implementation handoff. Correct the typestate snippet so each impl shows the required causal_posture() return, or mark the block as pseudocode. Remove the duplicated actual: u64 line from the BraidError snippet.
SCR-03 P4 Performance / Determinism Hygiene crates/warp-core/src/braid_shell.rs:747-767 Duplicate member validation is still quadratic. check_unique_member_strands scans members[..index] for every member. This runs during assembly/validation/replay, and BraidMemberRef already implements Ord; a deterministic BTreeSet removes the avoidable O(n²) path without changing semantics. Replace the prefix scan with a BTreeSet<BraidMemberRef> seen set after the mixed sealed/revealed posture check. Return DuplicateMemberStrand when insert(member.member_ref) returns false. Add/keep a regression proving duplicate sealed and revealed refs are rejected deterministically.

Checks run during this review:

  • git status --porcelain clean
  • git fetch origin
  • git diff --check origin/main...HEAD
  • pnpm exec markdownlint-cli2 docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md backlog/cool-ideas/CI-003-append-only-braid-membership.md CHANGELOG.md
  • gh pr checks 545 all passing at head f8c5fa20

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f8c5fa206d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md Outdated
Comment thread docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md Outdated
Comment thread crates/warp-core/src/braid_shell.rs Outdated
Comment thread crates/warp-core/src/braid.rs
Comment thread crates/warp-core/src/settlement.rs Outdated
Comment thread crates/warp-core/src/braid_shell.rs
Comment thread crates/warp-core/src/braid.rs
@flyingrobots

Copy link
Copy Markdown
Owner Author

Activity Summary — Self-Review Resolution

All discovered self-review/Codex findings have been resolved and their review threads are marked resolved.

ID Severity Source Area Commit Outcome
SCR-01 P3 Self/Codex docs/design/0028.../design.md typestate guarantee wording 0fe395b1 Runtime posture validation is now documented as authoritative; typestate language is qualified.
SCR-02 P5 Self/Codex docs/design/0028.../design.md stale snippets 0fe395b1 Typestate snippet now includes required method bodies; malformed BraidError snippet was verified clean.
SCR-03 P4 Self/Codex crates/warp-core/src/braid_shell.rs duplicate-member scan 0fe395b1 Duplicate detection now uses deterministic BTreeSet instead of prefix scanning.
SCR-04 P2 Codex crates/warp-core/src/braid.rs mixed revealed/sealed braid members 26319760 Braid::apply now rejects mixed member-reference posture before mutation.
SCR-05 P2 Codex crates/warp-core/src/braid.rs empty finalization 64962811 Braid::apply now refuses settlement finalization with an empty frontier.
SCR-06 P2 Codex crates/warp-core/src/settlement.rs sealed-member linkability 1ed6601d SettlementPolicy now carries redacted MemberBlindingSalt; hidden commitments can vary by settlement salt while staying stable across parent movement for the same salt.
SCR-07 P2 Codex crates/warp-core/src/braid_shell.rs proof-bound digest format 6ac53871 BRAID_SHELL_VERSION is now 2 for the proof-digest marker shape.
CR-NIT P5 CodeRabbit crates/warp-core/src/revelation.rs unsealed typestate trait b8fbb2fa CausalPostureState is sealed to Echo marker types; runtime posture validation remains the settlement authority.

Local witnesses passed:

  • cargo test -p warp-core --lib — 560/560
  • cargo test -p warp-core --test strand_contract_tests — 28/28
  • cargo test -p warp-core --test braid_public_api_tests — 1/1
  • cargo clippy -p warp-core --lib -- -D warnings -D missing_docs
  • cargo fmt --all -- --check
  • pnpm exec markdownlint-cli2 CHANGELOG.md docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md
  • git diff --check
  • scripts/ban-globals.sh
  • scripts/ban-nondeterminism.sh

Pre-push also passed exact affected slices and pushed b8fbb2fa to this PR branch. Remote CI is currently running; CodeRabbit status is green at the time of this comment.

@flyingrobots

Copy link
Copy Markdown
Owner Author

Code Lawyer: Final Phase I Self-Audit Finding

@codex please verify this final self-audit finding before merge. Worktree was clean, git fetch origin completed, PR review-thread GraphQL returned 49 total / 0 unresolved, and git diff --check origin/main...HEAD was clean.

ID Severity File / Lines Infraction Evidence Mitigation
FINAL-SCR-01 P5 Docs/API Contract docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md, BraidError snippet Design doc snippet omits implemented BraidError variants. Current code defines MixedMemberReferencePosture and EmptySettlementFrontier; the design snippet lists the braid error enum without those variants. Update the design snippet so documented error variants match crates/warp-core/src/braid.rs; validate with a deterministic rg witness and markdown lint.

@flyingrobots

Copy link
Copy Markdown
Owner Author

Activity Summary — Final Code Lawyer Pass

Final self-audit completed on head 0a613077. One docs/API-contract drift was found, posted for transparency, fixed, validated, committed, and pushed. Review-thread GraphQL remains 49 total / 0 unresolved.

ID Severity Source File Commit Outcome Witnesses
FINAL-SCR-01 P5 Self docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md 0a613077 BraidError design snippet now documents MixedMemberReferencePosture and EmptySettlementFrontier; invariant prose now names mixed revealed/sealed membership and empty frontier finalization checks. RED: `rg -n "MixedMemberReferencePosture

Local merge gate after push is green: cargo fmt --all -- --check; cargo test -p warp-core --lib; cargo test -p warp-core --test strand_contract_tests; cargo test -p warp-core --test braid_public_api_tests; cargo clippy -p warp-core --lib -- -D warnings -D missing_docs; pnpm exec markdownlint-cli2 CHANGELOG.md docs/design/0028-strand-typestates-proof-envelopes-and-evolving-braids/design.md; git diff --check origin/main...HEAD; scripts/ban-globals.sh; scripts/ban-nondeterminism.sh.

Merge gate is waiting only for GitHub Actions to finish on the final pushed head before the authorized admin merge.

@flyingrobots flyingrobots merged commit d741c6f into main Jun 15, 2026
36 checks passed
@flyingrobots flyingrobots deleted the refactor/braid-shell-member branch June 15, 2026 03:45

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

collapsed_from.is_some() || (!any_plural && !any_conflict)

P2 Badge Validate collapse shells against their plural parent

When a Derived shell carries collapsed_from, this accepts the shell regardless of whether its members actually match the named plural parent (or even contain any plural verdicts). Since append_braid_shell only calls validate() and replay_braid_shell only checks that the parent digest resolves to some plural shell, a malformed retained shell can name a valid plural parent while carrying unrelated all-derived members/result refs and still replay as a lawful collapse.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

DuplicateMember {
member_ref: BraidMemberRef,
},
EmptyCollapseWitness,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add the missing BraidError variants to the snippet

When this design doc is used as the braid API contract, the BraidError snippet under-documents two implemented rejection cases: crates/warp-core/src/braid.rs defines MixedMemberReferencePosture and EmptySettlementFrontier, but the documented enum jumps from DuplicateMember to EmptyCollapseWitness. Consumers following the snippet would not know to handle mixed revealed/sealed members or empty settlement frontiers, so the design listing should be kept in sync with the implementation.

Useful? React with 👍 / 👎.

Comment on lines +159 to +163
fn default_member_blinding_salt(policy_id: Hash) -> MemberBlindingSalt {
let mut hasher = Hasher::new();
hasher.update(DEFAULT_MEMBER_BLINDING_SALT_DOMAIN);
hasher.update(&policy_id);
MemberBlindingSalt(hasher.finalize().into())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use non-public per-settlement salt for sealed members

For callers that use SettlementPolicy::default() or allow_plural_over_footprint_overlap() without overriding the salt, this default is just a deterministic hash of the public policy id, so the same strand/child-worldline/retention tuple produces the same sealed blinded_commitment across retained shells. That turns the sealed member reference into a stable cross-shell tracking handle, which undercuts the privacy/linkability boundary this feature is meant to provide; the default salt needs non-public per-settlement entropy or the API should force callers to supply it.

Useful? React with 👍 / 👎.

///
/// Returns a posture obstruction error if the strand's actual posture is not Shared.
pub fn try_into_shared(self) -> Result<Strand<Shared>, StrandError> {
if self.retention_posture.causal_posture == crate::revelation::CausalPosture::Shared {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate the full posture before granting Shared typestate

When a dynamic strand is hand-built or mutated through its public fields, setting only retention_posture.causal_posture to Shared is enough for try_into_shared() to return Strand<Shared>, even if RetentionPosture::validate() would reject the bundle for a missing admission scope or invalid source-disclosure policy. That leaks an invalid posture through the static Shared type for any caller that relies on typestate evidence outside the registry-backed settlement path, so this conversion should revalidate the whole retention posture before constructing the Shared handle.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant