Skip to content

integration/consolidated-current — single-branch consolidation + Phase 2 (2A/2B) - #8

Draft
focusedbrain wants to merge 162 commits into
mainfrom
integration/consolidated-current
Draft

focusedbrain wants to merge 162 commits into
mainfrom
integration/consolidated-current

Conversation

@focusedbrain

@focusedbrain focusedbrain commented Aug 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

WR Code / Public Handshake — Email E2E slice, complete through Phase 5 on the single permanent development branch, now stamped for the final rig build.

Milestone Tag / commit Do-not-regress
Phase 2 phase-2-complete @ a310cb96 201 = 201
Consolidation-inherited remediation b46680a4 35 removed, 0 new
Phase 3 phase-3-complete @ 1c1cb3ef 166 = 166
Addendum 3G (contract v1.1) d8ac21b1 166 = 166
Pre-Phase-4 block 56dbf7c2 166 = 166
Phase 4 phase-4-complete @ 0a7ca3ae 166 = 166
Seal-key-source fix 6c758b80 0 new
Phase 5 phase-5-complete @ f12c6262 166 = 166
Rig stamp build047 21389e8e code unchanged (stamp only)

Rig build state

HOST_HASH = 21389e8ebe3936a573b292ad53c76a7ccd095f1b (short 21389e8), origin tip matches, tree clean, phase-5-complete is an ancestor. Expected [RUNTIME_IDENTITY]: commit == 21389e8e…, buildStamp == build047 (21389e8).

The stamp bump moves three literals together because three consumers read them: the extension outDir, VITE_EXT_BUILD_STAMP, and the Windows output-dir marker parsed by kill-wr-desk.cjs. The Electron stamp derives from the extension outDir by regex and appends the short HEAD at build time.

Slice content

Phases 2–5 delivered the provenance gate and fail-open closure, the unsuppressible rule-8 alert, CPR as a typed analysis input, the resolution infrastructure (hardened client, dual-channel validation, head/envelope/EVP verification, contract v1.1 embedded delegation), the A6 three-field status composition with resolution-bearing offers, and the email→offer path with EVP-first-render and manual entry.

E2E acceptance (a)–(d) met at logic level. UI rendering is unverified by design (no build, no app start in the agent session) — Phase-5 report §6 states exactly what that leaves for the rig. The integration-pending list is carried complete: nothing waived, no substitute trust path.

Details: code/docs/analysis/wr-code-email-e2e/ (reports + committed captures/) and code/docs/spec/.

Notes

  • main remains for author-designated document drops only.
Open in Web Open in Cursor 

Local Dev and others added 30 commits July 24, 2026 13:12
…trix, migration and risk, refactor plan)

Co-authored-by: Cursor <cursoragent@cursor.com>
…y, ingress admission, dead-path removal)

One shared full-claim identity guard (issuer+subject+email+wrdesk id, exact
match) replaces every partial comparison on ingest/ack/return paths; old
comparators deleted. Additive iss columns on coordination_handshake_registry
with lazy backfill (first write wins). New per-relationship ingress admission
filter is the first stage for all inbound deliveries incl. the BEAP inbox -
blocked transmissions die pre-visibility with an audit record. Removes dead
skipConsentForAutomation field, unused verifier, and no-op version step, with
structural-absence tests. Realm-distribution inventory + phase report in
docs/analysis/wr-handshake-gap/phase-1-report.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
… frozen core record, key extraction, anti-rollback)

Canonicalization module with domain-separation tags; frozen signed core
record (wr_canonical_v3 envelope) carrying the complete capsule content as
a critical declaration, verified fail-closed on receive on top of legacy
rules; containers with preserve-unknown parsing and criticality refusal
naming the namespace; namespace registry (implemented + reserved-inert).

Dual-format emission: v2 surface stays byte-compatible for old peers; new
receivers verify the envelope and mark evidence wire_format canonical_v3 /
legacy_v2. Core nonce store rejects replayed cores; generic anti-rollback
high-water store lands with documented backup/restore semantics. Key
extraction migration (v73) moves private key material into a dedicated
handshake_key_store (copy-before-null, idempotent); reads overlay the store.

Acceptance tests 1-7 green (replay compat, container semantics, canonical
determinism, nonce replay, key extraction, anti-rollback, do-not-regress at
exact baseline parity). ingress_path log-only guard added. Phase report in
docs/analysis/wr-handshake-gap/phase-2-report.md.
…, ledger freeze

- Profile registry (packages/ingestion-core/profileRegistry.ts): five records
  (pbeap_publisher, private_personal, org_internal, org_cross, legacy_v0) fixing
  signature cardinality, attestation rules, role symmetry, permitted ingress
  paths. resolveProfile is fail-closed [VII.4.2]; no conversion path [VII.4.7].
- verifyCanonicalEnvelope dispatches on the registry: unknown profile/version
  refusal naming the profile, distinct-key signature cardinality [VII.3.2],
  schema-level attestation presence/absence [VII.4.5]. New reason codes
  UNKNOWN_PROFILE / PROFILE_SCHEMA_VIOLATION surfaced in denial audit entries.
- Core store split (v75): wr_handshake_core append-only (UPDATE/DELETE aborted
  by triggers, anti-rollback high-water gated) + wr_handshake_runtime mutable
  slice; legacy writers dual-write through coreStore adapter; legacy_v0
  backfill with null ingress_path, unknown_legacy provenance, empty signature
  list (never fabricated).
- Ledger freeze at v74 via freezeAtVersion option + persisted ledger_meta
  marker (closes the lazy-migration hole); one-time hygiene sweep copies out
  and drops undocumented tables, re-asserts row-level key hygiene; hygiene
  assertion on every ledger open.
- ingress_path registry with initial identifiers (Q4 groundwork; log-only).
- Fix: 64-char-hex seed keys signed with a RANDOM key because
  generateKeyPairSync('ed25519', {seed}) silently ignores the seed; now
  wrapped in PKCS#8 DER (canonicalCore.ts, signatureKeys.ts).
- 30 new acceptance tests (profile dispatch, migration parity, hash
  stability, ledger freeze/sweep); phase report with handshake_type inventory
  (249 occurrences / 78 files) and rollback plan.

Co-authored-by: Cursor <cursoragent@cursor.com>
…gate, handshake_type elimination, silent revocation, edge-agent retirement)

Single formation pipeline (V1): formationPipeline.ts dispatches on the
Phase-3 profile registry; the four dialects are deleted (initiatorPersist,
recipientPersist, inbound auto-insert, edge-agent pairing). handshake_type
branching is eliminated - the admission situation is the profile parameter
same_principal (Q9: internal_device, UI label "Cross-Device"); legacy wire
compat is confined to samePrincipalWire.ts, the frozen db column, and
declared envelope-parse boundaries.

Capture methods + Connect-offer staging (V2, C1-C3): capture-method and
invitation-class registries (scan/assisted_discovery fail-closed stubs,
targeted_bound refusal-only); inbound invitations land in connect-offers.db
(own SQLite file, outside both relationship handles) and only a consent
event forms a record. Failed verification suppresses the offer entirely -
structurally unreachable, no override. 7-day timeout (Q7). Capture
provenance is a signed contract declaration (optirando.decl.capture_provenance)
on new formations; consent records are Hash-Pinned (preview +
bound-definition + contract-state hashes) with tamper invalidation.

Silent revocation (V5): revoke-notify capsule removed - enforcement is
exclusively the Phase-1 ingress admission filter (zombie old-build peers'
sends die pre-visibility with a logged record, verified). Q8: revocation no
longer deletes context blocks/embeddings/audit rows; content deletion is
the separate explicit operator action deleteRevokedRelationshipContent
(handshake.deleteRevokedContent RPC).

Edge-agent fold-in (V8/I3): edge_ingestor dialect retired for new
formations (RETIRED_FORMATION_DIALECTS, fail-closed unknown_profile);
legacy pairings stay readable by the agent dist; lockstep upgrade
documented.

Acceptance tests 1-8 green (phase4OneFormationPipeline,
phase4SilentRevocation, phase4EdgeAgentFoldIn acceptance suites +
structural scans). Wide do-not-regress sweep: failure set byte-identical
to the Phase-3 baseline (0 new, 0 fixed). Sequencing evidence and
deviations in docs/analysis/wr-handshake-gap/phase-4-report.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
…tap execution consent, Tier-L evidence chain, capability tokens)

Grant objects (E2-E4, E9) [VII.10.x]: wr_grants (migration v76, vault only)
with delivery/preparation rights and deliberately no execute variant; the
Phase-1 receiver-side ingress filter now consumes grant scopes (off-scope
blocked pre-visibility + logged + revoke offer after repetition); admitted
deliveries carry grant_ref provenance on email and P2P BEAP paths; legacy
relationships lazily backfilled from effective_policy (never a fabricated
consent); limit extensions parse-level critical.

Execution grants deleted + per-tap consent (V4) [VII.10.1, IX.19.2]:
GRANTED_TOOLS and ACTIVE-handshake blanket authorization removed; every
execution requires a fresh, single-use, Intent-Hash-bound human consent tap
(executionConsent.ts, wr_execution_consents); divergence from the presented
preview refuses execution and records a deviation PoAE; fail-closed kill
switch WRDESK_EXECUTION_CONSENT_TAP (never a consent-free path).

Evidence chain (H1-H4) [IX.19.1, X.10.1]: wr_evidence_chain - append-only by
trigger, per-contract monotonic sequence, SHA-256 prev-hash chaining,
explicit genesis at cutover; PoAC/PoAE writers on formation, grant
lifecycle, admission blocks, content deletion, and executions; BER schema
representable now (writers in Phase 6).

Ledger repurposing (Q10): handshake-ledger.db is the Tier-L evidence home
(ledger-native schema, hygiene-allowlisted); header docs state the actual
transitional dual role honestly.

Capability-token schema (T4/Q13) [XII.12.6 annex-number-provisional]:
carriage-only tokens with preserve-unknown-optional parsing (p2p_signal
pattern), optional context_scope/delegation_chain, delegable defaults false,
critical limit extensions.

Hygiene (H5): deleteHandshakeRecord no longer deletes audit rows; audit_log
frozen for mutation via triggers on both handles (INSERT stays open);
retention carve-out excludes wr_evidence_chain and audit_log. Pre-existing
purge losses are unrecoverable and the chain claims no pre-cutover
continuity.

Also fixes a Phase-4 latent bug: the default Connect-offer staging DB now
uses :memory: under vitest so test runs stop staging offers into the
developer-profile connect-offers.db (163 leaked fixture rows purged).

Acceptance tests 1-8 green (phase5GrantsEvidence.acceptance.test.ts et al);
do-not-regress verified against the Phase-4 baseline worktree - identical
pre-existing failure set, zero new regressions. Report:
docs/analysis/wr-handshake-gap/phase-5-report.md

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…sponsibility logging

EOF

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…json

Co-authored-by: Cursor <cursoragent@cursor.com>
…; stamp build046

Co-authored-by: Cursor <cursoragent@cursor.com>
…nce import depth

Channel provenance (SPF/DKIM/DMARC) and publisher resolution are mandatory
structural pipeline stages, not an opt-in trigger filter. A message failing
them yields no WR code and no affordance at all, so there is no class of
"WRCode-stamped email" a per-trigger checkbox could select. Disabling the
control left it naming a concept that does not exist.

Nothing ever produced the verdict it read: NormalizedEvent.wrcodeValid had no
writer, EventTagMatcher.evaluate has no production caller, and the router
behind the control is fed inline-chat and OCR text, never mail.

Removes the type, union member, evaluators, schema enum value, vestigial
wrcodeMatch field and the control itself; strips the condition from stored
agent configs at every read boundary. InputCoordinator's default: branch now
fails closed to match EventTagMatcher, which makes that stripping load-bearing
rather than cosmetic.

Also fixes email/providers/zoho.ts, which resolved aiProvenance five levels up
where its three peers in the same directory correctly use six - the cause of
both the blocked session:build and the 77 suite-load failures.

Co-authored-by: Cursor <cursoragent@cursor.com>
Brings main's document commits onto the branch so a fresh build covers the
complete state: WR Handshake Phases 1-5, art50 AI provenance, WR Code email
E2E Phase 1, and main. No code conflicts - main touched no source files.

Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
…II additions)

Additive delta to the v1.0 email E2E order: A-series decisions A1-A5,
Phase 3 additions 3D-3F (CatalogHead, DualAssuranceEnvelope, EVP), Phase 4
offer-schema and status additions, Phase 5 EVP-first-render, and acceptance
items (e)-(h).

Tracked rather than left in chat because the v1.0 order never was, which is
part of how the work lost its paper trail.

Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
…ug 2026)

Normative source for Delta v1.1 decisions A2 (EVP-first-render, XVII.4.4),
A4 (audit link, XVII.6) and A5 (platform suspension, XVII.3.3). Placed at
repository root alongside the other annexes.

Note: this annex depends throughout on Annex XIV 5.5 (Execution Authorization
Proof Chain and Catalog Commitment), which the Annex XIV copy in this repo
(v1.0, 26 July 2026) does not contain.

Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Author ruling on the collision named against Annex XVII XVII.3.2/XVII.3.3:
three orthogonal layers whose convergence is display-only. entry.status is
publisher-signed and platform suspension lives only in the envelope, so the
two cannot collide in data. Admission is conjunctive and fail-closed across
D4 status, entry.status and envelope.suspension; display keeps all three
distinct with headline precedence platform > entry > publisher-part.

Phase 4 bullet cross-references A6 for the composition rule.

Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Supersedes the A5 cross-reference wording with the authoritative statement:
the surface composes three orthogonal fields (D4 publisher-part status,
publisher-signed entry.status, platform envelope.suspension) under A6's
conjunctive fail-closed admission rule and headline precedence, with
distinct copy per layer. Offer-schema bullet unchanged.

Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Brings Refactor Order v1.0 (754e87e), the authoritative Annex XIV v1.1
carrying XIV.5.5, Annex XVII, and the updated Annexes IX/X/XI onto the
Phase-2 branch before order-02 work begins. Plain merge commit per the
author ruling: no cherry-picks, no rebase.

Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Local Dev added 16 commits September 25, 2026 12:09
- productionCompositionRoot.e2e: initWrcClient with no test seam builds every
  store on the durable security DB (redirected to a temp file) and an
  unconfigured deployment refuses a valid reference at Gate 2.
- connectOfferWrCodeSchema: pin the preview-hash key set (top level, entry,
  bound definition) and prove wr_code_class does not change the hash.
- provenanceGatesParsing.guard: pin the total of seven
  DepackageCutoverHeldError hold sites.
- relayRelease.e2e: source guard that the Gate-5 release chain carries one
  principal delegation, never a list.

Test-only change.
… SSO session (S2b, route)

Until now the WR Code RPCs existed only inside handleHandshakeRPC: the
renderer RPC dispatcher and the extension WebSocket forwarded vault.,
handshake., beap. and ingestion. only, so Runs 1-5 were reachable from tests
alone.

Both dispatchers now route wrc.* and refuse without an active SSO session
(getCurrentSession); unlike handshake.*, there is no skipVaultContext escape.
Submission and acceptance are therefore structurally account-bound (ruling 3).
The claimant party and keys still come from deployment config (Q19 default B).

Guard: productionRoute.guard.test.ts pins both routes, the session check,
the absent escape, and the five dispatcher targets.
153-set unchanged (plus the documented timing flake), +8 net tests, typecheck
extension 183 / workspace 428; fresh clone proven with test:prepare.
…ation (S2)

Agent draft under Q17 = A. Records what the client already expects (directory endpoint, per-class entry lookup keys, designation, closed signed objects, DNS and manifest formats), maps the wire shapes to Annex XVI v1.95 Appendix A.4, proposes public/account disclosure tiers (ruling 3, XVI.6.3), and leaves the relay/capsule wire legs open for S7. Lists client work C1-C8 and questions Q20-Q27. Not normative until ratified.
…-on domain by DNS (C1, C2)

C1 (XVI.6.5 entry assignment, XVI.16 entry substitution): deriveEntryDesignator refuses a P entry whose repository id differs from the reference's local block (designation_mismatch at Gate 3). Before, a registry could serve any other signed P entry of the same publisher under a local block and all six gates passed.

C2 (XVI.6.5 email-domain agreement): WrcDirectoryClient.dnsVerifiedDomains proves each registered domain live against its own _wr record. Gate 2 (acting principal) and Gate 6 (initiator SSO domain) accept only DNS-proven domains; before, only domains[0] was proven and any listed domain was accepted.

Tests: entryDesignator, fullChain (P substitution), directoryGate2 and capsuleAdmission (secondary domain with and without its own proof). Negative control: the 5 refusal tests fail without the fix. WRC contract v2.0 section 9.
…rt and captures

The Author accepted every recommended default on 2026-09-26. The delta loses its _DRAFT suffix, records the answers, and marks C1/C2 as implemented in fdf4a40. Before/after captures from the C:\\dev\\optirando checkout: 153-set unchanged, 0 regressions.
…ust pinned in source

pnpm run build:wrc-test compiles an in-process WR Code test registry behind the transport seam: six deterministic, seeded test publishers (catalogs, envelopes, Merkle proofs, dual-signed directory records, manifests, _wr answers) and 19 test codes covering every class and namespace status, through the real six gates. The signed-in SSO domain is registered live for the own test publisher. Test state lives in wrc-security.wrc-test.db and its own record cache; the app packages into build007-wrc-test beside the release build and shows a non-dismissible test-data banner; wrc.runtimeStatus reports the flavor.

Release builds alias the registry to an empty stub, scripts/verify-wrc-build-flavor.cjs fails any build chain whose bundle does not match its flavor, and WRC trust comes only from wrcTrustAnchors.ts (XVI.6.4 pinned; environment ignored; test- key ids refused). Only unbundled dev runs read WRDESK_WRC_* trust variables.

Tests: wrcTestRegistry (every code's gate outcome, one-time use, SSO domain, determinism, containment), wrcBuildFlavor.guard (alias, define, build chains, stub parity, pinned trust, output folder, code sheet), wrcTestRegistryRuntime.e2e (per-flavor composition root on a real security DB). Both builds verified. Code sheet: docs/operational/wrc-test-build.md.
…egistry, pinned release trust)

Records the reachability decision (built-in registry behind the transport seam, test builds only; no dev address allowance), what f8bf53f changed, and the verification: 153-set unchanged, 0 regressions, both build flavors verified.
A WR Code button in the message toolbar opens a panel: manual entry with a local format check, Find WR Codes in this message, Check through the six gates, then the offer with Accept/Decline or a status refusal. The surface renders main's view model (wrcSubmissionView.ts): offers are the EVP-first projection of offerPresentation.ts, statuses reuse the three-layer copy of entryStatusSurface.ts, every refusal has human copy and a tone, reason codes stay secondary.

Main decides the scan: wrc.scanMessage reads the sealed row and scans only when its Channel Provenance Record decodes with channel_pass (HC2, W5); manual entry is always available (HC3). wrc.declineReference releases the XVI.8.4 reservation at once; the panel declines an offer left open when the user checks another code, closes the panel or leaves the message.

Tests: wrcSubmissionView (every test code), wrcMessageScan (authenticated, unauthenticated, tampered, missing), runtime decline e2e, WrCodePanel (scan gating, offer render, decline on new check and on leave, none after accept; negative control verified). Route guard: eight wrc.* methods. docs/operational/wrc-test-build.md: where codes are entered.
…2b UI, first increment)

Maps the increment to HC2-HC4, HC6, W1, W2, W5; records what is not in it (HC5 connect-offer consent, S5, audit link, S7). Twelve review screenshots of the real panel with real pipeline outcomes. 153-set unchanged, 0 regressions.
…e rendered preview (HC5); directory C3/C4/C5/C8

The desktop handshake:accept answered HANDSHAKE_NOT_FOUND for every staged Connect offer, so no inbound request could be accepted in the app since Phase 4. It now resolves the staged offer, requires expected_preview_hash for it, and forwards the hash to consent. handshake.list carries connect_offer_preview; the accept dialog renders it and returns its hash.

WRC directory: the SC responder check uses the signed directory status (C3); the record carries a connector (C4); a grammar other than 2 is refused (C5); display_origin reaches the namespace verdict only after normalization and membership checks (C8).
…r channel (C6); account-tier credential and 401 (C7); HC5 in the extension and on every product route

S5: the WR Code offer shows the checked display_origin as the responsible domain, re-rendered from the verified directory record, never a link. C6: GET /v1/directory/operator-rollovers is decoded and folded from the pinned anchor when a record names an unknown operator key (at most once a minute, one shared fetch); the test registry's TEST03 verifies only through it. C7: the HTTP transport sends a bearer on entry and object reads only; 401 is entry_account_required with its own copy, not the capture error. The production credential source is not wired yet.

HC5: the extension renders the connect-offer preview and sends its hash; both product RPC dispatchers require the hash for a staged offer and for consentToOffer. The extension accept dialog no longer reports a refused accept as accepted. S4: stale interim comments in gatePipeline.ts updated.
@focusedbrain
focusedbrain force-pushed the integration/consolidated-current branch from 3a57dc6 to ad03c74 Compare September 26, 2026 18:42
Local Dev and others added 13 commits October 30, 2026 13:45
…and qBEAP preview fix

- 0.13 One egress check before every model call of handshake:chatWithContextRag
  (structured, selected-document, hybrid-structured, semantic). Locality comes
  from the provider instance (only OllamaProvider is local; cloudai counts as
  remote). Remote models never receive blocks whose governance denies cloud AI
  or marks them sensitive; assertRagEgressCleared runs right before each call,
  and a source guard enforces it.
- 0.14 No implicit scope: without a selected handshake/relationship the chat
  returns context_selection_required instead of using the newest handshake.
- 0.15 A stored API key is not consent: key sync and the orchestrator bootstrap
  yield a local config; inbox AI uses a cloud provider only on an explicit
  'cloud' preference and only with a checked per-message cloud policy
  (BEAP-origin messages follow their handshake's ai_processing_mode);
  /api/ocr/process ignores forceCloud; the extension sync sends 'local'.
- 0.16 Confidentiality fails closed: no ledger, no row or a read error reads as
  confidential; a sync with linked profiles and a locked vault (or an
  unreadable profile) never lowers the mirrored scope. Four existing suites now
  state their non-confidential fixture premise explicitly.
- 0.24 The local text-only chat lane refuses images (vision_not_supported, HTTP
  422) instead of dropping them; the watchdog retries text-only and reports
  "Screens not analysed".
- 0.4 Letter PDF export logs field names only, with a source guard.
- qBEAP P2P-inline ingest reassigned a const preview, so every decrypted qBEAP
  kept its placeholder preview (TS2588 from the typecheck baseline).

Decision D-37: recommended option (local first; cloud only through the
egress check; keys are not consent).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0 regressions, 0 repaired against iteration-06.after.3927cfaa.txt
(153 = 153, one known userDataBootstrap timeout flake); +43 tests;
typecheck app 426 (-2), extension 183.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…0.26, D-38), sealed sandbox-to-host mail delivery with receipts

Honest status ("fetched" is not success, "delivered" is)
- The sandbox poll reports held_delivery_failed / held_depackage_failed instead of
  ok when it delivered fewer messages than it fetched.
- The host never shows a green sync for delivered < fetched, whatever status the
  sandbox sends, and any unknown non-ok status is a named failure.
- New ingestion status PAUSED_DELIVERY_FAILED with a red banner in the app and the
  extension explainer.

Relay rule (decision D-38) and hardening (roadmap 0.26)
- internal-inference-p2p-invariants: the relay is blind (routing metadata and
  ciphertext only) and carries p2p_signal, sealed service RPC and handshake capsules.
- Sealed service RPC schema_version 2: static-static X25519 sender authentication
  bound to the device key pinned on the handshake, expires_at + message_id bound in
  the AAD, expiry checks on open, and a replay claim by the handler that takes the
  message (opening stays stateless so the dispatch chain can route by inner type).
- Every relay sender seals v2; every relay receiver requires it. v1 remains only for
  the in-capsule context_blocks_sealed canonical form. Source guard included.

Sealed sandbox-to-host mail delivery (replaces the retired direct-LAN POST)
- The sandbox seals the guest-derived result (custody-sealed originals stay on the
  sandbox) to the host's device key and sends it via the relay (24 h store-and-forward,
  6 MB plaintext cap, loud too_large_for_relay).
- The host checks the internal pair roles, maps the row to its account (mailbox
  address, then trigger account), deduplicates per account + provider message id,
  writes the inbox row and answers with a sealed receipt (stored/duplicate/failed).
- A sandbox delivery ledger skips acknowledged messages and resends unacknowledged or
  failed ones after 30 minutes (at-least-once, no loss).

Both devices need this build: an older peer cannot open v2 envelopes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ath)

0 regressions, 0 repaired against iteration-07.after.b1abbb4e.txt
(153 = 153, two known flakes green in isolation); +32 tests;
typecheck app 424 (-2), extension 183.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and keep the new registration on takeover (relay)

The relay keeps one connection per (user, device) and terminates the older one
when a second arrives. The client opened a second socket whenever a caller found
it not connected (the outbound queue's delivery-ack ensure), including while the
first socket was still opening; the evicted socket's close then cleared the shared
reference and scheduled another connect, which evicted the other socket in turn —
about one reconnect per second forever (3,331 connects in one host run), with no
close logged.

Client (coordinationWs.ts)
- connect() returns while a socket is open or opening and cancels a pending
  reconnect timer instead of racing it.
- Every socket handles only its own events; a replaced socket's late close logs
  close_stale_socket and changes nothing. close logs code and reason; a close
  before open rejects the connect promise.

Relay (wsManager.ts)
- Only the registered socket may deregister its (user, device) key. A replaced
  socket closing after the takeover used to remove the new registration, so live
  push missed the device until it reconnected. Needs a relay redeploy.

Tests: a loopback relay with the eviction rule (old client: 7 connections in 4 s,
new client: 1) and a relay takeover unit test (fails on the old relay code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t eviction loop)

0 regressions, 0 repaired against iteration-08.after.9c0c6b63.txt
(153 = 153, one known flake green in isolation); +4 tests;
typecheck app 418 (-6), extension 183.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…relay on the server

Run on the relay server. Detects the running relay container (podman or docker),
reads its ports, /data volume, restart policy, network and the environment that
differs from the image defaults, updates the repository, checks that the takeover
fix is in the source, builds packages/coordination-service/Dockerfile, backs up the
database after stopping, starts the new container with the same configuration,
waits for /health and rolls back automatically on failure. The old container is
kept as <name>-old-<timestamp>. DRY_RUN=1 prints the plan without changing anything.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ranch with the current OIDC issuer

Takes the relay deploy script that runs on the server into the repository
(code/scripts/ops/deploy-podman.sh) and replaces redeploy-relay.sh, so there is one
deploy script.

Changes to the server copy:
- BRANCH defaults to integration/consolidated-current (main lacks the relay
  changes since 2026-07-10, including the WebSocket takeover fix); overridable.
- COORD_OIDC_ISSUER / JWKS point at auth.optirando.com, where the app signs in
  (src/auth/oidcConfig.ts) and which Keycloak puts into the token's iss. The old
  auth.wrdesk.com is unreachable; with it the relay rejects every token (WebSocket
  closed with 4001 right after open, HTTP 401 on /beap/flush-queued). Overridable.
- Preflight: the JWKS URL must answer HTTP 200 from the server.
- The database (and WAL/SHM) is backed up after the old container stops and
  before the new one starts.
- git checkout -B "$BRANCH" origin/$BRANCH, so a branch that does not exist
  locally yet is created instead of resetting whatever branch is checked out.
- UTF-8 instead of Windows-1252.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…restart

The app makes a new X-Launch-Secret on every start and hands it over only in the
WebSocket handshake. Until the extension reconnected, sign-in failed with
"Unauthorized: missing or invalid launch secret", and the auth status check cached
that 401 as "logged out".

On that specific 401 the background now waits for the handshake (connecting at once
instead of on the backoff timer) and sends the request once more: sign-in, sign-in
wait, logout, auth status and electronRequest. A rejected status request is no
longer cached as a logged-out answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants