feat(warp-core): plural settlement + theta_braid retained boundary shells (design 0026, E1)#539
Conversation
E1 of the strand/braid campaign (echo#537 requirements 1-2, #538 rider): SettlementDecision::Plural + in-graph theta_braid shells in the BoundaryTransitionRecord family, replay-from-shell as the acceptance test. Source facts verified @465cf61e. Awaiting packet + test-plan review before RED.
…ready exists admission.rs#150-152@465cf61e mints the Plural discriminant at admission scope; the slice extends the algebra to settlement scope with no new ABI discriminant.
ENHANCE->APPROVE verdict applied in full: - outcome algebra is Derived/Plural/Conflict/Obstruction only; collapse is a witnessed transition emitting a new Derived shell with collapsed_from lineage, never a fifth arm - plural-settlement policy and collapse policy explicitly separated with the four-rule block - BraidShell made replay-sufficient: canonical member entries carry support-pin/basis/frontier/footprint/claim digests + compact verdict snapshots; no entry vectors, no histories - Obstruction included in shell emission scope; shells append-only - canonical member ordering + domain-separated digests (echo.shell.braid.v1 etc.) + permutation/tamper/query tests - E0-lite posture core (RevelationPosture, author-only default, least-revealed-member invariant) pinned as first commit of E1 - N-strand honesty rule; serialization variant-tag requirement; ABI wording corrected to wire-format-only claim - enhanced 12-item acceptance criteria + 11-item test plan adopted
Three-Tier Thinking Room substrate (Paper VII 6.3, echo#538) required
before any theta_braid shell lands, per design packet 0026 resolved
question 1:
- RevelationPosture { Scratch < AuthorOnly < Shared } with AuthorOnly
default — nothing ships with implicit shared visibility; stable
canonical wire tags for digest domains
- promote_posture: posture only widens, explicitly and witnessed;
narrowing and same-posture requests are typed PostureObstructions,
never silent no-ops
- least-revealed-member invariant via shell_posture_obstruction: a
composite shell cannot reveal more than its least-revealed member
- 10 unit tests; clippy-clean (remaining repo clippy errors are
pre-existing in coordinator.rs, untouched here)
Part of lane/0026-braid-shell-family (E1). theta_braid and
SettlementDecision::Plural build on these nouns next.
…fact (0026)
Type surface + failing tests for SettlementDecision::Plural per the
approved 0026 test plan (items 1, 5, 7 + regression pin):
- SettlementDecision::PluralAlternative(PluralAlternativeDraft) lowering
to the existing AdmissionOutcomeKind::Plural; draft carries source ref,
contended slots, policy id, and RevelationPosture (default AuthorOnly)
- SettlementPolicy { policy_id, plural } separates plural-settlement law
from any future collapse policy; Default = Refused (today's behavior)
- plan_with_policy / settle_with_policy entries; plan()/settle()
delegate with the default policy — legacy paths regression-pinned by
default_policy_plan_matches_legacy_plan_exactly
- ProvenanceEventKind::PluralArtifact retained-residue event kind;
append_plural_artifact mirrors conflict retention (no-op patch — the
base never silently takes the strand's value)
- ConflictReason::PluralUpstream (code 6): later suffix entries cannot
import past retained plurality
- ABI: PluralAlternativeDraft + PluralAlternative variant +
RevelationPosture + ConflictReason::PluralUpstream +
SettlementResult.appended_plurals (serde default — wire-additive)
RED: settlement_plans_plural_alternative_under_explicit_plural_policy
and settlement_retains_plural_artifact_without_collapsing_base_state
fail (plan still emits conflict under plural policy); 7 legacy tests
green. GREEN flips the overlap-not-clean branch next.
…print overlap (0026) Flips the overlap-not-clean branch: when each claim applied lawfully on its own and an explicit plural-settlement policy permits coexistence, the strand's claim is retained as SettlementDecision::PluralAlternative instead of conflict residue. The base worldline never silently takes the strand's value; later suffix entries are blocked with ConflictReason::PluralUpstream rather than importing past retained plurality. plural_id is domain-separated (echo:settlement-plural-artifact:v1) over target worldline, source coordinate, contended-slots digest, and policy id — deterministic across runs and input orderings of the same facts. All 9 settlement tests green (2 RED tests now pass; 7 legacy pinned); workspace clean under -D warnings.
…rine notes Checkpoint review corrections (0026): - settlement_overlap_slots_digest sorts slots by canonical byte key before hashing; plural_draft canonicalizes the retained slot vec. New test: slot permutation cannot move plural_id or overlap digest. - Packet records the E1a hierarchy invariant (PluralAlternative is per-entry residue; theta_braid is the plural settlement boundary; BraidShell is the replayable outcome) plus named debts: thin PluralArtifact event kind, PluralUpstream->Obstruction migration, posture witness quality bar.
…gate (0026) The braid shell module lands fully implemented as pure record law — shells are data discipline, not behavior — with 6 tests green: - BraidShell/BraidShellMember/BraidShellOutcome over the four-arm algebra; collapse is Derived-with-collapsed_from lineage, never a fifth arm; members carry compact digest sets + verdict snapshots, never histories - domain-separated digests (echo.shell.braid.v1, echo.braid.member.v1, echo.braid.witness.v1); canonical member ordering (permutation cannot move the shell digest); witness + shell digests recomputed on validate(), so tampering with policy/posture/verdict/outcome fails - posture floor enforced at assembly and validation (shell cannot reveal more than its least-revealed member) - replay_braid_shell reproduces outcome arm + member verdicts through BraidShellRecords — a trait whose only method returns shells, so strand-history access is a type error; collapse lineage requires a retained Plural parent - ProvenanceService retains shells append-only (validated, idempotent, divergent-content refused) with query iterator and take_braid_shells for hostile replay proofs RED (3 settlement gate tests fail — emission not wired): - plural settlement retains exactly one shell with full body - shell replays after runtime, registry, and provenance are dropped - derived and conflict settlements also retain shells SettlementResult.braid_shell: Option<Hash> (always None until GREEN); ABI braid_shell_digest serde-default additive.
…ells (0026) Every non-empty settlement act now retains exactly one theta_braid shell as its final fallible step (a failed settle restores entries before any shell describing them could land): - one member per settled strand (N-capable structures; E3 owns weave): verdict = plural > conflict > derived precedence over the per-entry decisions; verdict digest binds the full ordered decision sequence; claim digest binds ordered source commit hashes; basis / frontier / footprint / support-pin digests domain-separated - outcome arm follows the algebra: Plural carries the retained plural artifact ids (linking the boundary to its per-entry residue), Conflict carries ordered reason codes, Derived carries the appended import refs - the sacred test passes: after settling under plural policy, the shells are extracted and the runtime, strand registry, and provenance entries are dropped — replay_braid_shell reproduces the outcome arm, member verdicts, and policy id from the shells alone 497/497 warp-core tests green; workspace clean under -D warnings.
…_braid review (0026) Must-fixes: - shell version is digest-bound (hash_shell_body hashes the stored version) AND explicitly validated (UnsupportedVersion); tamper test - one retained boundary family made explicit in code: RetainedBoundaryRecord trait + RetainedBoundaryKind implemented for both BoundaryTransitionRecord (tick, echo.shell.tick.v1 digest) and BraidShell — theta_tick and theta_braid are visible siblings - no-leak law encoded as test: failed settlement retains no shell - residue->shell direction: plural_shell_index on ProvenanceService; braid_shell_for_plural resolves a plural artifact id to its boundary without scanning (no archaeology) - empty-settlement law named and tested: no claims means no braid outcome; an empty settlement emits no shell by law Should-fixes: - plural alternative_ids are a canonical set: sorted at assembly, NonCanonicalAlternativeOrder on validation; permutation test - support pins sorted before digesting (tuition paid once) - claim_digest binds the full source coordinate (worldline, tick, commit), not commit hash alone - first-class query API: BraidShellQuery + query_braid_shells (scan-backed), tested by member/outcome/posture Collapse transition (append-only or bust): - WitnessDigest newtype refuses zero and empty-input digests — a witness is never a 32-byte shrug - collapse_braid_shell: named CollapsePolicy -> new Derived shell with collapse_policy + collapse_witness + collapsed_from, plural parent byte-identical; missing policy -> retained Obstruction shell (CollapseResult::Obstructed), because obstruction is also law - Derived lineage fields are all-or-none (IncoherentCollapseFields); collapse-derived shells may summarize the plural members they collapsed; settlement-derived shells may not 508/508 warp-core tests; workspace clean under -D warnings.
…trait, canonical sets, residue links, collapse)
📝 WalkthroughWalkthroughThis PR introduces plural settlement outcomes with retained, replayable braid shells and revelation posture tracking. Plural alternatives are now a first-class settlement decision encoded in θ_braid shells, which canonicalize member ordering, compute deterministic digests, enforce posture floor constraints, and support append-only collapse lineage. The system integrates with provenance service for plural artifact resolution and with settlement planning/execution under explicit policies. ChangesPlural Settlement and Braid Shell System
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Blockers: 1. BoundaryTransitionRecord::boundary_digest is now a full canonical content digest: payload coordinates, every entry's coordinate, event kind, head key, parents, and hash triplet, plus the auth tag — a record digest, not a vibes checksum 2. WitnessDigest side door closed: check_outcome_law applies the newtype bar to collapse_witness and obstruction witnesses in both assemble() and validate(); empty-witness shells refuse to exist 3. plural_shell_index is append-only: a plural artifact id may never migrate to a different shell (PluralArtifactAlreadyBound); bindings are checked before anything inserts, so refusal leaves no partial state 4. take_braid_shells clears the residue index — no stale archaeology 5. BraidCoordinate(hash(basis, canonical member digests, policy_id)) stored on every shell, recomputed at validation (CoordinateMismatch), queryable via BraidShellQuery.coordinate — EchoCoordinate::Braid stops being decorative Should-fixes: - Obstruction outcomes carry obstructed_from: Option<Hash>; no-policy collapse names its plural parent; replay verifies obstruction lineage the same way it verifies collapse lineage - duplicate alternative_ids refused (a sorted list with duplicates is not a set, it is a list wearing a fake mustache) - duplicate member strands refused (N-capable invariant landed now) - late no-leak proof: pre-binding the deterministic plural id forces shell retention to fail AFTER entries append; test proves entries and frontier roll back, no shell leaks, index unchanged 516/516 warp-core tests; workspace clean under -D warnings.
|
All five blockers and all four should-fixes landed in 81501d1 (516/516 warp-core tests, workspace clean under -D warnings): Blockers
Should-fixes One emergent interaction worth noting: tampering a member's Ready for re-review. |
There was a problem hiding this comment.
Actionable comments posted: 6
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/witnessed_suffix.rs (1)
1084-1113:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAlign overlap-slot digesting with canonical ordering across modules.
conflict_reason_to_abinow includesPluralUpstream, but this module’ssettlement_overlap_slots_digestis still order-sensitive.crates/warp-core/src/settlement.rscanonicalizes slot bytes before hashing; this one does not. The same overlap set can emit differentoverlapping_slots_digestvalues depending on path, which breaks deterministic cross-surface contracts.Suggested fix (make witnessed-suffix digest canonical like settlement)
fn settlement_overlap_slots_digest(slots: &[SlotId]) -> Hash { + let mut encoded: Vec<Vec<u8>> = slots.iter().map(canonical_slot_bytes).collect(); + encoded.sort_unstable(); let mut hasher = Hasher::new(); hasher.update(b"echo:settlement-overlap-slots:v1\0"); - hasher.update(&len_to_u64(slots.len()).to_le_bytes()); - for slot in slots { - hash_settlement_slot(&mut hasher, slot); + hasher.update(&len_to_u64(encoded.len()).to_le_bytes()); + for slot_bytes in &encoded { + hasher.update(slot_bytes); } hasher.finalize().into() } -fn hash_settlement_slot(hasher: &mut Hasher, slot: &SlotId) { +fn canonical_slot_bytes(slot: &SlotId) -> Vec<u8> { + let mut bytes = Vec::new(); match slot { SlotId::Node(node) => { - hasher.update(&[1]); - hasher.update(node.warp_id.as_bytes()); - hasher.update(node.local_id.as_bytes()); + bytes.push(1); + bytes.extend_from_slice(node.warp_id.as_bytes()); + bytes.extend_from_slice(node.local_id.as_bytes()); } SlotId::Edge(edge) => { - hasher.update(&[2]); - hasher.update(edge.warp_id.as_bytes()); - hasher.update(edge.local_id.as_bytes()); + bytes.push(2); + bytes.extend_from_slice(edge.warp_id.as_bytes()); + bytes.extend_from_slice(edge.local_id.as_bytes()); } SlotId::Attachment(attachment) => { - hasher.update(&[3]); + bytes.push(3); match attachment.owner { AttachmentOwner::Node(node) => { - hasher.update(&[1]); - hasher.update(node.warp_id.as_bytes()); - hasher.update(node.local_id.as_bytes()); + bytes.push(1); + bytes.extend_from_slice(node.warp_id.as_bytes()); + bytes.extend_from_slice(node.local_id.as_bytes()); } AttachmentOwner::Edge(edge) => { - hasher.update(&[2]); - hasher.update(edge.warp_id.as_bytes()); - hasher.update(edge.local_id.as_bytes()); + bytes.push(2); + bytes.extend_from_slice(edge.warp_id.as_bytes()); + bytes.extend_from_slice(edge.local_id.as_bytes()); } } match attachment.plane { - AttachmentPlane::Alpha => hasher.update(&[1]), - AttachmentPlane::Beta => hasher.update(&[2]), - }; + AttachmentPlane::Alpha => bytes.push(1), + AttachmentPlane::Beta => bytes.push(2), + } } SlotId::Port((warp_id, port_key)) => { - hasher.update(&[4]); - hasher.update(warp_id.as_bytes()); - hasher.update(&port_key.to_le_bytes()); + bytes.push(4); + bytes.extend_from_slice(warp_id.as_bytes()); + bytes.extend_from_slice(&port_key.to_le_bytes()); } } + bytes }As per coding guidelines:
crates/warp-core/**is the deterministic kernel, so canonical ordering must hold across all deterministic 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/witnessed_suffix.rs` around lines 1084 - 1113, The overlap-slot digest in settlement_overlap_slots_digest is order-sensitive but must be canonicalized like in settlement.rs; change it to first convert each SlotId into its canonical hashed-bytes (using the same hash_settlement_slot transformation/format), collect those byte sequences into a Vec, sort them lexicographically, then feed the sorted byte sequences (with the same "echo:settlement-overlap-slots:v1\0" prefix and len_to_u64 count) into the Hasher before finalizing so the digest matches settlement.rs for the same set regardless of input order.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 154-170: Derived::result_refs are included in shell identity
without canonicalization, causing equivalent shells with refs in different
orders to produce different digests; update hash_into (the match arm for
Self::Derived) to canonicalize/sort result_refs before hashing (e.g., sort by a
stable key derived from each ProvenanceRef) so hash_provenance_ref is called on
a deterministic order, and ensure the same canonicalization is applied where
refs are forwarded (e.g., collapse_braid_shell's selected_result_refs) and where
validate checks equality so comparison uses the canonical form; use existing
helpers hash_provenance_ref and hash_optional_digest and pick a stable
comparator (e.g., digest or tuple of fields) to order ProvenanceRef instances.
- Around line 121-125: BraidShellOutcome::Plural must be rejected when
alternative_ids is empty: add a check in the shell validation/assembly path (the
functions named assemble and validate that process BraidShellOutcome) to return
an error if a Plural variant has alternative_ids.is_empty(), and update any code
paths that assume a valid plural (e.g., braid_shell_for_plural and
ProvenanceService::append_braid_shell) to rely on that validation so no plural
shell without alternative_ids is persisted or causes missing plural_shell_index
entries; ensure the error path prevents storing/plumbing plural shells with
empty alternative_ids.
In `@crates/warp-core/src/provenance_store.rs`:
- Around line 1751-1788: append_braid_shell currently only runs shell.validate()
but must also reject shells whose collapsed_from or obstructed_from point to
missing or non-Plural parents before persisting; update append_braid_shell to,
prior to inserting into self.braid_shells, check any shell.collapsed_from and
shell.obstructed_from (and any plural parent ids in the outcome) against
self.braid_shells and ensure the referenced parent exists and its outcome is
crate::braid_shell::BraidShellOutcome::Plural (or otherwise acceptable),
returning an appropriate crate::braid_shell::BraidShellError if the parent is
absent or has the wrong outcome so we never persist shells that cannot replay in
replay_braid_shell.
In `@docs/design/0026-braid-shell-family-and-plural-settlement/design.md`:
- Around line 145-155: Update the "Planned shape" pseudocode for
BraidShellOutcome::Derived to match the implemented variant in
crates/warp-core/src/braid_shell.rs: change result_ref to result_refs:
Vec<ProvenanceRef>, rename PolicyRef to Hash (collapse_policy: Option<Hash>),
add collapse_witness: Option<Hash>, and ensure collapsed_from uses Hash; make
the types and field names (result_refs, collapse_policy, collapse_witness,
collapsed_from) consistent with the code so the design section reflects the
actual BraidShellOutcome::Derived structure.
- Around line 161-171: Clarify and codify whether RevelationPosture exists at
shell-level, member-level, or both: update the design text and the planned shape
so BraidShell either (A) has no posture field and its visibility is derived as
shell.posture = min(member.posture for member in BraidShell.members) (the
"least-revealed-member" invariant), or (B) has an explicit posture field that
must be kept consistent with member postures via rules (e.g., shell.posture is
initialized to min(member.postures) and any shell-level promotion to Shared
requires a witnessed shell-level transform that optionally also promotes
member.postures); reference the types BraidShell, BraidShellMember,
RevelationPosture, the "least-revealed-member" invariant, and digest/replay
semantics so the doc states exactly which posture(s) are canonical for digesting
and when witnessed promotions apply.
- Around line 182-186: Update the design doc to state that canonical member
ordering is strictly ascending by BraidShellMember::member_digest (not the
4-tuple), matching the implementation in BraidShell::assemble which calls
members.sort_by_key(BraidShellMember::member_digest) and the validate() check
that errors on any prior > current; remove or replace the “sort key:
basis_digest, strand_ref, support_pin_digest, claim_digest” text unless you
explicitly redefine that 4-tuple to include every field hashed by member_digest
(e.g., frontier_digest, footprint_digest, verdict tag/digest, posture) so it
exactly matches BraidShellMember::member_digest().
---
Outside diff comments:
In `@crates/warp-core/src/witnessed_suffix.rs`:
- Around line 1084-1113: The overlap-slot digest in
settlement_overlap_slots_digest is order-sensitive but must be canonicalized
like in settlement.rs; change it to first convert each SlotId into its canonical
hashed-bytes (using the same hash_settlement_slot transformation/format),
collect those byte sequences into a Vec, sort them lexicographically, then feed
the sorted byte sequences (with the same "echo:settlement-overlap-slots:v1\0"
prefix and len_to_u64 count) into the Hasher before finalizing so the digest
matches settlement.rs for the same set regardless of input order.
🪄 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: 82033a4f-f02a-4b46-9bb6-a66c23b2631e
📒 Files selected for processing (9)
crates/echo-wasm-abi/src/kernel_port.rscrates/warp-core/src/braid_shell.rscrates/warp-core/src/coordinator.rscrates/warp-core/src/lib.rscrates/warp-core/src/provenance_store.rscrates/warp-core/src/revelation.rscrates/warp-core/src/settlement.rscrates/warp-core/src/witnessed_suffix.rsdocs/design/0026-braid-shell-family-and-plural-settlement/design.md
| /// Multiple lawful alternatives remain retained. | ||
| Plural { | ||
| /// Stable plural artifact ids of the retained alternatives. | ||
| alternative_ids: Vec<Hash>, | ||
| }, |
There was a problem hiding this comment.
Refuse Plural shells that carry no alternative_ids.
Right now BraidShellOutcome::Plural { alternative_ids: vec![] } passes assemble/validate as long as one member verdict is Plural. That leaves you with a retained plural shell that has no residue→shell mapping material to index, and ProvenanceService::append_braid_shell will store it without inserting any plural_shell_index entries.
Based on docs/design/0026-braid-shell-family-and-plural-settlement/design.md, plural shells are supposed to list plural artifact ids so braid_shell_for_plural can resolve residue → shell.
Also applies to: 404-417, 488-501
🤖 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 121 - 125,
BraidShellOutcome::Plural must be rejected when alternative_ids is empty: add a
check in the shell validation/assembly path (the functions named assemble and
validate that process BraidShellOutcome) to return an error if a Plural variant
has alternative_ids.is_empty(), and update any code paths that assume a valid
plural (e.g., braid_shell_for_plural and ProvenanceService::append_braid_shell)
to rely on that validation so no plural shell without alternative_ids is
persisted or causes missing plural_shell_index entries; ensure the error path
prevents storing/plumbing plural shells with empty alternative_ids.
| pub fn append_braid_shell( | ||
| &mut self, | ||
| shell: crate::braid_shell::BraidShell, | ||
| ) -> Result<Hash, crate::braid_shell::BraidShellError> { | ||
| shell.validate()?; | ||
| let digest = shell.digest; | ||
| if let Some(existing) = self.braid_shells.get(&digest) { | ||
| if *existing != shell { | ||
| return Err( | ||
| crate::braid_shell::BraidShellError::DuplicateDigestDivergentContent { digest }, | ||
| ); | ||
| } | ||
| return Ok(digest); | ||
| } | ||
| // Index bindings are append-only retained truth: a plural artifact | ||
| // id may never migrate to a different shell. Check every binding | ||
| // before inserting anything so a refused append leaves no partial | ||
| // state behind. | ||
| if let crate::braid_shell::BraidShellOutcome::Plural { alternative_ids } = &shell.outcome { | ||
| for plural_id in alternative_ids { | ||
| if let Some(existing) = self.plural_shell_index.get(plural_id) { | ||
| if *existing != digest { | ||
| return Err( | ||
| crate::braid_shell::BraidShellError::PluralArtifactAlreadyBound { | ||
| plural_id: *plural_id, | ||
| existing_shell: *existing, | ||
| attempted_shell: digest, | ||
| }, | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| for plural_id in alternative_ids { | ||
| self.plural_shell_index.insert(*plural_id, digest); | ||
| } | ||
| } | ||
| self.braid_shells.insert(digest, shell); | ||
| Ok(digest) |
There was a problem hiding this comment.
Reject invalid collapse lineage before persisting the shell.
append_braid_shell only calls shell.validate(), which is intra-record. A shell with collapsed_from or obstructed_from pointing at a missing or non-Plural parent is accepted here and only fails later when replay_braid_shell checks lineage. That leaves durable store state containing shells that can never replay. Validate the referenced parent against self.braid_shells before insertion.
🤖 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/provenance_store.rs` around lines 1751 - 1788,
append_braid_shell currently only runs shell.validate() but must also reject
shells whose collapsed_from or obstructed_from point to missing or non-Plural
parents before persisting; update append_braid_shell to, prior to inserting into
self.braid_shells, check any shell.collapsed_from and shell.obstructed_from (and
any plural parent ids in the outcome) against self.braid_shells and ensure the
referenced parent exists and its outcome is
crate::braid_shell::BraidShellOutcome::Plural (or otherwise acceptable),
returning an appropriate crate::braid_shell::BraidShellError if the parent is
absent or has the wrong outcome so we never persist shells that cannot replay in
replay_braid_shell.
| **E0-lite posture core (first commit of E1):** | ||
|
|
||
| ```rust | ||
| enum RevelationPosture { Scratch, AuthorOnly, Shared } | ||
| ``` | ||
|
|
||
| Default `AuthorOnly`. Promotion to `Shared` is an explicit witnessed act. | ||
| Invariant: **a braid shell cannot reveal more than its least-revealed | ||
| member** unless a witnessed redaction/promotion transform exists. Posture | ||
| is load-bearing, not cosmetic: it affects query, replay digests, promotion, | ||
| and visibility. |
There was a problem hiding this comment.
Clarify posture field presence in BraidShell struct.
Lines 161–171 describe RevelationPosture (E0-lite posture core) with default AuthorOnly, promotion as explicit witnessed act, and least-revealed-member invariant. However, the "Planned shape" pseudocode for BraidShell (line 128) includes posture: RevelationPosture, but the description at lines 163–171 does not explicitly state whether the posture is:
- A shell-level field (single per shell, reflecting the shell's visibility boundary)
- A member-level field (per
BraidShellMember, line 142) - Both
The E0-lite description is correct in principle, but the interaction between shell-level posture (if present) and member-level posture (line 142) under the "least-revealed-member" invariant needs clarification. Does the shell's posture always equal the minimum of member postures, or is it separately witnessed/promoted?
This affects replay visibility and digesting (lines 185–186 claim digest domains include posture), so the scope must be explicit in the design.
🤖 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/0026-braid-shell-family-and-plural-settlement/design.md` around
lines 161 - 171, Clarify and codify whether RevelationPosture exists at
shell-level, member-level, or both: update the design text and the planned shape
so BraidShell either (A) has no posture field and its visibility is derived as
shell.posture = min(member.posture for member in BraidShell.members) (the
"least-revealed-member" invariant), or (B) has an explicit posture field that
must be kept consistent with member postures via rules (e.g., shell.posture is
initialized to min(member.postures) and any shell-level promotion to Shared
requires a witnessed shell-level transform that optionally also promotes
member.postures); reference the types BraidShell, BraidShellMember,
RevelationPosture, the "least-revealed-member" invariant, and digest/replay
semantics so the doc states exactly which posture(s) are canonical for digesting
and when witnessed promotions apply.
| **Determinism:** members are canonically ordered (sort key: | ||
| `basis_digest, strand_ref, support_pin_digest, claim_digest` — or a single | ||
| canonical `member_digest`). Digest domains are explicit and separated: | ||
| `echo.shell.tick.v1`, `echo.shell.braid.v1`, `echo.shell.import.v1`, | ||
| `echo.braid.member.v1`, `echo.braid.witness.v1`. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Show the referenced doc snippet with line numbers
echo "=== docs/design/0026-braid-shell-family-and-plural-settlement/design.md (182-200) ==="
sed -n '170,210p' docs/design/0026-braid-shell-family-and-plural-settlement/design.md | nl -ba
# 2) Find where these digest/sort key fields are implemented/used in code
echo
echo "=== rg: basis_digest / strand_ref / support_pin_digest / claim_digest / member_digest ==="
rg -n --hidden --no-ignore-vcs -S "basis_digest|strand_ref|support_pin_digest|claim_digest|member_digest" .
# 3) Narrow to likely ordering/sorting code patterns
echo
echo "=== rg: sort / ordering / canonically / canonical / deterministic / member_digest ==="
rg -n --hidden --no-ignore-vcs -S "(sort|sorted|ordering|ordered|canonically|canonical|deterministic|determinism).*(basis_digest|strand_ref|support_pin_digest|claim_digest|member_digest)" . || true
# 4) Specifically look for braid/member ordering structures if they exist
echo
echo "=== rg: braid.*member.*(sort|order|ordered|digest) ==="
rg -n --hidden --no-ignore-vcs -S "braid.*member.*(sort|order|ordered|digest)" . || trueRepository: flyingrobots/echo
Length of output: 208
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== docs design snippet (requested range) ==="
sed -n '170,210p' docs/design/0026-braid-shell-family-and-plural-settlement/design.md
echo
echo "=== rg: digest field names (find ordering/sort usage) ==="
rg -n --hidden --no-ignore-vcs -S "basis_digest|strand_ref|support_pin_digest|claim_digest|member_digest" .
echo
echo "=== rg: ordering/canonical determinism hints ==="
rg -n --hidden --no-ignore-vcs -S "(sort|sorted|ordering|ordered|canonically|canonical|deterministic|determinism)" docs src . || true
echo
echo "=== rg: braid/member ordering code ==="
rg -n --hidden --no-ignore-vcs -S "braid.*member.*(sort|order|ordered|digest)" . || trueRepository: flyingrobots/echo
Length of output: 50439
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== crates/warp-core/src/braid_shell.rs: member_digest + member ordering ==="
sed -n '1,160p' crates/warp-core/src/braid_shell.rs
echo
sed -n '280,520p' crates/warp-core/src/braid_shell.rs
echo
echo "=== Narrow search for sort calls in braid_shell.rs ==="
rg -n "sort_by_key|sort_by\(|canonical_member_digest_list|member_digest\(\)" crates/warp-core/src/braid_shell.rsRepository: flyingrobots/echo
Length of output: 15475
Fix design doc: canonical member order is ascending BraidShellMember::member_digest (not the 4-tuple).
BraidShell::assemble canonicalizes member ordering with members.sort_by_key(BraidShellMember::member_digest); validate() errors on any decrease (prior > current), so the order is strictly ascending by member_digest.
The design text “sort key: basis_digest, strand_ref, support_pin_digest, claim_digest — or a single canonical member_digest” is under-specified/misleading: the implementation sorts only by member_digest(), which hashes additional fields beyond those four (e.g., frontier_digest, footprint_digest, verdict tag, verdict_digest, posture). Update docs/design/0026-braid-shell-family-and-plural-settlement/design.md (lines 182-186) to declare the exact ordering strategy (ascending member_digest) and remove the 4-tuple alternative unless you redefine it to exactly match member_digest()’s contents.
🤖 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/0026-braid-shell-family-and-plural-settlement/design.md` around
lines 182 - 186, Update the design doc to state that canonical member ordering
is strictly ascending by BraidShellMember::member_digest (not the 4-tuple),
matching the implementation in BraidShell::assemble which calls
members.sort_by_key(BraidShellMember::member_digest) and the validate() check
that errors on any prior > current; remove or replace the “sort key:
basis_digest, strand_ref, support_pin_digest, claim_digest” text unless you
explicitly redefine that 4-tuple to include every field hashed by member_digest
(e.g., frontier_digest, footprint_digest, verdict tag/digest, posture) so it
exactly matches BraidShellMember::member_digest().
Source: Coding guidelines
…ites The remaining #539 merge blocker: ProvenanceEntry's retained patch body (header fields explicitly, since patch_digest does not bind the header, plus the canonical patch_digest which binds ops and slots), materialization outputs (channel + frame bytes), and atom-write provenance (atom, rule, tick, old/new values) are now bound by the boundary digest. No retained field named like content is excluded. Sensitivity test proves it: stripping the patch, appending an output frame, or appending an atom write each move the digest. 517/517 warp-core tests; workspace clean under -D warnings.
|
Final blocker resolved in 00233fc: PR body updated: 13 commits, 517/517. Nobody is selling the dragon's tail separately. |
|
@codex please review |
🐰 Code Lawyer self-review —
|
| # | Severity | File:Loc | Type | Issue |
|---|---|---|---|---|
| M1 | 🟠 Major | provenance_store.rs:1214 (ProvenanceCheckpoint) + settlement.rs settle_with_policy |
Latent correctness | No-leak guarantee is sequencing-dependent, not enforced. checkpoint_for/restore snapshot only per-worldline entry/checkpoint lengths; braid_shells + plural_shell_index are not covered. Safe today only because append_braid_shell is the last fallible step. Any future fallible step after it leaks a shell describing rolled-back history — the exact class this system exists to murder. |
| D1 | 🟡 Medium | revelation.rs promote_posture |
Witness law hole | Accepts raw Hash witness; [0;32]/empty shrug passes. Braid shells enforce the bar via check_outcome_law+WitnessDigest; posture promotion does not. Exported pub API (no prod caller yet). |
| D2 | 🟡 Medium | coordinator.rs:2438 vs braid_shell.rs hash_event_kind |
DRY / divergence | Two independent ProvenanceEventKind hash encoders (byte-string tags vs numeric tags). Compiler-guarded against omission only; schemes can silently diverge in meaning. |
| D3 | 🟡 Medium | braid_shell.rs validate/assemble |
Efficiency (scales w/ N) | member_digest() (blake3) recomputed ~4× per member per validate() (order loop + coordinate + witness + shell digest); assemble uses sort_by_key not sort_by_cached_key. Trivial at E1 (1 member), O(4N) at E3. |
| L1 | 🔵 Low | braid_shell.rs assemble/validate # Errors |
Doc accuracy | Documented error set omits DuplicateMemberStrand, DuplicateAlternativeId, EmptyWitness, CoordinateMismatch, UnsupportedVersion, IncoherentCollapseFields. |
| L2 | 🔵 Low | braid_shell.rs replay_braid_shell |
Error clarity | Obstruction-lineage failure returns InvalidCollapseLineage { collapsed_from } — field name is collapse-specific, reused for the obstructed_from case. |
| L3 | 🔵 Low | provenance_store.rs take_braid_shells |
API surface | pub production method existing only for hostile-replay proofs; consider #[doc(hidden)] or test-gating. |
| L4 | 🔵 Low | braid_shell.rs collapse_braid_shell |
Footgun | selected_result_refs silently dropped on the no-policy (obstruction) path. |
| L5 | 🔵 Low | braid_shell.rs compute_witness_digest |
Semantics | witness_digest = hash(WITNESS_DOMAIN ‖ body) then folded into shell digest — a self-witness, not independent attestation. Fine as E1 scaffolding; should be doc'd as such. |
| L6 | 🔵 Low | provenance_store.rs BraidShellRecords::shell |
DRY | Duplicates inherent braid_shell() body. |
| L7 | 🔵 Low | lib.rs exports + settlement.rs SettlementPolicy::default |
Housekeeping | braid_shell exports sit under // --- Truth delivery ---; default() re-hashes a constant per call. |
Tally: 0 critical · 1 major · 3 medium · 7 low. Recommend addressing M1 (or documenting the invariant at the call site with a guard) before relying on the no-leak property under future change; the rest are improve-on-touch.
@codex — please confirm M1 (checkpoint/restore vs shell-store coverage) and sanity-check D1/D2. Do you concur, or is M1 acceptable as a documented sequencing invariant for E1?
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 00233fc72a
ℹ️ 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".
| /// Retained braid shells by canonical shell digest (θ_braid family). | ||
| braid_shells: BTreeMap<Hash, crate::braid_shell::BraidShell>, | ||
| /// Residue → boundary index: plural artifact id to braid shell digest. | ||
| plural_shell_index: BTreeMap<Hash, Hash>, |
There was a problem hiding this comment.
Include braid shells in rollback checkpoints
These new retained-shell maps sit outside ProvenanceCheckpoint: checkpoint_for only records per-worldline entry/checkpoint lengths, and ProvenanceService::restore only delegates to the store restore. In any transaction that captures a checkpoint, appends a braid shell, then hits a later fallible step and restores (the settlement path is safe only because append_braid_shell is currently last), the new shell and plural index entry remain visible even though the provenance entries they describe were rolled back.
Useful? React with 👍 / 👎.
| if to == from { | ||
| return Err(PostureObstruction::AlreadyAtPosture { posture: from }); | ||
| } | ||
| Ok(PosturePromotion { from, to, witness }) |
There was a problem hiding this comment.
Reject placeholder witnesses for posture promotion
When callers promote posture with a placeholder digest such as [0; 32] or blake3_empty(), this returns a successful PosturePromotion even though the module promises every widening is witnessed. Because this is an exported API for moving artifacts toward Shared, a caller can accidentally persist an unauthenticated promotion; the braid-shell path already has WitnessDigest to reject these shrug values, but posture promotion bypasses that quality bar.
Useful? React with 👍 / 👎.
| // collapsed; a settlement-derived shell must carry none. | ||
| collapsed_from.is_some() || (!any_plural && !any_conflict) | ||
| } | ||
| BraidShellOutcome::Plural { .. } => any_plural, |
There was a problem hiding this comment.
Require plural shells to name an alternative
For a public caller assembling or validating a retained shell directly, BraidShellOutcome::Plural { alternative_ids: vec![] } is accepted as long as some member verdict is Plural. That leaves a shell that replays as a plural outcome but names no retained plural artifact for plural_shell_index/braid_shell_for_plural to bind, so the boundary can no longer be reached from any residue id despite claiming retained plurality.
Useful? React with 👍 / 👎.
| let mut witness_hasher = Hasher::new(); | ||
| witness_hasher.update(COLLAPSE_OBSTRUCTION_WITNESS_DOMAIN); | ||
| witness_hasher.update(&plural_shell_digest); | ||
| let mut policy_hasher = Hasher::new(); |
There was a problem hiding this comment.
Bind refused collapse attempts to the selected result
When a caller attempts to collapse a plural shell without a policy, this obstruction witness only hashes the parent shell digest, so two different selected_result_refs attempts against the same plural parent produce the same obstruction shell. That loses the actual attempted collapse target in the retained evidence and makes distinct refused acts indistinguishable/idempotent.
Useful? React with 👍 / 👎.
…026)
Eleven findings (0 critical / 1 major / 3 medium / 7 low), all resolved:
M1 (major) — no-leak becomes a mechanism: ProvenanceCheckpoint snapshots
the braid-shell + plural-index key sets and restore() prunes anything
retained after the checkpoint, so a rolled-back settlement cannot leak a
shell describing vanished history. RED test
restore_rolls_back_braid_shells_and_plural_index_added_after_checkpoint.
D1 — witnessed-promotion law enforced by the type system: WitnessDigest
moved to revelation; promote_posture takes WitnessDigest (a shrug cannot
construct, so it cannot reach the function). One shrug-rejection impl,
shared by posture promotion and the shell family.
D2 — one event-kind digest scheme: BoundaryTransitionRecord reuses
coordinator::hash_provenance_event_kind (now pub(crate)); braid_shell's
parallel encoder deleted.
D3 — member digests computed once per assemble/validate (sort_by_cached_key
+ a single digest vec feeding coordinate/witness/shell digests).
L1 accurate # Errors docs; L2 obstruction lineage uses
InvalidLineageParent { parent }; L3 take_braid_shells is #[doc(hidden)];
L4 collapse no-policy ref-drop documented; L5 witness_digest self-witness
scaffolding documented; L6 trait shell() delegates to braid_shell();
L7 export grouping clarified.
518/518 warp-core tests; workspace clean under -D warnings; changed files
clippy-clean incl --tests.
✅ Code Lawyer findings resolved —
|
| # | Sev | Resolution |
|---|---|---|
| M1 | 🟠 | ProvenanceCheckpoint now snapshots the braid-shell + plural-index key sets; restore() prunes anything retained after the checkpoint. No-leak is a mechanism, not a sequencing convention — proven by restore_rolls_back_braid_shells_and_plural_index_added_after_checkpoint. |
| D1 | 🟡 | WitnessDigest moved to revelation; promote_posture takes WitnessDigest, so a shrug witness cannot construct, cannot reach it. One shrug-rejection impl shared with the shell family. |
| D2 | 🟡 | BoundaryTransitionRecord reuses coordinator::hash_provenance_event_kind (now pub(crate)); the parallel numeric encoder is deleted. One canonical event-kind scheme. |
| D3 | 🟡 | Member digests computed once per assemble/validate (sort_by_cached_key + a single digest vec feeding coordinate/witness/shell). |
| L1 | 🔵 | # Errors docs on assemble/validate now enumerate the full error set. |
| L2 | 🔵 | Obstruction lineage uses InvalidLineageParent { parent } (no more collapse-specific field name on the obstruction path). |
| L3 | 🔵 | take_braid_shells is #[doc(hidden)]. |
| L4 | 🔵 | No-policy collapse's selected_result_refs drop is documented. |
| L5 | 🔵 | witness_digest documented as E1 self-witness scaffolding (integrity, not independent attestation). |
| L6 | 🔵 | BraidShellRecords::shell delegates to braid_shell(). |
| L7 | 🔵 | Export grouping comment clarified. |
Design packet updated with a "Record-law remediations" section. @codex — re-confirm M1 (checkpoint now covers the shell maps) and D1 (type-enforced witness) at your convenience.
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Hill
Make lawful plurality a retainable, replayable outcome: settlement gains the
Pluralarm of the outcome algebra, and every braid-scope settlement act retains a θ_braid shell — an in-graph holographic boundary that replays without member strand histories (AIΩN Paper VII Prop 3.5, §4.2). The hostile replay test extracts the shells and drops the runtime, strand registry, and provenance entries entirely; replay reproduces the outcome arm, member verdicts, policy id, and witness from the shells alone.Design packet:
docs/design/0026-braid-shell-family-and-plural-settlement/design.md(reviewed twice by James: packet ENHANCE→APPROVE; θ_braid core APPROVE + ENHANCE-before-PR — all enhancements landed).Doctrine issues: #537 (shell family, requirements 1–2), #538 (tier posture, E0-lite). Connective doctrine for #470/#476/#483 — this PR establishes the shell family they must reuse.
What landed (15 commits incl. a main-sync merge, RED-first throughout)
E0-lite posture core (
revelation.rs):RevelationPosture { Scratch < AuthorOnly < Shared }, AuthorOnly default, widen-only witnessed promotion, least-revealed-member invariant.Plural settlement (
settlement.rs):SettlementDecision::PluralAlternativelowering to the existingAdmissionOutcomeKind::Plural;SettlementPolicyseparates plural-settlement law from collapse law (Default = Refused = legacy behavior, regression-pinned byte-identically); the plural branch fires only where each claim applied lawfully on its own under explicit policy; the base never silently takes the strand's value; later entries block withConflictReason::PluralUpstream; retention viaProvenanceEventKind::PluralArtifact; canonical slot ordering so permutation cannot moveplural_id.θ_braid shells (
braid_shell.rs): four-armBraidShellOutcome(collapse isDerived+collapsed_fromlineage, never a fifth arm); members carry compact digest sets + verdict snapshots, never histories; domain-separated digests (echo.shell.braid.v1/ member / witness); version digest-bound and validated; canonical member and alternative-set ordering; posture floor at assembly and validation;replay_braid_shellthroughBraidShellRecords— a trait whose only method returns shells, so strand access is a type error.Settlement emission: every non-empty settlement retains exactly one shell as its final fallible step (no-leak law tested); Plural outcomes carry the plural artifact ids (boundary→residue);
plural_shell_indexgives residue→boundary; empty settlement emits no shell by law;BraidShellQueryscan-backed query by basis/member/outcome/posture.Collapse transition:
collapse_braid_shell— named witnessedCollapsePolicy→ newDerivedshell with all-or-none lineage fields; missing policy → retainedObstructionshell (obstruction is also law); plural parent stays byte-identical;WitnessDigestrefuses zero/empty digests.One boundary family in code:
RetainedBoundaryRecord/RetainedBoundaryKindimplemented by bothBoundaryTransitionRecord(θ_tick) andBraidShell(θ_braid).ABI
Additive only:
PluralAlternativeDraft,SettlementDecision::PluralAlternative,RevelationPosture,ConflictReason::PluralUpstream,SettlementResult.appended_plurals+braid_shell_digest(serde-default). No wire-format breakage; Rust exhaustive matches updated in-repo.Verification
-D warnings; clippy clean; fmt cleanHonesty notes
ProvenanceEventKind::PluralArtifact { plural_id }stays thin by design — the shell is the body; the index links the directions. Named debts (PluralUpstream→Obstruction migration, full E0 strand posture) recorded in the packet.Summary by CodeRabbit