integration/consolidated-current — single-branch consolidation + Phase 2 (2A/2B) - #8
Draft
focusedbrain wants to merge 70 commits into
Draft
integration/consolidated-current — single-branch consolidation + Phase 2 (2A/2B)#8focusedbrain wants to merge 70 commits into
focusedbrain wants to merge 70 commits into
Conversation
…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>
Keep hand-authored Graphify config (.graphifyignore, Cursor rule) and gitignore the reproducible graph.json/html/report/cache outputs. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Email E2E Phase 2 added workspace dependency @repo/shared-beap-ui to apps/extension-chromium without updating pnpm-lock.yaml importer entries. Regenerated lockfile so pnpm install --frozen-lockfile succeeds on the consolidated stack. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
This 2MB JSON file is a vitest run summary (numTotalTests, testResults) committed during Email E2E Phase 2 work, not an intentional source artifact. Ignore similar dumps going forward. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Untracked Cursor agent state and dumps no longer appear in status. Already-tracked hand-authored rules under .cursor/rules/ remain tracked (gitignore does not untrack them). Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
…umbing Trace showed BeapMessage is populated only via sanitisedPackageToBeapMessage and inboxRowToBeapMessage. CPR lives in Electron depackaged_metadata, but handshake.beapInbox.list does not return that field — Option 1 would require sync-path surgery. Option 2 applied: Electron surfaces read CPR locally; BeapMessageDetailPanel takes an optional channelProvenanceRecord prop with unit tests; plumbing named "extension CPR plumbing (Phase 5)" in Delta v1.1. Also lands the consolidation section in the Phase-2 report (branch map, archive tags, ancestor checks, PR dispositions, workflow supersession). Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Supersedes the wholesale .cursor/ ignore. The Graphify integration is hand-authored and belongs in the repo: the always-on rule under .cursor/rules/ and, when used, .cursor/mcp.json. Agent state and dumps stay ignored; graph output stays untracked (derived, per-machine). Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
The order named ai_analysis_json as a candidate attachment point; it is the analysis OUTPUT column, not an input. The actual input point is the combined inbox analysis prompt: buildScamWatchdogUserContext / appendScamWatchdogToSystemPrompt, used by inbox:aiAnalyzeMessage and inbox:aiAnalyzeMessageStream. Only verdicts cross into the prompt — the depackaging boundary keeps raw Authentication-Results out, and the typed projection has no slot for them. Absence is stated explicitly so a missing record never reads as authenticated. Layering is one-directional and guarded: an unauthenticated channel may strengthen a concrete finding but is never a finding by itself, an authenticated one never clears a signal, and the model is told the rule-8 alert is the application's, not its to restate or replace. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
…ss capture Records the Option-2 trace and ruling, the actual 2C attachment point (the analysis prompt, not ai_analysis_json), and both captures. The 30 apparent new failures against the old phase-2-branch baseline are attributed by re-running the five suites at 743fd75: they predate this phase's work and arrived with the consolidation merges. Against the consolidated baseline the failure identity sets are equal. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Bounded pass ordered after phase-2-complete and before Phase 3, because p2p/coordination-client overlaps Phase-3 territory. Of the 35 failing identities in the five suites, only 29 are consolidation- inherited: they step in at exactly one commit, the #7 merge dffec03, whose canonical coordination-device-id role derivation the four clone-prepare fixtures do not satisfy — they still encode the retired local_role model and set no coordination ids. The product fails closed as that merge intended. The other 6 predate the consolidation and split three ways: harness schema lag, one unresolved seal-verification case where a product defect is not excluded, and two coordination-client tests that assert a deliberately retired relay path and an enqueue shape that no longer validates. CC_05b also fails alone at every commit probed yet passed in one full-workspace run, so the suite can report a broken test as green. Counting failures would not have caught that; the identity-set comparison did. No fixes applied; fix-vs-defer is the author's per class. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Delivered verbatim by the author via sandbox commit. Fulfils the Phase-3 prerequisite: the contract is an INTERFACE REFERENCE for the Phase-3 resolution client, not a build order for the WRC service. Phase 3 is built contract-first against local fixtures/mocks behind an isolated transport interface; no WRC service code. Not a Phase-3 start signal. Phase 3 begins only on an explicit author signal after the fix-vs-defer decision on the consolidation-inherited failures. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
A1. The hand-rolled in-memory inbox_messages schema never gained the two columns the production migration added, so suites that INSERT them died on SqliteError before reaching their assertion. The harness has lacked them since the columns were introduced; no test that inserts them has ever run green. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
…erivation B. PR #7 replaced the local_role role derivation with the canonical coordination-device-id derive, on the grounds that local_role is a per-device view that can disagree with the ledger. These fixtures still set only local_role and no coordination ids, so the derive returned device_id_not_in_handshake and the product refused every prepare. The fail-closed refusal is the enforcement #7 added and is left untouched; the fixtures now name the local instance, following the 2a24cdb precedent. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
A2. The four failing cases sealed rows with the inner key, tagged them seal_key_source='vmk', then unbound the inner provider and expected the read to succeed anyway. sealedQuery routes vmk rows to the inner provider only, so the rows were filtered and prepare returned MESSAGE_NOT_FOUND. No product change can make that pass without a verification bypass, and reject mode states there is none in any environment. The tests encode the pre-B-9 trusted read that "source read uses sealedQuery" deliberately removed. Three of them are about content extraction per row shape, not about key routing, so they now seal per the product's own policy (effectiveInboxRowSealKeySource returns 'ledger' for every one of these shapes) and exercise their real subject. The fourth asserted cloning a row with no canonical plaintext. Production writes NULL depackaged_json only where no plaintext exists yet (beap_qbeap_pending_main, main-process decode errors), so there is nothing to clone; it now pins the refusal rather than a body_text fallback. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
…bient mode A3. CC_06 asserted that use_coordination=false sends outbound to a relay URL. That path was retired; the queue refuses it with a typed permanent PREFLIGHT_FAILED / CONFIG_PERMANENT and never calls fetch. The test now pins the refusal, per the ruling that the refusal is the invariant worth keeping. A4. CC_05b enqueued a content-bearing message_package and discarded the result. enqueueOutboundCapsule does return a typed rejection on every path and logs it, so nothing was dropped silently and no product change is warranted; the silence was the test throwing the result away. It now asserts the enqueue. The rejection was SANDBOX_DATA_EGRESS_FORBIDDEN: the Electron mock's userData dir is shared and persists across files and runs, and roughly seventeen suites write orchestrator mode into it, so whichever suite ran last decided whether this one could send. That is the mechanism behind the false green. The suite now pins host mode for its own duration and restores what it found. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
All 35 pinned identities green in isolation and in the full workspace, 0 new failures, 201 -> 166. No product code changed: every fix is in tests or the harness. Records what the package deliberately did not do. A2 needed no product change: the four cases asserted the pre-B-9 trusted read that "source read uses sealedQuery" removed. A4's authorized typed rejection already exists - enqueueOutboundCapsule returns a typed result on every path and logs it, so the never-fails-silently condition never triggered; the silence was the test discarding it. Names the root cause behind the false green: the Electron mock shares one userData dir whose orchestrator-mode.json survives across files and runs, and two suites write mode 'sandbox' without restoring it. Correcting the count in c5447a4, which said roughly seventeen; it is two. Adds the error-taxonomy item for the MESSAGE_NOT_FOUND conflation with a remediation proposal, and the seal-policy divergence between verificationKeySourcesForInboxRow and sealedQuery. Both reported, neither implemented. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Remediation accepted; the consolidation-inherited item is closed. What came out of it now carries its own names and schedules, so none of it rides along unowned: - test-isolation bidirectional risk: root cause attached, mock untouched until a dedicated decision. - inbox-read error taxonomy: adopted in principle, scheduled with Phase 4 as an authorized bounded item - same never-fails-silently family as the Phase-4 status surface. - seal-key-source policy unification: bounded diagnosis at Phase-4 entry, implementation only on approval. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Interface reference against WRC-Registry-API-Contract v1.0 @20794bff. No WRC service code; everything below the transport interface is swappable and an unconfigured deployment refuses with not_configured rather than looking like a registry that answered "no such publisher". 3A hardened client: https only, redirect refused rather than followed, TLS 1.2 floor with no way to weaken rejectUnauthorized, total deadline, streaming size cap, and an SSRF guard at the resolved ADDRESS via a custom lookup - checking the hostname would fall to DNS rebinding. Placed in main rather than a shared package because both shared packages are imported by the MV3 extension and the guards need node:dns/net/https; reported as a deliberate deviation. 3B orders the chain so the ordering is the security property: registry claim, then DNS + manifest, then the part cross-check, then head, envelope, EVP. No branch reaches a trusted presentation having skipped a leg, and the registry is consulted last for key material so it can only ever be caught diverging. 3C folds r7 alignment through the CPR ratchet, so this stage can tighten a verdict and structurally cannot loosen one, and activates discovery_record. 3D persists the epoch floor separately from the record cache: evicting a publisher must not reopen a rollback window. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
…ment
Contract-faithful double signs real Ed25519 material, builds a real Merkle
tree and a real epoch sequence, so verification is proven against signatures
rather than stubs that return true.
Two findings from writing them, both fixed here:
- parseOutboundUrl let a bracketed IPv6 literal through. URL.hostname keeps the
brackets and isIP('[::1]') is 0, so https://[::1]/ passed the literal-address
check and was caught only later by the lookup guard. Now unwrapped.
- The signature tamper helper flipped the LAST base64url character, which for a
64-byte signature carries two meaningful bits and four discarded padding
bits. Several tampers decoded to identical bytes, so whether a test caught a
forged signature depended on which random key was generated. Tampering the
first character now, with an assertion that the decoded bytes actually
differ; the matrix was run five times over fresh keys to confirm.
Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Records the deliberate 3A placement deviation and its reason, the two findings from writing the tests (bracketed IPv6 bypassing the URL guard; a signature tamper that decoded to identical bytes and made forgery detection depend on the random key), the integration-pending live legs, and one contract gap: the contract defines DelegationRecord and a write endpoint for it but no read path, so a client that has never seen a delegation cannot verify a head signed by a delegated catalog key. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Amendment as an additive delta beside the byte-exact v1.0 author drop, CRLF preserved, following the Order v1.0 + Delta v1.1 precedent in this repo. The head already arrives on every resolve, so embedding the delegation makes verification deterministic and immune to selective blocking of a side-fetch. resolveSigningKey therefore takes a single headDelegation rather than a list: a collection-shaped field is an invitation to satisfy a delegated head from somewhere other than the head, which is the property the amendment buys. Each way of getting it wrong is its own reason - missing record, kid mismatch, not rooted, out of window, bad signature - because a status surface that collapses them cannot tell an operator what happened. Sub-delegation is unrepresentable rather than refused: authority is catalog-signing-only, so a record naming anything but the DNS-pinned root as root_kid is rejected before its signature is considered. A malformed embedded record fails the decode instead of degrading to null, which would have turned a broken chain into "root-signed head" and handed the verifier the wrong question. The delegations endpoint is audit-only and guarded as such: source-walking asserts the verification modules never reference it, and a deliberately broken audit endpoint leaves verification green. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Addendum records the v1.1 amendment, the client change and its rationale, the eight negative cases with their distinct reasons, and the clean do-not-regress pair (166=166, +16 passing). Codifies the two rulings this phase produced: node-only guards never live in browser-reachable packages, and every negative-test mutation helper must assert its own semantic effect - a helper whose effect depends on fixture randomness is invalid by construction. Closes the contract gap as ruled, and schedules epoch-floor hardening into the pre-Phase-4 block with the two existing items; bound-origin-set plurality goes with 5A instead. Narrowing worth noting: delegation fetch is no longer integration-pending for verification, because verification never fetches. Only the live audit round-trip against GET /delegations remains. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
MESSAGE_NOT_FOUND covered three states - row absent, row present but unverifiable, row present with no canonical plaintext - so neither an operator nor a log could tell a missing message from a tampered one from a locked vault. The Phase-2 diagnosis needed a probe to find out which had happened. MESSAGE_NOT_FOUND now means the row is genuinely absent, and its copy drops the "or could not be verified" hedge. SOURCE_UNVERIFIABLE and SOURCE_NO_CANONICAL_CONTENT carry the other two, following the pattern the vault-unavailability branches already use. Absence of content outranks unverifiability: you cannot verify what is not there, and "no decrypted content yet" is the actionable thing to say. The classification reads the canonical column without returning it - presence only, never content, since this sits behind the gate it is classifying. Renderer copy and the prepare-failure detail list updated; b9 assertions split by the state each case actually creates. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
A floor a deletable userData file can reset is decorative: anyone able to remove the file could let a publisher replay an older, correctly signed CatalogHead. Schema v77 adds wrc_publisher_epoch_floor; the resolved-record store keeps only cache and no longer owns the floor. Monotonicity is a property of the statement rather than of a read-then-write a caller could race or skip, and the store exposes exactly two operations, read and raise - there is no set, clear, or delete to call. A legacy cache file's epoch_floor key is ignored on load, since reading it back would reintroduce the reset path this removes. When the DB is unavailable the fallback is an in-process floor. That is strictly safer than what it replaces: empty for the process, but unlowerable and never written anywhere a deletion could reach. Guard test deletes the cache and serves an older correctly signed head; it still fails with head_epoch_rollback. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Six production call sites; one uses the policy. Sites 2-4 are the extension's sealed inbox read, which routes from the row tag alone. Consequence: a legacy inner-sealed non-confidential row is visible in the Electron inbox - which tries the policy list, succeeds on inner, and re-seals forward - and absent from the extension inbox, which maps vmk to inner only and filters the row while emitting tamper telemetry for a row that is not tampered. Wider than the Phase-3 report framed it, which named clone-prepare rather than the extension list. Authoritative: the policy decides which providers may be TRIED; the row tag records what the row WAS sealed with. The tag cannot govern policy because it is exactly the field that is stale on the legacy rows the try-list exists for. Recommends teaching sealedQuery an optional key-source list, keeping today's default, then migrating the extension list first. No code changed; the fix is gated on approval. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
The Phase-3 assertion proved the floor outlived record eviction by reading it back out of the same cache persistence - which is precisely the property item (iii) removed. Sharing the floor store between the two record stores asserts the new reason: the floor is its own store, so neither eviction nor deleting the cache file reopens a rollback window. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Records (i) and (iii) as implemented, (ii) as diagnosis with the fix gated on approval, and the clean do-not-regress pair. Also records the one correction the identity comparison caught: a Phase-3 test proved the floor survived eviction by reading it back out of the cache persistence, which is the property (iii) removes. A count-only check would have shown 167 vs 166 and invited a shrug. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
4A composes three orthogonal layers instead of merging them. The two "suspended" statements are different parties talking about different objects - the publisher withdrawing its entry, and the platform suspending the object - so they get distinct copy and never collapse into one word on one screen. Admission is conjunctive and fail-closed; the headline is the failing leg closest to the object; every failing leg stays visible, because never-fails-silently means telling the operator all of what is wrong rather than the first thing. Compromised is revoked plus the unsuppressible alert flag, and superseded surfaces its successor without offering it. 4B carries resolution output on the offer - all of it from the verified chain, none from carrier bytes - and extends the preview hash to cover the entry, the resolution mode, and per the O2 extension the evp_ref and value_statement. That last one is the substance: consent is pinned to the hash, so if two offers showing different signed value promises hashed identically, the consent record would not bind what the operator actually agreed to. O6 re-checks status at consent time, since a publisher can withdraw inside the window between staging and consent. The 7-day offer timeout stays UI staleness and is not this gate. CREATE TABLE IF NOT EXISTS does nothing to an existing database, so the new columns are added explicitly and idempotently. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
The Phase-4 acceptance guard pins every reader of the staged-offer table to connectOfferStaging, so no second module can build an alternate, unsuppressed listing. I had put the O6 gate in ipc.ts, which violated exactly that. The guard was right; the gate moved to the module that owns the table. Worth noting the comment I first wrote to explain the move named the table literally, which tripped the same scanner - the invariant is enforced by substring, so prose about it has to avoid the string. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
Records the A6 composition and why the three layers stay separate, the preview-hash coverage extension and why it is the substance of 4B rather than paperwork, the O6 gate, and the clean do-not-regress pair. Also records that an existing acceptance guard caught a real placement error: the O6 gate belonged in the module that owns the staged-offer table, not in ipc.ts. The guard was right. Co-authored-by: focusedbrain <focusedbrain@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Permanent development branch for the WR-Code Email E2E slice. Single-branch workflow; phase boundaries marked by reports and
phase-<N>-completetags.phase-2-complete@a310cb96b46680a4phase-3-complete@1c1cb3efd8ac21b156dbf7c2phase-4-complete@0a7ca3aePhase 3 + 3G — resolution infrastructure
Hardened outbound client (SSRF guard on the resolved address, redirects refused, TLS floor, streaming size cap); resolution client whose operation order is the security property (registry claim → DNS → manifest → part cross-check → head → envelope → EVP); r7 alignment folded through the CPR ratchet. Contract v1.1 moved the delegation into the head, so verification completes from the DNS-pinned root plus the embedded record alone — no fetch in the verification path.
Pre-Phase-4 block
MESSAGE_NOT_FOUNDnarrowed to a genuinely absent row;SOURCE_UNVERIFIABLEandSOURCE_NO_CANONICAL_CONTENTadded.Phase 4 — entry lifecycle and offer schema
A6 composes publisher-part status, publisher-signed entry status, and platform suspension without merging them: conjunctive fail-closed admission, headline by closeness to the object, every failing leg visible, distinct copy per layer. 4B carries resolution output on the offer and extends the preview hash to cover the entry, resolution mode,
evp_refandvalue_statement, so consent is pinned to the value promise actually shown. O6 re-checks status at consent time.An existing acceptance guard caught a real placement error during this phase — the O6 gate belonged in the module that owns the staged-offer table, not in
ipc.ts.Details:
code/docs/analysis/wr-code-email-e2e/andcode/docs/spec/.Notes
mainremains for author-designated document drops only.