feat(portability): surfaces content map + lossy one-way .af exporter (threads-jq4)#2
Conversation
Implement bead threads-jq4 by adding hash-verified PortableWeave surface payloads and a one-way Letta .af handoff exporter marked non-round-trippable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
✅ Nova sign-off — threads-jq4 portability: surfaces map + lossy .af exporter Surfaces map (PortableWeave.surfaces: BTreeMap<SurfaceId, PortableSurfaceContent>)
Lossy .af exporter (LossyAfExport)
sha2 dependency: workspace-pinned CI: No check results visible yet (draft → just marked ready). All four tests locally: format + clippy + test should pass cleanly given the implementation. Crate tag + coven pin bump recommendation: cut v0.1.3 now. Merging. |
There was a problem hiding this comment.
Pull request overview
This PR extends coven-threads-core portability (Phase 3 / C7) by adding an optional, hash-verified surfaces content map to the canonical .weave artifact and introducing a lossy, explicitly non-round-trippable Letta .af export path for handoff use-cases.
Changes:
- Add
PortableWeave.surfaces: BTreeMap<SurfaceId, PortableSurfaceContent>and verify surface payloads against per-threadContentHashstrands after envelope verification. - Add
export_weave_with_surfacesandexport_af(lossy one-way.afexporter) plus corresponding error variants. - Expand the C7 round-trip test suite to cover surface-map round-trips, tamper detection,
.afexport markers, determinism, and legacy.weaveimports without surfaces.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/coven-threads-core/tests/c7_roundtrip.rs | Adds fixtures and tests for surfaces-map verification, tamper failure, .af export markers/determinism, and legacy import behavior. |
| crates/coven-threads-core/src/portability.rs | Implements surfaces content-map support + verification, introduces lossy .af export types/functions, and adds new error variants. |
| crates/coven-threads-core/src/lib.rs | Re-exports the new portability APIs and .af export types. |
| crates/coven-threads-core/Cargo.toml | Adds sha2 as a workspace dependency for Sha256 surface hashing. |
| Cargo.toml | Adds workspace dependency version for sha2. |
| Cargo.lock | Locks new transitive dependencies introduced by sha2. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pub messages: Vec<serde_json::Value>, | ||
| /// Letta metadata field. | ||
| pub metadata_: Option<serde_json::Value>, |
| pub limit: usize, | ||
| /// Letta block metadata. | ||
| pub metadata_: Option<serde_json::Value>, |
| is_template: false, | ||
| label: surface.as_str().to_string(), | ||
| limit: content.data.len(), | ||
| metadata_: None, | ||
| template_name: None, |
| if !matched { | ||
| return Err(PortabilityError::SurfaceContentUnbound { | ||
| surface: surface.clone(), | ||
| }); | ||
| } |
…uqx) Add substantive annotations from Sage and Echo review (2026-07-18): Decision #1: rationale callback — ApprovalPath is authoritative, Channel is derived; if enforcement rule shifts, revisit all eight decisions. Decision #2: audit implication — close event needs reason field (applied|vetoed|expired|superseded); delayed-apply without it is a post-hoc audit black hole. Decision #3: forward-only promotion — retroactive region projection corrupts authority trail with apparently-authored writes from before promotion. Decision #4: fail-closed on extraction ambiguity — not silent LLM fallback; ambiguity is an explicit ignored/blocked state. Decision #5: RFC provenance pointer added. Decision #6: Cave ProposalView labeled [DESIGNED, NOT SHIPPED] from the start. Decision #7: label-variant round-trip is daemon wire contract (load-time reject on drift); Cave has zero policy freedom over label strings. Decision #8: proposal_window_opened paired with close event; window is a first-class audit interval. threads-uqx.3 design constraints (Sage): evidence_replay_hash on ProposalClassification; VetoWindow.min_visible: Duration; label mapping as daemon wire contract. Co-authored-by: Sage <sage@opencoven.dev> Co-authored-by: Echo <echo@opencoven.dev> Co-authored-by: Nova <nova@opencoven.dev>
* spec(proposal): draft Phase 5 approval semantics
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* spec(phase-4): align route-1 Returns with WeaveListView shape (threads-k9s)
Cave implementation wraps route-1 data as { weaves, degraded } rather than
overloading a WeaveSummary[] with degraded entries — the wrapped shape avoids
any fabricated health fields on degraded familiars. Same-day alignment of the
2026-07-18 §2.7/R12 amendment.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* spec(phase-5): record Val+Nova decisions, open Phase 5 (threads-uqx)
All 8 open questions resolved on Sage's recommended defaults:
- ApprovalPath separate from Channel (load axis preserved)
- Delayed apply only for veto windows (fail-closed posture)
- Classify-first for harness regions
- Deterministic + probe for invariants (not LLM-judge-only)
- RFC #3/#4 blocker resolved (familiar-contract@2a5bb98)
- Daemon contract before Cave ProposalView extension
- Display labels preserved, typed variants internal
- proposal_window_opened audit event lands with delayed apply
Rename PHASE-5-PROPOSAL -> PHASE-5-APPROVAL-SEMANTICS to reflect open status.
Co-authored-by: Nova <nova@opencoven.dev>
* spec(phase-5): fold Sage + Echo review into decision record (threads-uqx)
Add substantive annotations from Sage and Echo review (2026-07-18):
Decision #1: rationale callback — ApprovalPath is authoritative, Channel is
derived; if enforcement rule shifts, revisit all eight decisions.
Decision #2: audit implication — close event needs reason field
(applied|vetoed|expired|superseded); delayed-apply without it is a post-hoc
audit black hole.
Decision #3: forward-only promotion — retroactive region projection corrupts
authority trail with apparently-authored writes from before promotion.
Decision #4: fail-closed on extraction ambiguity — not silent LLM fallback;
ambiguity is an explicit ignored/blocked state.
Decision #5: RFC provenance pointer added.
Decision #6: Cave ProposalView labeled [DESIGNED, NOT SHIPPED] from the start.
Decision #7: label-variant round-trip is daemon wire contract (load-time reject
on drift); Cave has zero policy freedom over label strings.
Decision #8: proposal_window_opened paired with close event; window is a
first-class audit interval.
threads-uqx.3 design constraints (Sage): evidence_replay_hash on
ProposalClassification; VetoWindow.min_visible: Duration; label mapping as
daemon wire contract.
Co-authored-by: Sage <sage@opencoven.dev>
Co-authored-by: Echo <echo@opencoven.dev>
Co-authored-by: Nova <nova@opencoven.dev>
* feat(audit): add ApplyAudit event type, detail column, migration SQL (v0.1.4)
Closes coven-threads#5. Unblocks coven#414.
Changes:
- AuditEventType::ApplyAudit variant + apply_audit CHECK tag
- WardAuditRecord.detail: nullable JSON column for event-specific payload
- WardAuditRecord::for_apply() constructor
- apply_prev_sha256_hex() / apply_bytes_written() decode helpers
- WARD_AUDIT_MIGRATION_V014_SQL: transaction rebuild for v0.1.3 stores
- APPLY_AUDIT_DETAIL_KEY_PREV / _BYTES stable constants
- Exhaustiveness test extended; 3 new tests (shape, roundtrip, migration)
- Workspace version bumped 0.1.3 -> 0.1.4
- CHANGELOG.md created
Design: prev_sha256+bytes_written ride in detail JSON (Option A);
migration exports SQL constant, no rusqlite dep added (Option B-lite).
Bead: threads-3ym
* review(audit): add user_version ladder + explicit detail invariant doc
Addresses two Sage review notes (neither was blocking):
1. WARD_AUDIT_MIGRATION_V014_SQL now stamps PRAGMA user_version = 14
inside the transaction. Future migrations gate on 'user_version < N'
instead of substring-sniffing sqlite_master DDL. Migration doc-comment
updated to name the gate rule and explain the intentional non-idempotence
of the CREATE step (double-run fails loudly, not silently).
2. WardAuditRecord::detail doc-comment now names the full invariant for
ApplyAudit rows (prev_sha256 hex 64 chars + bytes_written u64) and
explicitly states other event types MAY leave it None or use their
own documented shape. Turns the implicit decoder contract into a
searchable spec.
CHANGELOG updated. Tests still green, clippy clean.
* feat(phase-5): core approval types — ApprovalPath, VetoWindow, ProposalClassification (threads-uqx.3)
Adds crates/coven-threads-core/src/approval.rs with the Phase-5 core type
sketch from specs/PHASE-5-APPROVAL-SEMANTICS.md §3, decisions 1/2/7/8.
Types introduced:
- ApprovalPath (AutoRegression | FamiliarCoherence | HumanApproval |
HumanApprovalWithRationale) — promotion ceremony, separate from Channel
- ApprovalPathKind — variant without inner fields for label round-trip
- VetoWindow — delayed-apply only; min_visible + duration; deadline helpers
- ProposalClassification — channel, regions, tier floor, approval_path,
evidence_replay_hash (WARD-C7 generalised)
- SurfaceRegionId — opaque region ref, full predicate in uqx.5
- WindowCloseReason — applied | vetoed | expired | superseded (decision 2+8)
- ProposalWindowAuditDetail / ProposalWindowCloseAuditDetail — detail payloads
for proposal_window_opened and close events
- ApprovalPathWireEnvelope — daemon wire format {variant, label, veto_deadline}
Key constraints enforced:
- ApprovalPath != Channel (decision 1); highest ceremony wins
- VetoWindow is delayed-apply only; min_visible <= duration (decision 2)
- display_label() + from_display_label() round-trip is exhaustive; unknown
labels return None so daemon rejects at load (decision 7)
- evidence_replay_hash on ProposalClassification (WARD-C7)
Tests: 15 passing (all approval tests green; full suite 90 passing)
Closes #8
* feat(phase-5): identity invariant predicates + advisory probes (threads-uqx.4)
Adds crates/coven-threads-core/src/identity_invariants.rs.
Types / implementations:
- FamiliarNameInvariant: PatternPredicate over Blake3 content hash of an
identity surface (e.g. IDENTITY.md). Fails closed on mismatch, snapped
thread, or missing thread (decision 4: ambiguity → reject, not fallback).
- ManifestAnchoredInvariant: PatternPredicate over manifest entry hash;
holds under Forced channel (WARD-C1..C6: must survive compaction).
- CompositeIdentityInvariant: all-components-must-hold composite predicate;
identity invariant is a unit — no partial pass.
- AdvisoryProbeResult: Gate-3 supplementary evidence shape; is_authoritative
always false; confidence validated in [0.0, 1.0].
- AdvisoryProbes: the advisory_probes block (Q4 decision) — separate from
deterministic probes block; empty means 'no advisory signals ran'.
Key constraints enforced:
- Predicate-first; no new strand kind (decision 3/4)
- Fail-closed on ambiguity (decision 4)
- advisory_probes structurally separate from probes; never sole authority
- descriptor is derived, never enforced on (§2.3)
Tests: 16 new (identity_invariants::tests), all passing.
Full suite: 106 tests, 0 failures.
Closes #9
* feat(phase-5): SurfaceRegionPredicate + Gate-4 replay (threads-uqx.5)
Adds crates/coven-threads-core/src/surface_regions.rs.
Types introduced:
- MaterializedDiff / SurfaceDiff: pure diff input; predicates are pure
functions of this (Gate-4 replay constraint — no Cave state, no stale
metadata, no agent self-report)
- RegionEvidence: predicate output; feeds ProposalClassification.affected_regions
and evidence_replay_hash; includes replay_bytes for Gate-4 deadline check
- SurfaceRegionDescriptor: derived, Cave-renderable, never enforced on
- SurfaceRegionPredicate trait: materialize (authoritative) + describe (derived)
- ExecutionPromptRegion: SOUL.md / AGENTS.md, tier-0 floor, human review
- ToolDefaultsRegion: TOOLS.md, tier-1 floor, familiar review
- HeartbeatBehaviorRegion: HEARTBEAT.md, tier-1 floor
- SurfaceRegionRegistry: classify_all + descriptors + path_tier_floor helper
Key constraints enforced:
- Classify-first; threads stay source-bound (decision 3)
- materialize is pure over MaterializedDiff — daemon-replayable at Gate-4
- replay_bytes are deterministic (sorted surface order)
- path_tier_floor = min across evidence (most protective wins)
- SurfaceRegionDescriptor never enforced on
Tests: 12 new (surface_regions::tests), all passing.
Full suite: 118 tests, 0 failures.
Closes #10
* spec(phase-5): resolve coherence review findings
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(phase-5): harden authority-boundary core
Bind semantic identity evidence into Gate-4 validation, commit replay to the complete materialized diff, validate wire contracts, and make proposal audit lifecycle persistence fail closed while preserving legacy history.
Bump the breaking public API to 0.2.0 and cover the retired invariant corpus, delayed approval semantics, schema migration paths, and append-only terminal behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Document Phase 5 and harden approval contracts
Updates docs and status pages to reflect Phase 4 freeze and active Phase 5 approval semantics, including a new delayed-apply scheduler diagram and expanded architecture/FAQ/glossary guidance.
In `coven-threads-core`, tightens fail-closed behavior by requiring the `auto_regression.veto` wire key, preserving stronger veto windows across path promotion, validating `ApplyAudit` detail shape and event-type accessors, enforcing advisory probe validation at deserialization, adding `#[must_use]` on key APIs, and expanding tests around these contracts.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Nova <nova@opencoven.dev>
Co-authored-by: Sage <sage@opencoven.dev>
Co-authored-by: Echo <echo@opencoven.dev>
Summary
Validation
Bead: threads-jq4