Skip to content

feat(auth): Authlib OAuth 2.1 core + S154 P0 repair — dark by default - #346

Draft
creator35lwb-web wants to merge 16 commits into
mainfrom
feat/registration-auth-gate
Draft

feat(auth): Authlib OAuth 2.1 core + S154 P0 repair — dark by default#346
creator35lwb-web wants to merge 16 commits into
mainfrom
feat/registration-auth-gate

Conversation

@creator35lwb-web

@creator35lwb-web creator35lwb-web commented Aug 30, 2026

Copy link
Copy Markdown
Owner

What this is

The registration-auth lane, now an Authlib-based OAuth 2.1 authorization server per D-ALTON-2026-09-01-AUTHLIB-REGISTRATION-AUTH, replacing the hand-written state machine. Everything ships dark behind two independent default-off gates.

Exact head: 59424c57406cbe536cf03565edef310024f7167f (was 84fd926; advanced by the CS round-6 erasure-seal / receipt-truth repair below — three commits: cedc24b52dd282615525f9c4f7460b31db500568 on 84fd9265701a38766bb6d3d8aa3cc3a0de77745b, then 59424c57406cbe536cf03565edef310024f7167f after RNA's own adversarial lens refuted part of it, then this evidence-integrity child after a second lens found false claims in the regression file's own comments; the deviation from the one-SHA instruction is disclosed in the round-6 section. 84fd9265701a38766bb6d3d8aa3cc3a0de77745b is one child of 68daa5c189d758bd1da2edb63cf42b25140ae2d4, itself one child of 2c43cd532790a6bf5d5dee184cd3898db2ff8dea, itself one child of fecc67cfe5712d123895db1decac02cc91eedecd; the two S159 child commits of d125bce47c521dee91ebc2ab5fb2038a243e702a are explained under Two SHAs, with cause in the round-2 section, and d125bce is the T S156 gate-truth repair on 7a0c462). No merge / staging / deployment / credential issuance / policy publication / notice / activation is requested by this SHA. Production remains v0.5.62, serving revision 00504-c8d (image digest unchanged from 00503-rxh; config-only hygiene 2026-09-01). Composition: this branch contains PR #345 (c8d1e8ca) as byte-identical ancestors per Alton's one-release decision.

Architecture (Authlib as protocol core, VerifiMind owns state)

oauth/authlib_server.py runs Authlib's AuthorizationServer + AuthorizationCodeGrant + RefreshTokenGrant behind a thin async Starlette adapter (VMOAuth2Request/VMPayload preserve duplicate params). Authlib owns standards parsing, grant/error validation, redirect/client binding, PKCE, bearer structure, DCR parsing, metadata. PKCE is constrained to S256 (S256OnlyCodeChallenge — stock Authlib permits plain). Public clients only (token_endpoint_auth_method=none), grants authorization_code+refresh_token, scope mcp. Tokens stay opaque, hash-only at rest (vmat/vmrt/vmpat.{id}.{secret}), minted through the validated grant path.

P0 findings → fixes (each with a discriminating regression)

P0 Fix Test
P0-1 cache secret-bypass stores.validate_bearer cache keyed on credential_digest (full credential), not token_id; secret verified on miss; expiry re-checked; revoke clears cache test_oauth_core.py::TestCacheSecretBypass (wrong secret rejected before AND after warm-up)
P0-2 UUID authorizes disclosure/deletion dup-email returns no UUID / no opt-out URL (both register paths); dashboard + opt-out require _authenticated_subject == uuid UNCONDITIONALLY (flag-gating left the default dark posture open) test_v0513/test_registration dup-email non-disclosure; boundary owner-scope tests
P0-3 non-atomic grant state minting bound INTO the transactional code-claim (save_tokenclaim_code_and_mint_tokens); refresh rotation transactional; OTP attempt-cap transactional; optimistic-concurrency + retry TestConcurrency (exactly one winner via barrier), wrong-PKCE-doesn't-consume
P0-4 issuer/audience/resource/scope absent tokens carry issuer+audience+scope; VMBearerValidator/authenticate_bearer re-check vs env; 401 wrong issuer/audience, 403 insufficient scope TestBearerAuthorization, boundary 403 test
P0-5 PAT/revocation inconsistent one bearer path accepts ACCESS+PAT, rejects REFRESH; revoking any credential tombstones the whole grant_id family; refresh-reuse revokes family TestBearerKinds, TestRefresh replay-revokes-family
P0-6 SMTP STARTTLS unauthenticated starttls(context=ssl.create_default_context()); fail-closed on send error boundary/mailer tests
P0-7 dark ≠ dark issuance independent OAUTH_ISSUANCE_ENABLED (default off) blocks EVERY mutating OAuth/DCR/PAT/ceremony path; metadata stays readable dark-mode zero-mutation tests
P0-8 staging cannot isolate env-bound issuer/resource/origin/collection-namespace/telemetry (oauth/config.py); stage/prod tokens mutually rejected by audience; mail recipient allowlist TestStageProdIsolation
P0-9 DCR/redirect/consent/limits exact redirect membership + no fragment/userinfo; agree-checkbox required; duplicate-param rejection; last-hop XFF; global + per-action + unused-client-expiry limits DCR/redirect/consent tests
P0-10 ceremony/attribution residuals verified-mailbox ceremony (consent only after OTP); peppered OTP bound to session+purpose; HMAC-pseudonymous telemetry subject; env actor-class ceremony + gate tests

WP-A dependency authority + third-party-license evidence

Authlib==1.8.0 (BSD-3-Clause) + joserfc==1.7.5 (≥1.7.3 issuer-validation fix, GHSA-r74j-q665-7rpj) + cryptography==46.0.1 pinned in both pyproject.toml and requirements.txt; parity tests extended (TestAuthlibDependencyAuthority).

THIRD_PARTY_NOTICES.md now reproduces each pinned artifact's license file verbatim (five files: Authlib LICENSE; joserfc LICENSE; cryptography LICENSE, LICENSE.BSD, LICENSE.APACHE) with SHA-256 receipts binding each block to the exact PyPI release artifact (wheel + sdist digests verified against PyPI's published digests; license bytes verified identical across every artifact and the installed dist-info/licenses/ copy; Authlib tag v1.8.01a86748b). Publisher provenance verified via PyPI's integrity API: every listed Authlib/joserfc/cryptography artifact carries a Trusted Publishing attestation bundle from its GitHub publisher (authlib/authlib pypi.yml, authlib/joserfc pypi.yml, pyca/cryptography pypi-publish.yml) — the earlier notice's unverified "(Trusted Publishing provenance)" parenthetical is replaced by this checked statement. tests/unit/test_third_party_notices.py re-verifies every block byte-for-byte against the installed pinned distribution in CI. No license text paraphrased; no legal-certification claim.

Internal adversarial pass (run before 2f44433)

Four independent attacker-position reviews were run against the implementation; the token core held (digest-keyed cache, transactional claim, PKCE matrix, OTP cap, refresh rotation + family revocation, STARTTLS), and the following real bypasses were found and repaired in that SHA, each with a discriminating regression:

Finding Repair
PAT minted into a fresh family — a stolen access token became a silent 180-day credential surviving revocation PAT bound to parent_grant_id; revoke_grant_family sweeps descendants
/oauth/pat called the store directly, skipping issuer/audience/scope routed through authenticate_bearer; ACCESS-only (no PAT chaining)
Cross-instance cache window + query-then-update race left live credentials after a revoke revocation tombstones consulted at validation time (incl. cache hits)
Truthy issuer/audience guard admitted tokens missing those fields in any environment absence now DENIES
Unverified /register planted the email→UUID record the verified ceremony would adopt (pre-registration subject hijack) + account-existence oracle legacy paths mark records unverified and return no identifier (uniform response); ceremony upgrades only on mailbox proof
To: list smuggled the OTP past the recipient allowlist strict single-address enforcement before any backend
Bare-UUID opt-out/dashboard open in the default dark posture ownership check made unconditional
Staging could read/create/tombstone production accounts; a staging revision missing one env var became production account collections namespaced; non-production K_SERVICE must declare its environment; PRM never falls back to production
Leftmost-XFF let one header rotate the rate-limit key last-hop XFF
Consent page framable (UI redress); array JSON-RPC params crashed the boundary (500) X-Frame-Options/frame-ancestors/no-store; params type-guard

Independent CS review → BOUNDED-HOLD → repaired (7a0c462)

The independent CS safe-to-stage review (Antigravity) verified the crypto core, tombstoning, PKCE S256-only, and OCC token issuance HOLD, and returned BOUNDED-HOLD with four findings — all repaired at 7a0c462, each with a discriminating regression (tests/unit/test_cs_review_findings.py):

CS finding Repair
F1 legacy /register + /early-adopters/register bypass the dark-issuance gate (public Firestore write while dark) issuance guard on both handlers → dark 503, zero write
F2 the oracle fix returned uuid="" for every registration, orphaning the no-email anonymous user branch on email: absent → return UUID (no oracle/hijack surface); present → withhold
F3 the ceremony adopted an unverified record's attacker-injected display_name/feedback on mailbox proof wipe all caller-injectable fields when upgrading unverified → verified
F4 a second hardcoded production PRM in registration_gate._denial_payload (staging → production auth server lockout) env-bound from config.current_environment()

Prospective correction of the earlier evidence line. This body previously stated "CI 10/10 green at 7a0c462". That statement was never bound to the completed check-suite: the CodeQL check from the github-advanced-security app — the code-scanning results check GitHub creates when the CodeQL Analysis workflow job uploads its SARIF, evaluating new alerts in code changed by this pull request — completed at 2026-09-01T15:15:26Z with FAILURE: "10 new alerts including 2 errors" — two error-level py/uninitialized-local-variable annotations (error, hint at registration_gate.py:206-207) plus eight notes. Final check-suite at 7a0c462: 10 check-runs, 9 success / 1 failure, mergeable_state=unstable. (Two runs share the name Bandit SAST Analysis: the real scan from security-scan.yml, and the synthetic instant-pass job of the same name from docs-ci-bypass.yml, which triggers because this PR also touches **.md files — it runs only verify_public_docs_contract.py. The ruleset's sole required context is that name; that a synthetic job can satisfy it on a PR that also changes mcp-server/** is a pre-existing gate-design observation, recorded here and not changed.) T S156 caught this on a fresh exact-head read. (The repository ruleset formally requires only Bandit SAST Analysis; T's review contract requires every current check green, and the red results check is what CS re-review, T rebind, staging, and merge are held on.) 7a0c462 was therefore not a green review target; the merged private records that repeated the 10/10 claim are preserved as history and corrected prospectively in the Hub.

T S156 gate-truth repair (head d125bce47c521dee91ebc2ab5fb2038a243e702a)

The finding. _denial_payload built the two denial payloads with two independent if statements (if reason == DENIAL_CROSS_SUBJECT: … then if reason != DENIAL_CROSS_SUBJECT: …). Logical complements to a reader — but not provably exhaustive to static analysis, so CodeQL flagged error/hint as potentially uninitialized at the point of use. T bounded it as control-flow/static-analysis ambiguity, not a demonstrated bypass; the required gate was nevertheless red.

The repair (one commit, +10/−2 in the module). The decision is now one exhaustive if/else; register_url, prm_url = _env_urls() is hoisted above it (pure resolver, already called exactly once on every path, cannot raise). Neither payload changed — the strings, key order, error_code mapping, env-bound register_url/resource_metadata, and the prior fall-through semantics (any non-cross-subject reason → authentication-required) are byte-identical. No authentication, authorization, routing, provider, billing, pricing, release-identity, or activation semantics changed.

Discriminating tests (tests/unit/test_gate_truth_denial_payload.py, 16 tests): exact-string pins for both payloads (incl. the no-PRM fallback clause); every admitted reason — and an unknown reason — always binds error and recovery_hint; environment binding for both payloads (CS F4 extended to the cross-subject payload: staging never points at production); the real _env_urls exception path (staging with no resolvable origin → no PRM anywhere in the payload — see the class sweep below); a structural AST pin that exactly ONE if binds error/hint, the if/elif chain ends in a real terminal else, and every arm definitely binds both names (definite-assignment semantics — a binding under a nested non-exhaustive if does not count, so the pin covers the nested shape on the 3.11 Run Tests job); and an independent CPython oracle — error/hint compile to LOAD_FAST (proven bound), not LOAD_FAST_CHECK (3.12+; 7a0c462 compiled them with LOAD_FAST_CHECK). The structural test fails on the 7a0c462 source (binding_ifs=2, has_else=[False, False]) and passes on this head — proven by running the same AST logic against both sources.

Independent adversarial pass before push (six read-only lenses, each trying to refute one claim): payload byte-equivalence held over a 1,656-case differential (9 reasons × 8 tool names × 23 _env_urls outcomes incl. real oauth.config paths; 0 mismatches; _env_urls called exactly once per call); CodeQL satisfiability derived from the query's own SSA/taint source (UninitializedLocal.ql/Undefined.qll) plus the CPython oracle, and the full security-and-quality suite reasoned against the rewritten lines with no new error-level query firing; license fidelity, eight-note disposition, test discrimination and contract scope each held. Six minors surfaced across the lenses and all were taken — every one a test or prose precision fix, none cleanup: the structural pin accepted an if/elif chain with no terminal else and, separately, a binding under a nested non-exhaustive if (now definite-assignment semantics); no test exercised the real _env_urls fallback path; the notice test collapsed duplicate blocks to the last copy; the notice's scope sentence overclaimed ("every pinned component" → the three OAuth pins) and one paragraph tracked license clauses in prose (replaced by a plain endorsement disclaimer). Follow-up recorded, not made (workflow change): the LOAD_FAST_CHECK oracle only executes on 3.12+, while the only pytest job runs 3.11 — running the gate-truth file inside the built 3.12 image in the parity job would put the oracle on a required gate.

The eight notice-level notes — inspected, dispositioned, left untouched (T: repair only a note that blocks the gate or exposes a real defect; no general cleanup):

# Note Disposition (evidence)
1 mcp_auth_boundary.py:34 unused global _PRM_URL Dead: zero references (its only occurrence is the definition); _prm_url() returns the env-bound PRM or None and never consults it. The re-wire hazard is already guarded by test_auth_boundary.py::test_misconfigured_env_never_challenges_toward_production. Not a defect; left untouched (cleanup candidate).
2–3 authlib_server.py:12 List, :17 InvalidClientError unused imports Zero Load references; no string annotations, __all__, re-exports, or getattr-by-name; sibling names on the same import statement are used, so no import-time side effect changes. Left untouched.
4–5 stores.py:69registration.py:562 cyclic import Both edges are function-local deferred imports (_db(); process_optout under if matched:); the module-level import graph is acyclic and neither module calls the deferred edge at import time. Import-order probe in fresh subprocesses (stores-first, registration-first, verifimind_mcp.server, http_server, oauth.authlib_server, oauth.endpoints, middleware.mcp_auth_boundary) all import cleanly; both alerts predate the Authlib head. Left untouched.
6–8 oauth_fakes.py:13 Any, test_oauth_core.py:13 re, :22 config unused imports (test files) Zero references (the only other re token is inside a comment; config shares its import statement with used names). Left untouched.

Gate-neutrality receipt: at head 2f44433 the same app check concluded success with "8 new alerts — 8 notes" (and 5 notes at 6ab7e32/5f9b83d); it turned to failure only at 7a0c462 when the two error-level alerts appeared. Notes do not fail the results check, and the ruleset counts none of them.

Class sweep beyond the eight (found by the adversarial pass, not by CodeQL): registration_gate.py:49 PRM_URL is the same dead-production-constant class as _PRM_URL, but no test exercised the real _env_urls() exception path — a future re-wire (return REGISTER_URL, PRM_URL) would have passed every test. One discriminating regression added (test_misconfigured_staging_never_advertises_a_production_prm, both reasons): staging with no resolvable origin → the real path → resource_metadata absent and no PRM path anywhere in the payload; the documented fallback register_url is pinned explicitly. PRM_URL itself is left untouched (cleanup, not a defect); the _env_urls docstring's plural "module constants … fallback" wording is noted for a later bounded pass.

Local gates at this head (imports bound to the exact tree — verifimind_mcp.__file__ printed from the clone; the global editable install points at a stale clone and must not be trusted): unit 1,289 passed / 3 skipped / 1 failed (the failure is the documented dev-env test_exact_pins_are_the_versions_actually_installed: installed fastapi/fastmcp/starlette/uvicorn ≠ pins while Authlib/joserfc/cryptography match; CI installs the pins and is the authority), coverage 77.8%; registration 84 passed; integration 7 passed / 11 skipped — 1,380 passed across CI's three invocations, +22 over 7a0c462 (16 gate-truth + 6 notice tests). Bandit with CI's flags (-r src/ -ll -ii --skip B101): no issues. Safety and SonarCloud run remotely.

Remote checks at d125bce47c521dee91ebc2ab5fb2038a243e702a (read from the commit's check-suite after every check — including the asynchronous GitHub Advanced Security app check — had completed):

Check (unique context) Producer Conclusion Completed (UTC)
Bandit SAST Analysis (posted twice) Security Scan workflow, run 33643868406 — the real scan, advisory (|| true); and Docs Contract and CI Bypass, run 33643868407 — synthetic instant-pass, triggered by **.md success / success 14:43:22 / 14:43:15
Safety Dependency Check Security Scan — advisory (|| true) success 14:43:27
Security Scan Test Suite, run 33643868480 success 14:43:26
Production Image Dependency Parity Test Suite — builds the Python 3.12 image and asserts the pins inside it success 14:43:26
Run Tests Test Suite — Python 3.11: unit + registration + integration success 14:44:35
Server Health Check Test Suite success 14:44:41
SonarCloud Code Analysis sonarqubecloud success 14:44:17
CodeQL Analysis Security Scan — scan + SARIF upload (security-and-quality) success 14:44:29
CodeQL github-advanced-security — the code-scanning results check on new alerts in changed code success 14:44:24

10 check-runs / 9 unique contexts, non-success 0; PR mergeStateStatus: CLEAN (was UNSTABLE). Read at 14:44:57Z after every expected name existed and every run had completed.

CodeQL (github-advanced-security) annotations at this head: success — title "8 new alerts", summary "8 notes": exactly the eight dispositioned notes above (annotations 8/8 match), 0 error-level. Open code-scanning alerts for pr=346 by rule: 2× py/cyclic-import, 1× py/unused-global-variable, 5× py/unused-import, py/uninitialized-local-variable (alerts #237/#238 closed). Code-scanning analysis on refs/pull/346/merge: 2026-09-02T14:44:21Z, merge sha 28983337b, results=8 (was 10 at 7a0c462's merge 7d75947a4).

CS round 2 (2026-09-02) → bounded availability repair (head fecc67cfe5712d123895db1decac02cc91eedecd)

The independent CS safe-to-stage RE-review of d125bce (Antigravity, static, bound to that head) returned NOT SAFE TO STAGE on three availability-class findings while verifying that the security core — tombstone/cache fail-closed, duplicate-parameter first/last split, email normalization, strict STARTTLS — HOLDS: the second independent round in which the core held. RNA verified each finding against the code and the live ingress (private record 20260903_RNA_s158_cs_rereview_verified_findings.md); Alton activated the bounded repair; this head is the second of two child commits of d125bced021f8c (the three repairs, 7 paths) and fecc67cfe5712d123895db1decac02cc91eedecd (a rename forced by a CodeQL false positive, 5 paths) — see Two SHAs, with cause below.

CS finding Verification Repair (this head) Regression — tests/unit/test_cs_round2_availability.py (49 tests; the discriminators FAIL on d125bce: 33 failed / 16 passed against the old modules)
F1 OOM: the execution-mode body peek kept buffering past _MAX_PEEK_BODY Confirmed. The S155 truncation fix ("never stop reading") removed the 1 MiB bound; no test pinned either behaviour; reachable by any anonymous caller once the gate is on and AUTH_BOUNDARY_MODE=execution (dark today). The F1 lens also found a pre-existing S155 defect in the same function: after the buffered messages the replay returned an empty http.request sentinel forever instead of awaiting the live receive; the MCP streamable-HTTP path hands that receive to the SSE disconnect listener, which loops on it, so any anonymous non-gated initialize in execution mode froze the event loop (whole-instance hang, reproduced on the real app; unreachable today — gate dark, default connection mode passes the live receive) Bounded buffer + spliced replay: reads stop once the cap is crossed, the request is marked protected, and the replay yields the buffered messages then always hands over to the live receive (which also removes the sentinel hang). Oversized → protected; authenticated large bodies arrive intact (the S155 concern); anonymous oversized get the 401 with the remainder unread (safe under the pinned uvicorn==0.52.4: remainder discarded, keep-alive preserved); small bodies and mid-read disconnects decide and replay exactly as before — only the post-body tail changes, from a sentinel to the server's real stream 4 MiB streamed in 64 KiB chunks (derived from the cap so the bound cannot go vacuous): anonymous → 401 after ≤17 reads (d125bce: 64); authenticated → all 4 MiB delivered downstream with ≤17 reads buffered before the app is entered (d125bce: 64); small-body replay unchanged; disconnect mid-read still protected; SSE-listener regression: after the body the next receive() is the live one and returns the disconnect (d125bce: sentinel forever → spin)
F3 Firestore transaction failures escaped as HTTP 500 Confirmed, mechanism pinned from the installed library (2.21.0 and 2.29.0): @firestore.transactional retries Aborted ×5 then raises a plain ValueError("Failed to commit transaction in 5 attempts.") from last_exc; and when the very first BeginTransaction RPC fails, the wrapper's rollback path raises ValueError("…cannot be rolled back") whose only link to the backend error is __context__. Neither is a google.api_core type, so _backend_failure never mapped them; non-retryable RPC failures escaped directly. The first S159 repair walked __cause__ only and was refuted by the F3 adversarial lens on the begin-failure shape (end-to-end 500 through the real token handler) before any SHA was minted. The same escape class existed in every raw store write/query and in the ceremony's raw Firestore calls stores.is_backend_failure walks __cause____context__ (unless suppressed, bounded); one _guarded(op) decorator maps backend failures to StoreUnavailable on _read, run_transaction, and every raw Firestore write/query (10 sites); application signals (_RefreshReuseDetected) and programming errors pass through. Ceremony: any store outage → the honest 503 page (no more "invalid code" / "session expired" on outage; cleanup drops best-effort); _resolve_or_create_subject maps backend failures to the existing 503 Real-wrapper shapes (the library's own Transaction + transactional over a scripted GAPIC surface): begin failures ×6 incl. RetryError/RefreshError, commit-Aborted-then-retry-begin-fails, exhausted retries, failing rollback, in-transaction read failure → StoreUnavailable (d125bce and the first repair: escaped); signal passes through as the same instance; KeyError not masked; chain-walk pins (cause, context, suppressed); class sweep over 10 raw store calls (d125bce: escaped); ceremony outage → 503 for verify_code and session read (d125bce: 200 "invalid code" / 400 "expired"); subject resolution → None (d125bce: raised)
F2 last-hop X-Forwarded-For collapses per-IP limits behind a load balancer Topology-conditional — correct today by receipt (2026-09-02): verifimind.ysenseai.org is a Cloud Run domain mapping via ghs.googlehosted.com; 0 forwarding rules, 0 serverless NEGs, ingress all → the Google Front End appends the client last. Behind a Google external Application LB the last element would be the LB. The assumption was unstated in two limiters — the standing _client_ip residual New utils/client_ip.py: one resolver with an explicit INGRESS_PROXY_HOPS (default 1 = GFE-appended last element; 2 behind the external LB — and the docstring now says why too-large is worse than a collapse: on an ingress that appends fewer elements than the depth, [-depth] is caller-supplied again), shared by rate_limiter.get_client_ip and oauth/endpoints._client_ip; a chain shorter than the trust depth falls back to the direct peer, never a caller-supplied element; the startup banner discloses the configured depth. Staging must re-bind the depth to its own ingress with the same receipt before the client matrix relies on per-IP mint limits. Lens differential at depth 1 (2,046 inputs × both resolvers): byte-identical for every input with ≥1 XFF element; the only differences are two degenerate no-XFF inputs (whitespace-only X-Real-IP; empty/None peer) that now resolve to the direct peer/unknown instead of ''/None — strictly improving and unreachable on Cloud Run, where XFF is always present depth 1 → last element on both limiters (identical to d125bce); depth 2 → the client behind the LB (d125bce: the LB); short chain at depth 2 → direct peer (d125bce: the caller-supplied element); env parsing defaults and clamps; prior no-XFF fallbacks preserved per limiter; disclosure matches configuration

No authentication, authorization, routing, provider, billing, pricing, release-identity, policy, notice, or activation semantics changed. Three independent adversarial lenses (F1 ASGI/splice semantics; F3 exception taxonomy and end-to-end 503 mapping; F2 resolver equivalence + contract scope + regression discrimination) ran read-only against both trees before the commit: F1 HOLDS on every claim (27-scenario byte-identical differential; early-401-with-unread-body verified safe from the pinned uvicorn==0.52.4 source) and found the pre-existing sentinel hang, fixed by the same hand-over. F2/scope HOLDS (2,046-case differential × both resolvers × five depths; scope exact; discrimination confirmed), with two hardenings taken — a cap-derived stream bound so the F1 tests cannot go vacuous, and an honest "too large" docstring. F3 REFUTED the first repair: BeginTransaction failures surface as the rollback ValueError chained only via __context__ (500 end-to-end through the real token handler, on google-cloud-firestore 2.21.0 and 2.29.0), and the same escape class existed in every raw store write — repaired before any SHA was minted (chain walk, _guarded on eleven store entry points, ceremony 503, subject-resolution mapping) and re-verified with the lens's own instruments on the committed tree: 28-shape unit probe 0 mismatches; end-to-end probe → HTTP 503 for every begin-time failure (was 500), commit-time still 503.

Two SHAs, with cause. The activation contract asked for one new exact SHA. The set-bound check-suite read at d021f8c found the github-advanced-security CodeQL results check red: py/clear-text-logging-sensitive-data (high) on the new startup-banner line — CodeQL's sensitive-name heuristic (maybeSecret: …|(?<!un|un_|is|is_)trusted(?!_iter)|…) classifies any identifier, env-var name, or dict key containing "trusted" as a secret, so disclosing TRUSTED_PROXY_HOPS (an integer 1–8) tripped it — plus two py/empty-except notes on the best-effort session drops. Nothing was asserted green at d021f8c; the poller caught the red row before any evidence was written. Repaired at fecc67cfe5712d123895db1decac02cc91eedecd with no behaviour change: TRUSTED_PROXY_HOPSINGRESS_PROXY_HOPS (env var, function names, dict key; the reason is documented in the module) and explanatory comments inside the two except bodies. Both commits are in this PR; the head line and every receipt below bind to fecc67cfe5712d123895db1decac02cc91eedecd.

Local gates at this head (imports bound to the clone, verifimind_mcp.__file__ printed): unit 1,338 passed / 3 skipped / 1 failed (the documented dev-env test_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority; +49 over d125bce), coverage 78.7%; registration 84 passed; integration 7 passed / 11 skipped; Bandit with CI's flags (-r src/ -ll -ii --skip B101): no issues.

Remote checks at fecc67cfe5712d123895db1decac02cc91eedecd (read from the commit's check-suite after every expected name existed and every run completed, including the github-advanced-security CodeQL results check):

Check (unique context) Producer Conclusion Completed (UTC)
Bandit SAST Analysis (posted twice) Security Scan workflow, run 33725409151 — the real scan, advisory (|| true); and Docs Contract and CI Bypass, run 33725409118 — synthetic instant-pass, triggered by **.md success / success 06:54:22 / 06:54:14
Safety Dependency Check Security Scan — advisory (|| true) success 06:54:25
Security Scan Test Suite, run 33725409213 success 06:54:28
Production Image Dependency Parity Test Suite — builds the Python 3.12 image and asserts the pins inside it success 06:54:23
Run Tests Test Suite — Python 3.11: unit + registration + integration success 06:55:16
Server Health Check Test Suite success 06:55:23
SonarCloud Code Analysis sonarqubecloud success 06:54:52
CodeQL Analysis Security Scan — scan + SARIF upload (security-and-quality) success 06:55:20
CodeQL github-advanced-security — the code-scanning results check on new alerts in changed code success 06:55:17

10 check-runs / 9 unique contexts, non-success 0; PR mergeStateStatus: CLEAN. Read at 06:55:33Z on 2026-09-03 after every expected name existed and every run had completed. (The same read at d021f8c was 9 success / 1 failure — see Two SHAs, with cause.)

CodeQL (github-advanced-security) at this head: success — title "8 new alerts", summary "8 notes": exactly the eight pre-existing notes dispositioned in the T S156 section (annotations 8/8 match); 0 error-level. The py/clear-text-logging-sensitive-data error and the two py/empty-except notes reported at d021f8c are gone. Open code-scanning alerts for pr=346 by rule: 2× py/cyclic-import, 1× py/unused-global-variable, 5× py/unused-import; py/uninitialized-local-variable. Code-scanning analysis on refs/pull/346/merge: 2026-09-03T06:55:13Z, merge sha a1156a5dd, results=8 (was 11 at d021f8c's merge 5183aecbb).

CS round 3 (2026-09-05) → bounded security repair (head 2c43cd532790a6bf5d5dee184cd3898db2ff8dea)

The independent CS safe-to-stage round 3 of fecc67c (Antigravity, static, bound to that head) returned BOUNDED-HOLD / NOT SAFE TO STAGE. T S157 accepted the exact-head hold while bounding its authority (the transcript proves checkout/rev-parse but ran no repository test, runtime, deployment, or clean-worktree gate, omitted parts of the requested checklist, and cited one wrong source path), confirmed one reported blocker with exact-head probes, reclassified the second, and found a broader class the report did not surface (review .macp/reviews/20260905_T_pr346_cs_round3_bounded_hold.md; contract .macp/briefs/20260905_T_to_RNA_pr346_round3_bounded_security_repair.md). Alton activated the bounded repair; this head is one child commit of fecc67cfe5712d123895db1decac02cc91eedecd (8 source paths + the test fake + 1 new test file). The S160 adversarial lens on WP-A found a fourth, HIGH-severity defect at fecc67c that no review round had surfaced — see the last table row.

Finding Verification Repair (this head) Regression — tests/unit/test_cs_round3_security.py (44 tests; the discriminators FAIL on fecc67c: 33 failed / 11 passed against the old tree)
F1 (T Finding 1) refresh revocation not tombstone-complete Confirmed (T probe: grant_tombstone_present: True, late_refresh_accepted: True). validate_refresh checked only the record's own revoked/rotated_to; the rotation transaction never read a tombstone; revoke_grant_family writes the tombstone first and then sweeps by query, so a rotation that committed after the sweep's snapshot left a refresh descendant that validated on its own flags and could rotate forever (bearer descendants were already refused — bearer validation consults the tombstone). Classification per T: revocation correctness + bounded availability/EDoS, not a demonstrated protected-resource bypass validate_refresh denies grant / parent-grant / subject tombstones after secret verification (same coverage as bearer). rotate_refresh_tokens re-verifies the secret INSIDE the transaction, derives grant/parent/subject from the persisted old-token document, refuses any caller identity that disagrees, reads the three tombstone documents THROUGH the transaction before marking the old token or writing descendants (the read set is what makes a concurrent revocation conflict the commit), checks kind/expiry, and writes descendants under the persisted identity. Denials raise the new RefreshRejected, which save_token maps to Authlib's invalid_grant (400) — a denial, never a retryable 503 (before: a token found revoked inside the transaction surfaced as StoreUnavailable → 503). Reuse detection, token format, hash-only storage, bearer/PAT behaviour, and rate limits are unchanged. Both interleavings are closed: tombstone before commit → conflict → retry → deny; rotation commits first → descendants inherit the tombstoned grant id and every validation path refuses them valid refresh stays valid (pin); grant / parent-grant / subject tombstone → validate_refresh None (fecc67c: record returned); real token endpoint → 400 invalid_grant, zero descendant docs, old token unmarked (fecc67c: 200 + two live descendants); the descendant of a rotation that committed before the tombstone is dead on both bearer and refresh paths (fecc67c: rotated again); deterministic two-party barrier: the tombstone commits inside the rotation's pre-commit window → conflict → retry (new_transaction called exactly twice) → deny, nothing written, for grant and subject tombstones (fecc67c: 200 + descendants — the tombstone key was never in the read set); full family revocation in the window → invalid_grant (fecc67c: StoreUnavailable → 503); concurrent double-refresh → loser denied invalid_grant and the whole family revoked incl. the winner's pair, per RFC 9700 §4.14.2 (fecc67c: StoreUnavailable → 503, a denial reported as an outage); descendants keep parent_grant_id, so a parent tombstone landing AFTER the commit still kills them (fecc67c: descendants written without the link → alive); forged grant / forged subject / forged client / wrong secret / empty persisted grant-or-subject refused without writes, each bound to its stated reason (fecc67c: committed — the empty-identity case minted descendants no tombstone could reach); a store outage during rotation stays StoreUnavailable (pin); unrelated tombstones do not over-deny (pin); bearer + PAT + revoke_credential contracts pinned
Lens A (S160) — HIGH at fecc67c: token KIND bound to the caller-controlled wire prefix Found by the S160 adversarial lens, reproduced on the fecc67c tree: all token kinds share one collection and one {id}.{secret} scheme, and validate_refresh checked only the presented prefix — so an ACCESS token rewritten vmat.vmrt. resolved to its own record (same id, same secret) and the refresh grant rotated it into a 30-day refresh family: 200, two new documents, the victim's access record marked rotated_to. A stolen one-hour token upgraded itself. contain_refresh_reuse trusted the wire kind the same way. Unreachable in production today (the OAuth endpoints are dark), present at every reviewed head of this PR validate_refresh and contain_refresh_reuse now gate on the PERSISTED kind (the same gate bearer validation already had); the rotation transaction independently re-checks it re-prefixed access token → validate_refresh None, endpoint 400 invalid_grant, zero docs, victim's access unmarked and still valid (fecc67c: 200 + two docs + victim marked rotated); re-prefixed PAT → None / 400 / zero docs / PAT still valid (fecc67c: record returned; only Authlib's client check stopped the rotation); a revoked access token re-prefixed as refresh no longer drives reuse containment (fecc67c: it did)
F2 (CS "dark-posture bypass" → T: environment isolation) anonymous feedback writes while both gates are dark T reclassified. _registration_dark_response is deliberately scoped to credential/account issuance; feedback is intentionally public (T D-157-5: an absolute-dark rule is an Alton product decision, not a mechanical repair). The real blocker: a staging service sharing the GCP project wrote the production-named feedback collection (T probe: dark_feedback_status: 201, collections_written: feedback) both feedback write sites (registration-path .add and standalone submit_feedback) resolve their collection through the one environment seam BEFORE the client is opened; production keeps bare feedback and its anonymous-feedback semantics byte-for-byte; staging writes staging_feedback staging → staging_feedback only, nothing in feedback, on both sites (fecc67c: production feedback); production with both gates dark → 201, collections touched exactly ["feedback"], no account/credential/mail mutation, mailer untouched (pin); feedback is not an exempt path → the outer limiter applies (pin)
F3 (T Finding 3) fail-open environment isolation across account / lookup / tier / feedback / history Confirmed (T probes: misdeclared_staging_collection: early_adopters; persisted: True + collections_written: ea_registrations from a misdeclared staging; production_record_seen_from_staging: True, staging_record_seen_from_staging: False). registration.account_collection caught every exception from current_environment() and returned the bare production name; registration_lookup, rate_limiter._resolve_uuid_tier, both feedback sites, and trinity_history (both directions) used raw production names the broad except is gone: EnvironmentMisconfigured PROPAGATES from account_collection, and every user-data reader/writer — register_early_adopter, register_user, get_ea_status, process_optout, submit_feedback, registration_lookup.resolve_registration, rate_limiter._resolve_uuid_tier, trinity_history read + write — resolves its collection through that seam BEFORE constructing a client or touching Firestore. Explicit fail-closed answers: a new app-level EnvironmentMisconfigured → 503 service_misconfigured handler in http_server.py (Retry-After, no-store, names no environment detail, states that nothing was stored) for /register, /early-adopters/register, /early-adopters/feedback, /whoami, /early-adopters/status/{uuid}; resolve_registrationUNAVAILABLE; tier → scholar (never elevated); opt-out → processed=false 503; Trinity history → refused with no client. Production and local development keep bare names (OAuthEnvironment.account_collection prefixes only a declared staging); the quarantined coordination store is untouched (excluded by T's contract) misdeclared staging: resolver raises instead of falling back; lookup UNAVAILABLE with zero reads; tier scholar with zero reads; feedback 503 with zero writes; /register 503, no persisted=true, zero writes; /early-adopters/register 503, zero writes; /whoami + status 503, zero reads; opt-out processed=false, account untouched, zero I/O; history read [] / write refused with zero I/O — every one FAILS on fecc67c (production reads/writes observed; 2xx / persisted=true returned). Declared staging: lookup cannot see the production record and sees its own (source stays the logical store name); tier consults only staging_early_adopters; feedback on both sites; history in both directions; account status reads only staging (pin). Production + development: bare names for all four bases; staging prefixed (pin)

No authentication-gate activation, routing, provider, billing, pricing, release-identity, policy, notice, merge, staging, or deployment semantics changed. Four independent adversarial lenses (real-Firestore transaction semantics; environment-isolation completeness sweep; regression-vacuity audit; production-drift + analyzer exposure) ran read-only against the tree before the commit: four background lens agents were terminated by an API session limit before reporting; two (real-Firestore transaction semantics; regression vacuity) were re-spawned and completed, and two (isolation-completeness sweep; production-drift/analyzer exposure) were executed inline by RNA and are disclosed as non-independent. Lens A HOLDS the library claims (installed client 2.21.0, wrapper diffed byte-identical against 2.30.0: the transactional wrapper re-raises the same exception after rollback; the read is sent inside the transaction; Authlib renders the denial as 400 with nothing persisted) and the race claim by invariance (the outcome is the same whether the server aborts the reader, blocks the writer, or ignored the absent-key read) — and REFUTED four things before the SHA: the HIGH re-prefix escalation (its own row above), the reuse-in-window 503, the dropped parent link, and the empty-identity mint; a non-numeric expires_at → 500 is recorded as a class-wide pre-existing residual. Lens C: no test passes for the wrong reason; the barrier proofs are sound within the disclosed optimistic model; five hardenings taken (the fake now refuses read-after-write like the real client; the recording fake counts transactional I/O; production-caller pins; an outage-stays-outage pin; reason-bound denials plus an unrelated-tombstone over-deny guard) and one receipt corrected (at fecc67c the opt-out de-identified the production account through the fallback name and then answered "unavailable" — a silent partial mutation, not a processed=true). Inline sweep: every Firestore access point in mcp-server/ is namespaced or in the excluded coordination store; Starlette's exception-class routing and the wrapper's re-raise verified from the installed sources; no new logged identifiers, empty excepts, or unused imports.

Local gates at this head (imports bound to the clone, verifimind_mcp.__file__ printed): unit 1,382 passed / 3 skipped / 1 failed (the one failure is the documented dev-env test_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority), registration 84 passed, integration 7 passed / 11 skipped; Bandit with CI's flags (-r src/ -ll -ii --skip B101): no issues. Old-head discrimination: the same test file against a sparse worktree at fecc67c (imports bound to the worktree, rev-parse printed): 33 failed / 11 passed — every "fecc67c:" test fails for its stated reason (log bound in the S160 record), every "pin:" test passes.

Remote checks at 2c43cd532790a6bf5d5dee184cd3898db2ff8dea (read from the commit's check-suite after every expected name existed and every run completed, including the github-advanced-security CodeQL results check):

Check (unique context) Producer Conclusion Completed (UTC)
Bandit SAST Analysis (posted twice) Security Scan workflow, run 33949772734 — the real scan, advisory (|| true); and Docs Contract and CI Bypass, run 33949772730 — synthetic instant-pass success / success 06:25:50 / 06:25:39
Safety Dependency Check Security Scan — advisory (|| true) success 06:25:46
Security Scan Test Suite, run 33949772731 success 06:25:59
Production Image Dependency Parity Test Suite — builds the Python 3.12 image and asserts the pins inside it success 06:25:53
Run Tests Test Suite — Python 3.11: unit + registration + integration success 06:26:50
Server Health Check Test Suite success 06:26:56
SonarCloud Code Analysis sonarqubecloud success 06:26:17
CodeQL Analysis Security Scan — scan + SARIF upload (security-and-quality) success 06:26:48
CodeQL github-advanced-security — the code-scanning results check on new alerts in changed code success 06:26:46

10 check-runs / 9 unique contexts, non-success 0; PR mergeStateStatus: CLEAN. Read at 06:27:10Z on 2026-09-05 after every expected name existed and every run had completed.

CodeQL (github-advanced-security) at this head: success — title "8 new alerts", summary "8 notes": the same eight pre-existing notes dispositioned in the T S156 section (annotations 8/8; two line numbers shifted by this diff), 0 error-level. Open code-scanning alerts for pr=346 by rule: 2× py/cyclic-import, 1× py/unused-global-variable, 5× py/unused-import; py/uninitialized-local-variable. Code-scanning analysis on refs/pull/346/merge: 2026-09-05T06:26:41Z, merge sha b192aedbe, results=8 (unchanged from fecc67c's a1156a5dd).

Evidence currency (T WP-C). d021f8c was the intermediate S159 head (9/10 — results check red); fecc67c is the final green S159 head and the parent of this commit. The merged S159 handoff's d021f8c current/green lines and the original CS prompt's 2f44433 pins are preserved as history and corrected prospectively in the S160 close; the round-4 CS prompt is pinned only to 2c43cd532790a6bf5d5dee184cd3898db2ff8dea.

CS round 6 (2026-09-08, T S159) → bounded erasure-seal / receipt-truth repair (head 59424c57406cbe536cf03565edef310024f7167f)

CS-protocol round 6 — executed by a fresh Codex subagent on T's platform per Alton's standing seat decision and adjudicated separately by T (.macp/reviews/20260908_T_pr346_cs_round6_adjudication_exact_pair_rebind.md; artifact SHA-256 689D9A8E…) — accepted the S162 repair on its own terms (F-01 refresh containment holds under every tested replay, wrong-secret, outage, descendant, PAT and warm-cache ordering; the transaction-protected F-02 cases hold; the 24-test parent differential is genuine at 18 fail / 6 pass) and returned BOUNDED-HOLD / NOT SAFE TO STAGE on three narrower TERMINAL-STATE defects: the durable post-state at the failure and concurrency boundaries, which green tests had not asserted. Alton activated T's bounded repair; this head is one child commit of 84fd9265701a38766bb6d3d8aa3cc3a0de77745b.

Finding (T S159 round 6) Verification Repair (this head) Regression — tests/unit/test_cs_round5_identity_availability.py (36 tests; 10 FAIL on 84fd926 / 26 pass)
R6-01 · HIGH — a claim naming an erased subject can be created after opt-out returns success Confirmed (T probe: processed=True, tombstoned=True, claim_names_erased_subject=True) and reproduced independently before designing: the legacy read, the tombstone check and the claim write were THREE separate operations in both registration lanes and the verified resolver, and opt-out's reverse-query claim sweep is a snapshot that cannot exclude a later writer an erasure seal is committed BEFORE the claim sweep, and every existing-subject claim create / backfill / reassert runs in ONE transaction that reads the seal AND the subject tombstone through itself. Both markers enter the transaction's read set, so a concurrent erasure either conflicts the commit — the retry sees the marker and refuses — or waits for it, in which case the sweep that follows the seal removes what the writer wrote. The seal is read by claim writers and by NO credential validation path, which is what lets it be written early without killing the caller's bearer (the resumability R6-03 requires). A brand-new subject keeps the plain atomic claim: an identifier no one has seen cannot already be erased a complete opt-out interleaved at the claim-write seam leaves NO claim naming the erased subject, from the registration lane and from the verified resolver (84fd926: the claim survived in both); the seal refuses claim writers while the caller's real PAT still validates and the subject is not yet tombstoned (pin on the two-marker split)
R6-02 · MEDIUM — a feedback-only failure returned a false no-account receipt Confirmed (T probe: status=503, persisted=False, accounts=1, stored_email and stored_registration_feedback present): the feedback add() was the LAST write inside the fatal boundary, so its failure returned "no account was created" while the account carrying the address and that feedback text was committed the feedback document is auxiliary: losing it costs the feedback, not the account. Its failure is non-fatal and reported as feedback_received=false — truthful, and identical for a new and an existing address, so it discloses nothing. Every remaining 503 therefore comes from the claim or account write, which makes the receipt's absence claim provable rather than asserted a feedback-only outage returns 201 for a new AND an existing address with byte-equal receipts, feedback_received=false, the account present and no feedback document added (84fd926: 503 with the account stored); a full storage outage still returns the honest 503 with the absent account ASSERTED, in both lanes (pin)
R6-03 · MEDIUM — a committed tombstone could be reported unprocessed, with no authenticated retry Confirmed (T probe: processed=False, subject_tombstone_committed=True, bearer_valid_after_recovery=False): success after a failed hygiene sweep depended on an IMMEDIATE confirmation read, and when that read failed too the caller was told the erasure had not happened and to retry with a bearer the tombstone had already killed the tombstone write is separated from the credential sweep: its normal return is the proof the marker landed, so no confirmation read is needed. The sweep is pure hygiene — the tombstone is what denies on every validation path — so its failure is logged, not fatal. A failure of the tombstone write itself still raises: that commit is genuinely ambiguous, and the receipt says only that deletion could not be confirmed while naming the private rights channel, a continuation that does not depend on the revoked bearer a committed tombstone whose sweep AND confirmation read both fail returns processed=true with the claim released, PII scrubbed and the bearer dead (84fd926: processed=false); a tombstone write that fails is never reported as success and still names the private channel (pin on the ambiguity half)

Also corrected: the test fake gated each logical write twice (T's R6-05); create()/update() now pass the gate exactly once, as one commit RPC would, which is what makes the new single-owners-write assertion a sound oracle. Deferred by T's instruction and NOT repaired: R6-04, _count_tier_slots() converting an aggregate-count exception to 0 so a count-only failure can exceed the cohort cap — an availability/data-integrity issue with no staging credential or privacy effect, recorded for its own bounded lane.

Three commits, and why — a disclosed deviation from the one-SHA instruction. The first commit cedc24b52dd282615525f9c4f7460b31db500568 introduced the erasure seal by WIDENING one shared predicate (_subject_revoked → seal or tombstone). RNA's own adversarial lens, run against that head before it was handed to review, refuted it: the widened predicate also gated the resolver's decision that a mailbox is free again, which is a different question. An erasure interrupted after the seal — its claim release failed, so its caller keeps a live bearer and is expected to retry — then looked revoked to that branch, and the next sign-in minted a FRESH subject with a new active account carrying the address in clear while stranding the original: never tombstoned, credentials alive, unreachable from the owner route. At the round-6 parent that same state failed closed, so it was a HIGH self-inflicted regression, reproducible with no attacker and only an outage. The corrective commit splits the predicate: _subject_revoked keeps seal-or-tombstone and answers "may I bind new state to this subject?" (the transactional claim guard and the legacy-backfill refusal); the new _subject_erasure_complete is tombstone-only and answers "is this mailbox free again?" — the only predicate that may trigger a reclaim. An unfinished erasure now fails closed with an explicit log and the caller finishes it with the bearer that state deliberately leaves alive. Three LOW items landed with it: write_subject_tombstone("") no longer returns success for a marker the store silently drops (the exact contract R6-03 rests on), subject_is_sealed_or_revoked no longer falls open on an empty identifier, and the hygiene-sweep handler no longer swallows programming errors behind a backend warning. A THIRD commit 59424c57406cbe536cf03565edef310024f7167f then followed, because a second lens found false claims in the regression file's own comments: a pin: contract that three tests break at the parent (they fail there on symbol absence, which proves an addition rather than a behaviour), a differential characterised as "six behavioural" when it is five, one test whose only assertion a bare uncaught 500 satisfied, and a helper docstring claiming "every field" while skipping one — plus four mutants that no test in the suite killed. All are corrected and all four mutants closed (each verified to fail with its mutation applied); tests and comments only, no source change. The repair is therefore one bounded change delivered in three commits; handing the independent seat a head carrying a known HIGH regression, or evidence whose comments misdescribe it, was judged the worse option. The regression discriminator fails at cedc24b with "an unfinished erasure handed the mailbox to a fresh subject".

Re-disclosed rather than certified: the account write exists only on the new-address branch, so an account-collection-only outage still separates new from existing (201 vs 503) in both lanes. The lens confirms this is identical at the round-6 parent — pre-existing residue, already on T's own deferred list — but cedc24b's framing ("every remaining 503 comes from the claim or account write") asserted it away. It is a residual, not a closed item.

Disclosed residuals: the erasure seal is permanent, like every marker in that collection — a mailbox is re-registerable after erasure (the address gets a fresh subject) but that subject identifier is sealed forever, which belongs on the TTL lane; honouring the seal in _subject_revoked makes a sealed-but-not-yet-tombstoned subject un-adoptable and un-healable a beat earlier than before (deliberate — erasure has begun); the account-collection-only outage asymmetry and the new-versus-existing commit-timing difference are unchanged from round 5.

No authentication-gate activation, routing, provider, billing, pricing, release-identity, policy, notice, merge, staging, or deployment semantics changed; anonymous feedback remains admitted while issuance is dark. The user-visible behaviour changes are exactly three: a feedback-store failure no longer fails the registration, an erasure whose hygiene sweep fails is reported as processed rather than unconfirmed, and a claim naming a subject under erasure is refused. Adversarial lenses before the commit: two adversarial lenses ran read-only against the pushed head in separate agent contexts, before it was handed to review. Lens A (identity/lifecycle refutation, attacker position) enumerated every writer into the owners collection and confirmed the seal machinery is built rather than asserted — every existing-subject claim write really does read both markers through the transaction that writes it, verified under BOTH the fake's optimistic model and real server-client read locks; the seal is invisible to validate_bearer, validate_refresh, rotation and _is_tombstoned; the fresh-mint paths cannot be driven onto an existing subject; and seal-as-denial-of-service is closed by the owner-scoped opt-out route (anonymous and wrong-subject both 401 with zero markers written). It graded R6-03 HOLDS and REFUTED part of R6-01: the widened predicate reached the resolver's mailbox-is-free decision, forking an interrupted erasure into a fresh subject and stranding the original — the HIGH regression the corrective commit closes, reproduced independently by a discriminating test before the fix. It also found the empty-identifier fail-open, the empty-key tombstone contract, and the over-broad sweep handler (all closed), and confirmed the account-write asymmetry as pre-existing at the parent rather than new. Lens B (regression vacuity, mutation harness) audited the suite against a 16-mutant catalogue and the old-head reason table; its report is recorded in the S163 record.

Local gates at this head (imports bound to the clone, verifimind_mcp.__file__ printed; on Windows PYTHONPATH uses ; and pytest's temp root must be short): required targeted set + this file 242 passed — all 230 round-6 controls retained plus the 12 new tests; unit 1,457 passed / 3 skipped / 1 failed (the one failure is the documented dev-env test_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority); integration 7 passed / 11 skipped; Bandit on the changed sources: no new findings. Old-head discrimination: the same file, with the changed fake, against a detached worktree at 84fd926: 10 failed / 26 passed — SEVEN fail behaviourally and THREE fail because store primitives this repair introduces do not exist at the parent; a symbol-absence failure proves an addition, not a behaviour, so the split is stated rather than presented as ten behavioural.

Remote checks at 59424c57406cbe536cf03565edef310024f7167f (read from the commit's check-suite after every expected name existed and every run completed, including the github-advanced-security CodeQL results check):

Check (unique context) Producer Conclusion Completed (UTC)
Bandit SAST Analysis (posted twice) Security Scan workflow, run 34252456327 — the real scan, advisory (|| true); and Docs Contract and CI Bypass, run 34252456337 — synthetic instant-pass success / success 16:40:27 / 16:40:16
Safety Dependency Check Security Scan — advisory (|| true) success 16:40:31
Production Image Dependency Parity Test Suite, run 34252456156 — builds the Python 3.12 image and asserts the pins inside it success 16:40:38
Security Scan Test Suite success 16:40:39
SonarCloud Code Analysis sonarqubecloud success 16:41:17
Run Tests Test Suite — Python 3.11: unit + registration + integration success 16:41:25
Server Health Check Test Suite success 16:41:31
CodeQL github-advanced-security — the code-scanning results check on new alerts in changed code success 16:41:31
CodeQL Analysis Security Scan — scan + SARIF upload (security-and-quality) success 16:41:31

10 check-runs / 9 unique contexts, non-success 0; PR mergeStateStatus: CLEAN, draft. Read at 16:41:43Z on 2026-09-08 (UTC; 2026-09-09 00:41 +08:00) after every expected name existed and every run had completed.

CodeQL (github-advanced-security) at this head: success — title "13 new alerts", summary "13 notes", 0 error-level and 0 in production logic: 6× py/cyclic-import on the established lazy registration ↔ oauth.stores imports (the pattern that AVOIDS a real import cycle — this repair added call sites, so the count rose from 2 at 68daa5c to 6), 5× py/unused-import and 2× py/unused-global-variable (one of them _WRITE_OPS, dead in the test file since the owners-write assertion was re-instrumented at the commit gate). The four test-style notes disclosed at 84fd926 are CLEARED. All cosmetic; disclosed and deferred to the next bounded SHA or T's residual list rather than spent on a third commit. Code-scanning analysis on refs/pull/346/merge: 2026-09-08T16:41:24Z, merge sha 7c6211305, results=13; 0× py/uninitialized-local-variable.

CS round 5 (2026-09-06, T S159) → bounded lifecycle/availability repair (head 84fd9265701a38766bb6d3d8aa3cc3a0de77745b)

CS-protocol round 5 — executed by a fresh Codex subagent on T's platform per Alton's standing seat decision and adjudicated separately by T (.macp/reviews/20260906_T_pr346_cs_round5_adjudication_exact_pair_rebind.md; artifact SHA-256 54DD7BC5…) — reproduced every S161 receipt (206 targeted, 1,423 / 2 unit, the exact 32 / 7 old-head differential; the S161 repair materially closes the four round-4 traces) and returned BOUNDED-HOLD / NOT SAFE TO STAGE on four narrower orderings in the lifecycle of the credential family and the ownership claim. Alton activated T's bounded repair; this head is one child commit of 68daa5c189d758bd1da2edb63cf42b25140ae2d4.

Finding (T S159) Verification Repair (this head) Regression — tests/unit/test_cs_round5_identity_availability.py (24 tests; 18 FAIL on 68daa5c at their stated first assertion / 6 pins pass on both heads)
F-01 · HIGH — save-time refresh reuse denied without containing the winning family Confirmed (T probe: SAVE_TIME_RECORD_IS_NONE True, FAMILY_TOMBSTONED False, WINNER_ACCESS_LIVE True): save_token's second validate_refresh returned None for a token rotated after grant-authentication and raised invalid_grant without calling contain_refresh_reuse the save-time None branch mirrors the first-validation branch: contain_refresh_reuse(presented) revokes the grant family before invalid_grant. Containment fires only for a secret-valid, already-rotated token (a wrong-secret or unknown token revokes nothing); an outage on either guarded read raises StoreUnavailable and propagates, never collapsing into a denial the loser's save-time re-validation, with the winner rotating between the two validations, returns invalid_grant AND the winner's access and refresh are dead — with a known-positive that they were live an instant earlier (68daa5c: both live); an outage INSIDE containment propagates as StoreUnavailable (68daa5c: no containment ran); an outage raised by the re-validation itself propagates (pin)
F-02 · HIGH — unconditional stale-claim delete could fork one mailbox into two verified subjects Confirmed (T two-thread probe: ACCOUNT_COUNT 2): the resolver released a tombstoned owner claim with a bare delete(), so two verified ceremonies each erased the other's replacement claim and wrote separate accounts ABA-safe ownership: _reclaim_tombstoned_owner reads the claim and installs the fresh subject in ONE transaction, bailing (re-read, adopt the winner) if the claim no longer names the tombstoned uuid it read; the resolver mints its fresh verified subject inline through it, create-if-absent; _release_owner_claim deletes only while the claim still names the expected subject. Read-set conflicts (fake) or server read locks (real client) serialize competing writers two seam-gated verified resolvers racing on a stale tombstoned claim converge on ONE subject, one account, one claim, never the revoked identifier (68daa5c: two accounts); a resolver interposed inside the reclaim's read→commit window is conflicted and adopts (68daa5c: two accounts); opt-out leaves a claim re-pointed to another subject alone, via the public path (68daa5c: erased) and when the re-point lands inside the release's own commit window (68daa5c: erased); the reclaimed claim is unverified until its record lands (pin)
F-03 · MEDIUM — opt-out not resumable after a late claim-delete failure; false success on retry Confirmed (T probe: BEARER_LIVE_AFTER_FIRST False, CLAIM_AFTER_FIRST True, SECOND_DIRECT_PROCESSED True, CLAIM_AFTER_SECOND True): PII scrub and credential revocation ran BEFORE the claim delete, so a delete failure left the caller revoked (401 on retry), the claim present, the email key gone, and a later retry reporting success while the claim remained ordered by reversibility: de-identify PII → release EVERY claim naming the subject through a reverse lookup on the claim's own uuid field (a durable cleanup identity that survives the scrub and also cleans the inherited pre-repair state — claim present, email already scrubbed — the parent could never clean) → tombstone + revoke LAST. A release failure leaves the caller's real bearer valid and the claim findable for the retry; once the subject tombstone every validation path consults has landed, a failed hygiene sweep no longer denies an erasure that happened. Scrubbing before releasing closes the release-to-tombstone window: a ceremony there finds no email and mints a FRESH subject after a failed release the caller's real PAT still validates and the claim still names the subject (68daa5c: bearer dead); a retry after the outage clears returns processed=true only with the claim gone (68daa5c: true with the claim present); an inherited scrubbed record with a stale claim is cleaned by uuid (68daa5c: never); a sweep failure after the tombstone reports truthful success with the bearer dead (68daa5c: false); a scrub failure before the release is resumable with the same credential (pin); a clean opt-out still releases, scrubs, tombstones (pin)
F-04 · MEDIUM — a reads-up/writes-down claim-store outage reopened email enumeration in both lanes Confirmed (T probe: EA_EXISTING 201 / EA_NEW 500 text/plain; LIGHT_EXISTING 200 / LIGHT_NEW 500): an existing address performed no write while a new address hit claim_email().create() and the backend failure escaped as HTTP 500 symmetric write, honest receipt: every email-bearing submission performs exactly one owners-collection write — a new address creates its claim; an existing address whose claim is missing (a legacy, pre-claim record) gets it backfilled by the lane, naming the record's own uuid and lane; an existing address whose claim exists gets the invariant email_hash re-asserted (nothing new stored) — so a storage outage fails both identically, and both lanes then raise RegistrationStoreUnavailable, rendered by the handlers as ONE retryable 503 (persisted:false, uuid:"", "could not be completed and no account was created", Retry-After: 60, Cache-Control: no-store). Never a generic 500 (oracle), never a success screen (F-RES-1): a persistent quota or permission failure is loud. The client-construction (db is None) branch, slot-cap, environment-identity, validation errors, and the anonymous lightweight path are outside the boundary and unchanged; a non-backend exception still propagates EA and lightweight lanes under writes-down: new and existing both 503, whole body and header keys equal, persisted:false, no phantom identifier (68daa5c: 500 vs 201 / 500 vs 200); a quota exhaustion is never a silent success in either lane and persists nothing (68daa5c: 500); an existing address performs exactly one owners write — update with a claim, create for a legacy record (68daa5c: none); a legacy record's claim is backfilled by the lane (68daa5c: no claim); feedback-store-only and reads-down outages return the same honest receipt for both addresses (68daa5c: 500); a record-store-only failure yields the honest receipt (68daa5c: 500); the anonymous path keeps its UUID and hands out none on a write outage (pins)

Disclosed residuals, not scored: (a) a failure confined to the ACCOUNT collection while the owners collection accepts writes is asymmetric by construction (an existing address never writes its account record on this path) — not a realistic Firestore failure mode, since transport, quota, and IAM failures are project-wide; (b) the read/write timing difference between new and existing addresses (the client retries a failing commit for up to ~60 s) is named, as round 5 named it; (c) an existing address now costs one small owners write per duplicate submission, bounded by the outer rate limiter exactly as a new-address flood is; (d) the lanes now backfill a legacy record's missing claim — the verified resolver's own rule applied one step earlier, a behaviour change T should see plainly.

No authentication-gate activation, routing, provider, billing, pricing, release-identity, policy, notice, merge, staging, or deployment semantics changed; anonymous feedback remains admitted while issuance is dark; the db is None branch keeps its ratified F-RES-1 shape. Adversarial lenses before the commit: two adversarial lenses ran read-only against the draft tree before the commit, in separate agent contexts. Lens A (identity/lifecycle refutation, attacker position; 24 probes outside the tree, 24/24 on the draft vs 14 failing at the parent) held F-01, F-02, and F-03 across the optimistic-fake and pessimistic-lock models, warm-cache and PAT descendants, and every opt-out failure point — and REFUTED the first F-04 draft (MEDIUM): it answered the oracle by returning the uniform SUCCESS receipt on a write outage, claiming persistence that never occurred and turning quota exhaustion or permission misconfiguration into silent success on every request (a reversal of the ratified F-RES-1 doctrine); it also found two LOW F-03 residuals (a sweep failure after the tombstone reported failure with a dead bearer; a claim re-created by a ceremony in the release window with no reverse lookup) and two hygiene items (a dead db parameter; the fake raising KeyError where production raises NotFound). All redesigned before the SHA: symmetric owners write + honest 503; scrub → reverse-lookup release → tombstone-authoritative success; hygiene closed. Lens B (regression vacuity, mutation harness): on the first suite 10/14 mutants killed, two old-head failures incidental (a missing symbol; a re-raised server exception), real oracle shapes hidden by field-subset receipts, an F-01 pin injected upstream of its branch, a tombstone read standing in for "bearer alive"; thirteen verified candidate tests handed over. The suite was rewritten — whole-body and header-key receipts, a status-not-exception client, known-positives inside the F-01 patch, a real PAT as the retry-credential proxy, the candidates, and the redesign's own discriminators — and the final 18-mutant matrix on the committed code has no survivor (the one first-pass survivor, an unconditional transactional release unreachable through the reverse lookup alone, is killed by re-pointing the claim inside the release's commit window).

Local gates at this head (imports bound to the clone, verifimind_mcp.__file__ printed; on Windows PYTHONPATH uses ; and pytest's temp root must be short — a deep redirected TEMP produces long-path false failures in an unrelated LLM-failover file, reproduced identically at the clean parent): unit 1,445 passed / 3 skipped / 1 failed (the one failure is the documented dev-env test_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority), required targeted set + round-4 controls 206 passed + this file 24 passed, integration 7 passed / 11 skipped; Bandit on the changed sources: no new findings (one pre-existing Low B105 on the literal "Bearer"); mutation matrix on the final code 18/18 killed. Old-head discrimination: the same file (and the updated fake) against a detached worktree at 68daa5c (rev-parse printed, helpers confirmed absent in the old source): 18 failed / 6 passed — every "68daa5c:" test fails at its stated first assertion, every "pin:" test passes (log bound in the S162 record).

Remote checks at 84fd9265701a38766bb6d3d8aa3cc3a0de77745b (read from the commit's check-suite after every expected name existed and every run completed, including the github-advanced-security CodeQL results check):

Check (unique context) Producer Conclusion Completed (UTC)
Bandit SAST Analysis (posted twice) Security Scan workflow, run 34067551697 — the real scan, advisory (|| true); and Docs Contract and CI Bypass, run 34067551635 — synthetic instant-pass success / success 23:40:46 / 23:40:42
Safety Dependency Check Security Scan — advisory (|| true) success 23:40:53
Production Image Dependency Parity Test Suite, run 34067551657 — builds the Python 3.12 image and asserts the pins inside it success 23:40:54
SonarCloud Code Analysis sonarqubecloud success 23:41:23
Run Tests Test Suite — Python 3.11: unit + registration + integration success 23:41:50
CodeQL github-advanced-security — the code-scanning results check on new alerts in changed code success 23:41:52
CodeQL Analysis Security Scan — scan + SARIF upload (security-and-quality) success 23:41:55
Server Health Check Test Suite success 23:41:56
Security Scan Test Suite success 23:42:01

10 check-runs / 9 unique contexts, non-success 0; PR mergeStateStatus: CLEAN. Read at 23:42:07Z on 2026-09-06 (UTC; 2026-09-07 07:42 +08:00) after every expected name existed and every run had completed.

CodeQL (github-advanced-security) at this head: success — title "10 new alerts", summary "10 notes", 0 error-level, 0 in production code: the six pre-existing dispositioned notes (1× py/unused-global-variable, 5× py/unused-import) plus four new test-style notes, all in tests/unit/test_cs_round5_identity_availability.py — a reads-down fake's __getitem__ raising the outage class instead of a LookupError (py/unexpected-raise-in-special-method), the thread runner catching BaseException (py/catch-base-exception), and two chained == … is not None comparisons (py/test-equals-none ×2). They became visible only after the push; the one-SHA contract forbids a cosmetic follow-up commit, so they are disclosed here and deferred to the next bounded SHA (or T's residual list). Code-scanning analysis on refs/pull/346/merge: 2026-09-06T23:41:46Z, merge sha 1bdf4e2a8, results=10 (was 6 at 68daa5c's 0da13bbb2); 0× py/uninitialized-local-variable.

CS round 4 (2026-09-05, T S158) → bounded identity/provenance repair (head 68daa5c189d758bd1da2edb63cf42b25140ae2d4)

CS-protocol round 4 — executed by a fresh Codex subagent on T's platform per Alton's seat decision and adjudicated separately by T (.macp/reviews/20260905_T_pr346_cs_round4_adjudication_exact_pair_rebind.md; artifact SHA-256 39E32698…) — reproduced every S160 receipt (167 targeted, 1,382 / 3 / 1 unit, 33 / 11 old-head differential; WP-A and WP-B materially hold) and returned BOUNDED-HOLD / NOT SAFE TO STAGE on a new, narrower class: unauthenticated state acquiring verified-subject meaning. Alton activated T's bounded repair; this head is one child commit of 2c43cd532790a6bf5d5dee184cd3898db2ff8dea.

Finding (T S158) Verification Repair (this head) Regression — tests/unit/test_cs_round4_identity.py (39 tests; 32 FAIL on 2c43cd5 at their stated first assertion / 7 pins pass on both heads)
1 · HIGH — unverified state crossed the mailbox-verification boundary; no canonical email owner across lanes Confirmed (T probes adopted_updates_consent: True, linked_feedback_survives_adoption: True): adoption wiped name and inline feedback only; both lanes were query-then-write in two different collections, so one address could own two accounts and the resolver adopted whichever it scanned first One canonical owner per email: a hash-keyed claim document (email_owners, environment-resolved — bare in production, staging_ in staging) created with Firestore's atomic DocumentReference.create(), which exactly one caller can ever win; both lanes first scan BOTH account collections for a legacy record, then claim; the verified resolver adopts through the claim, backfills legacy records into claims, and heals a claim whose record write never landed. Mailbox proof is a state-transition sanitizer, not retroactive authorship: adoption neutralizes every caller-chosen field (profile, inline feedback, updates_consent → off, tier → lane default, pilot_source), stamps the verified actor's OWN Terms/Privacy acceptance and consent_source, records what it neutralized, and detaches feedback linked before proof. Registration-path feedback is now written with uuid=None and an explicitly unverified link one account per address across lanes, both orders (2c43cd5: two); a legacy record in the OTHER lane blocks a new account (2c43cd5: did not); a claimed address is owned before its record is visible (2c43cd5: no claim concept); the ceremony creates one subject AND its claim, backfills a legacy record, heals a claim without a record under the claimed identifier, canonical key (2c43cd5: none of these existed); T's exact probe — consent, tier, pilot provenance, profile neutralized, ceremony-time acceptance, consent_source (2c43cd5: updates_consent survived); linked feedback never carries the subject (2c43cd5: uuid=<victim>); legacy linked feedback detached (2c43cd5: survived); verified-bearer feedback untouched, an already-verified subject unchanged, a deletion-requested record never adopted (pins)
2 · HIGH — anonymous feedback stored a caller-claimed victim UUID as attribution Confirmed (T probe anonymous_claimed_uuid_persisted: True): the body uuid was written into the authoritative field with no subject binding attribution derives ONLY from a validated Bearer (http_server._bearer_subject): uuid is the bearer subject or None, attribution names the grade, a body UUID is kept only as claimed_uuid_unverified; a Bearer that is present but invalid → 401 with nothing stored; credential-store outage → 503 with nothing stored; anonymous feedback stays admitted (T D-157-5) anonymous + body UUID → uuid None, claim recorded (2c43cd5: body UUID stored); bearer of subject A + body UUID B → uuid == A, B kept only as a claim (2c43cd5: B stored); invalid bearer → 401, nothing stored (2c43cd5: 201); outage → 503 no-store, nothing stored (2c43cd5: 201); anonymous without a claim still 201 (pin)
3 · MEDIUM — both email lanes were account-existence oracles Confirmed (T probes ea_messages_equal: False, lightweight_messages_equal: False): the duplicate branch said "If an account already exists…" with feedback_received=False, the new branch "Thanks — your interest is recorded…" one uniform response per lane for new AND existing addresses (_UNIFORM_EMAIL_MESSAGE, _uniform_lightweight_response); the EA lane records feedback for both cases, explicitly unverified, so feedback_received is truthful and identical; the slot-cap and storage-down branches were already existence-independent EA lane new vs existing: identical status, body bytes, content-type and content-length under a frozen clock, one account, both feedback records unverified (2c43cd5: bodies differ); lightweight lane likewise (2c43cd5: differ); the anonymous lightweight path still returns its UUID (pin)
4 · MEDIUM — owner routes turned a credential-store outage into 401 Confirmed (T probes owner_dashboard_auth_outage_status: 401, owner_optout_auth_outage_status: 401): _authenticated_subject's broad except swallowed StoreUnavailable _bearer_subject classifies absent / invalid / ok and PROPAGATES StoreUnavailable (and EnvironmentMisconfigured); dashboard → 503 page with no history read; opt-out → the existing non-enumerating processed=false 503 with no deletion attempted; both carry Retry-After and Cache-Control: no-store dashboard outage → 503, read_trinity_history not called (2c43cd5: 401); opt-out outage → 503 processed=false, process_optout not awaited (2c43cd5: 401); missing / invalid / wrong-subject credentials stay 401 on both routes (pin)

Closed before the SHA by the S161 adversarial lenses (each with a parent-failing regression): (a) a regression of this repair's own first draft — opt-out did not release the ownership claim, so an opted-out mailbox could never register again and, after a purge, the heal path would have re-created the address under the subject-tombstoned identifier; now opt-out releases the claim and the resolver never adopts or heals a tombstoned subject (it releases the stale claim and mints fresh); (b) lane record writes were unconditional set(), so a record the ceremony healed between the lane's claim and its write could be clobbered with the unverified preregistration — lane and heal records are now create-if-absent; (c) any early_adopters document, verified or not, was granted the pioneer rate bucket — an explicitly unverified preregistration now earns none (legacy records without the flag keep their standing); (d) registered_at now reflects the verified actor's moment (the earlier value kept as provenance), corrupt claims fail closed, and no broad except turns programming errors into "invalid credential". Also closed in passing: the round-4 residual that the verified resolver constructed the Firestore client before resolving its collections (it now resolves first, like every other user-data path). The other round-4 residuals — raw UUID in two warning-log paths, per-subject rate buckets rotated by unauthenticated UUID headers with process-local caps, single-chunk body-peek retention, DCR echo bounds, corrupt expires_at — are unchanged and stay routed to the staging matrix and backlog exactly as T recorded them.

No authentication-gate activation, routing, provider, billing, pricing, release-identity, policy, notice, merge, staging, or deployment semantics changed; anonymous feedback remains admitted while issuance is dark. Adversarial lenses before the commit: two adversarial lenses ran read-only against the tree before the commit, in separate agent contexts. Lens A (identity/provenance refutation) held contracts 3–5 and the regression envelope, verified create()'s Precondition(exists=False)AlreadyExists from the installed client, and REFUTED four things in the first draft — the opt-out claim retention (HIGH), the heal-vs-set() clobber (MEDIUM), unverified cohort privilege (MEDIUM), and registered_at / fail-open corrupt claims / the broad except (LOW) — all repaired before the SHA (its own full-suite observation: 1,395 passed / 3 skipped with the parity module deselected). Lens B (regression vacuity, mutation harness): 24 of 27 mutations of the repaired code were killed by the round-4 file and every old-head reason matched; it flagged a mislabelled pin, a timestamp-echo oracle hidden by the frozen clock, the resolver's dead lost-race path, the unpinned atomic create, and unasserted sanitizer fields — all folded in (clock advanced between the two requests with the second receipt required to carry the current time; two interleaving tests; a one-atomic-create-and-nothing-else pin; tier / consent / whole-record / claim-path-detach / lightweight-lane assertions; which-503 pinned by body and headers).

Local gates at this head (imports bound to the clone, verifimind_mcp.__file__ printed): unit 1,421 passed / 3 skipped / 1 failed (the one failure is the documented dev-env test_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority), registration 84 passed, integration 7 passed / 11 skipped; Bandit with CI's flags (-r src/ -ll -ii --skip B101): no issues. Old-head discrimination: the same file against a sparse worktree at 2c43cd5 (imports bound to the worktree, rev-parse printed): 32 failed / 7 passed — every "2c43cd5:" test fails at its stated first assertion, every "pin:" test passes (log bound in the S161 record).

Remote checks at 68daa5c189d758bd1da2edb63cf42b25140ae2d4 (read from the commit's check-suite after every expected name existed and every run completed, including the github-advanced-security CodeQL results check):

Check (unique context) Producer Conclusion Completed (UTC)
Bandit SAST Analysis (posted twice) Security Scan workflow, run 33998533443 — the real scan, advisory (|| true); and Docs Contract and CI Bypass, run 33998533461 — synthetic instant-pass success / success 23:23:25 / 23:23:23
Safety Dependency Check Security Scan — advisory (|| true) success 23:23:31
Security Scan Test Suite, run 33998533415 success 23:23:32
Production Image Dependency Parity Test Suite — builds the Python 3.12 image and asserts the pins inside it success 23:23:39
Run Tests Test Suite — Python 3.11: unit + registration + integration success 23:24:29
Server Health Check Test Suite success 23:24:36
SonarCloud Code Analysis sonarqubecloud success 23:24:06
CodeQL Analysis Security Scan — scan + SARIF upload (security-and-quality) success 23:24:38
CodeQL github-advanced-security — the code-scanning results check on new alerts in changed code success 23:24:33

10 check-runs / 9 unique contexts, non-success 0; PR mergeStateStatus: CLEAN. Read at 23:24:41Z on 2026-09-05 (UTC; 2026-09-06 07:24 +08:00) after every expected name existed and every run had completed.

CodeQL (github-advanced-security) at this head: success — title "6 new alerts", summary "6 notes": six of the eight pre-existing dispositioned notes (1× py/unused-global-variable, 5× py/unused-import); the two py/cyclic-import notes no longer appear in the merge-ref analysis; 0 error-level; py/uninitialized-local-variable. Code-scanning analysis on refs/pull/346/merge: 2026-09-05T23:24:27Z, merge sha 0da13bbb2, results=6 (was 8 at 2c43cd5's b192aedbe).

Evidence at 2f44433/7a0c462 (history, unchanged)

Local at 7a0c462: 1,351 passed / 3 skipped across unit + registration (1,267 + 84). Full end-to-end OAuth ceremony verified through the real ASGI app (DCR → authorize → verify → consent → token → bearer at /mcp; 401 unauth, 403 insufficient-scope).

Deferred lanes (T's route, not silently omitted)

CIMD with SSRF-hardened metadata retrieval and the SDK-backed MCP 2026-07-28 implementation remain after CS pass 1 and the exact-client 2025-11-25 matrix. Physical-deletion (TTL) sweep receipts are a retention follow-up. Before the client matrix relies on per-IP mint limits, staging must bind the _client_ip trusted-last-hop assumption to the actual ingress topology. Release-lane follow-up surfaced by the license lens: .dockerignore excludes *.md, so THIRD_PARTY_NOTICES.md is not copied into the production image (the upstream license files ARE shipped at <dist>.dist-info/licenses/ by uv pip install); adding !THIRD_PARTY_NOTICES.md is a deployment-artifact change and is deliberately not made in this bounded SHA. No claim is made that those gates are complete.

Deploy-time env (for the eventual staging/dark deploy — not this SHA)

VERIFIMIND_ENVIRONMENT and VERIFIMIND_PUBLIC_ORIGIN (a staging service MUST declare both — since this head an unresolvable identity refuses every user-data surface with an explicit 503 instead of falling back to production collection names), OAUTH_ISSUANCE_ENABLED (default off), REGISTRATION_GATE_ENABLED (default off), AUTH_BOUNDARY_MODE (default connection), INGRESS_PROXY_HOPS (default 1 — the element the Google Front End appends last; set 2 only behind a Google external Application LB, proven by the deployment's ingress receipt), OAUTH_HASH_PEPPER, VALUE_SUBJECT_HMAC_KEY, SMTP_* (Brevo, via Secret Manager), MAIL_RECIPIENT_ALLOWLIST (staging). Policy publication/gate dates are still identity-time fills (dates only).

Chain from here

Only with every remote check green at this exact SHA: fresh CS-protocol safe-to-stage round 7 of 59424c57406cbe536cf03565edef310024f7167f — executor per Alton's standing seat decision (a T (CTO) Codex subagent, adjudicated by T's main session and named by that evidence grade), new exact-head prompt only → T exact-pair rebind → Alton's separate isolated-staging decision (binding INGRESS_PROXY_HOPS to the staging service's own ingress by the same receipt; the staging service must declare VERIFIMIND_ENVIRONMENT=staging AND its own VERIFIMIND_PUBLIC_ORIGIN, or every user-data surface refuses with an explicit 503 by design) → dark client matrix → SSRF-CIMD + 2026-era lane → CS final → T exact-head rebind → Alton's separate policy/notice/activation decisions.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU

creator35lwb-web and others added 4 commits August 29, 2026 00:26
…weep)

Groq now serves qwen/qwen3.8-27b (Preview tier, 131k context, 16,384 max
completion - model docs verified 2026-08-29). Additions:

- PROVIDER_CONFIGS groq models += qwen/qwen3.8-27b (3.6 stays listed while
  Groq serves it); models_verified_at -> 2026-08-29
- GROQ_8K_TPM_MODELS += qwen/qwen3.8-27b CONSERVATIVELY (same family/size/
  tier as live-proven 3.6; over-clamp only shortens completions, under-clamp
  repeats the v0.5.49 413 class); revisit on a live per-model probe
- tests: catalog membership + discriminating TPM-set membership pin

BYOK-catalog-only: no hosted routing, no CC-1 experiment surface (BYOK-on-
lane runs are denominator-excluded; no window is running). Focused suites
317 passed / 0 failed. NO release identity yet - draft until authorized.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2WLyXrgH8dSXqKJiXW5Cu
…date

T S151 D-151-7 bounded repair (forecast F-T-151-4): the frozen currency
fixture as_of=2026-08-07 predated this PR''s groq models_verified_at
2026-08-29, tripping verification_date_in_future on two tests. The
green-fixture day moves to 2026-08-29 - on the newest pin, inside every
provider''s 90-day window (earliest: openai 2026-06-22, stale from
2026-09-21). The all-stale 2027-01-01 fixture and the real-clock health
test are unchanged. No release identity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
Alton GO 2026-08-30 (AY/AZ registration-auth lane). Mechanism ships
complete but DARK: REGISTRATION_GATE_ENABLED defaults off, and runtime
behavior stays byte-identical to v0.5.62 except honest repairs.

- RegistrationGate (FastMCP middleware): when enabled, the four
  execution tools (consult X/Z/CS + run_full_trinity) require a
  server-verified registered UUID from the X-VerifiMind-UUID header;
  hosted-key and BYOK alike. Discovery, template reads, pages stay
  anonymous. Fail-closed on registration-store outage (retryable 503-
  class denial), in-band structured denials, free-registration CTA only.
- registration_lookup.resolve_registration: reader-side union of
  early_adopters + ea_registrations, status-aware (deletion_requested
  revokes), positive-cache only. Closes the broken POST /register
  bridge without write migration (no slot-cap or pioneer-tier side
  effects).
- Telemetry: tool_invoked contract UNCHANGED (name-only dispatch
  attempt); new tool_authorized {tool, registered_uuid} after
  verification and tool_denied {tool, reason} with NO caller input;
  trinity_run_started/completed gain registered_uuid only for verified
  runs; denied calls emit zero lifecycle events.
- Honest repairs live now: POST /register db-None returns
  persisted:false "NOT saved" (F-RES-1 parity); /whoami recognizes
  lightweight registrations and reports lookup outages honestly; stale
  Privacy v1.0/v2.5 page references corrected.
- Sybil bound: identity-minting POSTs (/register,
  /early-adopters/register) leave the rate-limit exemption; page GETs
  stay exempt.
- Terms v2.5 + Privacy v2.6 (EN+BM, one canonical source): announce the
  requirement with gate-effective date 2026-09-30 (proposed; finalized
  at release identity) - the 14-day advance notice, machine-checked by
  test_gate_effective_date_honors_14_day_notice.
- get_public_contract().registration projects the flag truthfully in
  both states.

Tests: 36 new (fail-closed inversion, denial walks, lifecycle join,
no-lifecycle-on-denial, bridge semantics, first HTTP-level POST
/register + /whoami coverage, 14-day notice); full unit suite 1213
passed locally (1 known env-only dependency-parity failure; CI is the
authority).

NO release identity: version stays 0.5.62; activation is a separate
Alton-held config flip on/after the policy effective date.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
… ladder

Consumes T S152 BOUNDED SECURITY HOLD (P0 1-8) + T S153 OAuth addendum
per the accepted Design v2 (private brief + PR #144). Everything stays
DARK behind REGISTRATION_GATE_ENABLED (default off) - dark parity is
test-pinned.

- oauth/ package: narrow spec-exact OAuth 2.1 core, STDLIB CRYPTO ONLY
  (zero new dependencies - the design''s bounded fallback, expanded CS
  scope): authorization-code + PKCE S256-only, opaque store-validated
  tokens vm{at,rt,pat}.{id}.{secret} with SHA-256 verifiers at rest and
  constant-time compare; refresh rotation with reuse-detection lineage
  revocation; RFC 8414 AS metadata, RFC 9728 protected-resource
  metadata, RFC 7009 revoke, bounded RFC 7591 DCR (https/loopback/
  private-scheme redirect URIs); enumeration-safe email ceremony
  (uniform "code sent", 8-digit code, 5-attempt cap, email stored as
  hash in verification docs); vendor-agnostic SMTP mailer that FAILS
  CLOSED in production (console backend refused on Cloud Run); PAT lane
  minted from a live access token (P0 #3, #1, D-153-4).
- McpAuthBoundary (pure ASGI, inside the rate limiter): HTTP 401 +
  WWW-Authenticate with the PRM pointer / 503+Retry-After on store
  outage; connection mode default + execution mode (bounded body peek
  with replay, fails toward authentication); legacy X-VerifiMind-UUID
  header confers nothing (D-153-5).
- RegistrationGate v2: subject only from the boundary contextvar;
  cross-subject user_uuid FAILS CLOSED (P0 #5); events renamed to what
  they prove - tool_admitted (admission only, P0 #8) and terminal
  tool_completed {success, inference_quality, environment,
  traffic_class, execution_id}; all events + trinity lifecycle carry an
  HMAC-pseudonymous subject, raw UUIDs never in value telemetry (P0 #6,
  unkeyed => field omitted, never raw).
- Union revocation: opt-out now tombstones BOTH registration
  collections AND every live OAuth/PAT credential, success only when
  all stores answered (P0 #2); validation caches bounded to 60s with
  local purge (P0 #7); issuance limits keyed on server-observed
  properties only (P0 #4).
- Policies: Terms v2.5 / Privacy v2.6 rewritten for the OAuth ceremony
  in EN + Bahasa Malaysia - UUID explicitly a NON-credential, tokens
  are the credentials, pseudonymized telemetry disclosed, transactional
  email processor disclosed (named at release), 14-day notice machinery
  unchanged and machine-checked.
- Composition per Alton 2026-08-31: branch rebased onto #345 exact
  code-PASS head c8d1e8c (commits byte-identical ancestors) so ONE
  version release patch carries qwen currency + this spine.

Tests: 1,333 passed / 3 skipped locally across unit + registration
suites (116 in the five auth suites; sole local failure is the
dependency-parity check refusing the stale dev environment - CI is the
authority). CIMD fetch is a marked follow-up on the v0.5.56 pinned-dial
fetcher; dark client matrix, CS review, T rebind, and every release/
activation authority remain ahead. NO release identity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
@creator35lwb-web
creator35lwb-web force-pushed the feat/registration-auth-gate branch from ea0395f to 6ab7e32 Compare August 31, 2026 11:08
@creator35lwb-web

Copy link
Copy Markdown
Owner Author

Design v2 implemented at 6ab7e32 — T S152/S153 HOLD consumed

Composition change (Alton, 2026-08-31): this branch is now rebased onto #345''s exact code-PASS head c8d1e8c (its commits are byte-identical ancestors), so one version release patch carries qwen currency + the auth spine, per integrate-once-deploy-once.

What replaced v1 (all DARK behind REGISTRATION_GATE_ENABLED, dark parity test-pinned):

  • OAuth 2.1 spine, stdlib crypto only, zero new dependencies (src/verifimind_mcp/oauth/): code+PKCE (S256 only), opaque hash-at-rest tokens (vmat/vmrt/vmpat), refresh rotation with reuse-detection lineage revocation, RFC 8414/9728/7009, bounded DCR; enumeration-safe email ceremony (uniform answers, hashed emails, attempt caps); production-fail-closed vendor-agnostic mailer; PAT lane for local clients. Resolves the Authlib decision point via the design''s bounded-fallback lane — expanded CS scope acknowledged.
  • McpAuthBoundary (pure ASGI): HTTP 401 + WWW-Authenticate with the PRM pointer (D-153-5), 503+Retry-After on store outage; connection-mode default + execution-mode (bounded body peek with replay, fails toward authentication); the legacy UUID header confers nothing.
  • Gate v2: subject only from the boundary; cross-subject user_uuid fails closed (P0 🚀 MCP Server Proof-of-Concept Complete - Genesis Context Server Now Available! #5); tool_admitted (admission only, P0 🎉 20 Real AI Concept Validations - Examples Now Available! #8) + terminal tool_completed {success, quality, environment, traffic_class, execution_id}; HMAC-pseudonymous subjects everywhere — raw UUIDs never in value telemetry (P0 #6).
  • Union revocation (P0 Fix broken documentation links in README #2): opt-out tombstones both collections + every live credential; caches bounded ≤60s (P0 VerifiMind-PEAS: Prompt Engineering Attribution System - Production-Ready MCP Server for Multi-Model AI Validation #7); identity-independent mint limits (P0 Welcome to the VerifiMind-PEAS Community! #4).
  • Terms v2.5 / Privacy v2.6 rewritten for the OAuth ceremony (EN+BM): UUID explicitly a non-credential; tokens are the credentials; pseudonymized telemetry + email processor disclosed; 14-day notice machine-check unchanged.

Evidence: 1,333 passed / 3 skipped locally (116 across the five auth suites; the sole failure is the dependency-parity test correctly refusing the stale dev env — CI authoritative).

Identity-time fills: policy dates + gate-effective date (2026-09-02/2026-09-30 proposed) + email-processor name. Marked follow-ups: CIMD fetch on the v0.5.56 pinned-dial fetcher; dark client matrix (Claude.ai/Claude Code/Codex/Cursor) is a release gate ahead of CS review → T exact-pair rebind → Alton''s dark-release/notice/activation decisions. No release identity.

Comment thread mcp-server/src/verifimind_mcp/oauth/endpoints.py Fixed
Comment thread mcp-server/src/verifimind_mcp/oauth/endpoints.py Fixed


def _db():
from verifimind_mcp.registration import _get_firestore
Comment thread mcp-server/src/verifimind_mcp/registration.py Fixed
Comment thread mcp-server/tests/unit/oauth_fakes.py Fixed
creator35lwb-web and others added 2 commits September 1, 2026 00:36
Alton selected Brevo (2026-09-01, cost-control lens: 300/day free SMTP
relay, no card, EU processor) - decision point 1 of Design v2 closed.
Privacy v2.6 section 7 and the Bahasa Malaysia mirror now name the
processor instead of the identity-time placeholder; scope unchanged
(recipient address + verification message only). Remaining identity-
time fills: publication/gate dates only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
Consumes T S155 route + D-ALTON-2026-09-01-AUTHLIB-REGISTRATION-AUTH.
Replaces the hand-written OAuth state machine with an exactly-bound
Authlib 1.8.0 core behind a thin async Starlette adapter (no Flask/
Django, no client-integration misuse), and closes every S154
BOUNDED-SECURITY-HOLD finding plus the bypasses found by an internal
attacker-position pass run before this SHA.

WP-A dependency authority
- Authlib==1.8.0 + joserfc==1.7.5 (issuer fix GHSA-r74j-q665-7rpj) +
  cryptography==46.0.1 pinned in pyproject.toml AND its requirements
  mirror; THIRD_PARTY_NOTICES.md (BSD-3-Clause + provenance);
  TestAuthlibDependencyAuthority added. The image-parity CI job reads
  exact pins from pyproject, so the real Python 3.12 image verifies
  these automatically.

WP-B Authlib protocol core
- AuthorizationServer + AuthorizationCode/RefreshToken grants, public
  clients only (auth method none), response_type code, scope mcp.
- PKCE registered as REQUIRED and constrained to S256 (stock Authlib
  permits plain). Duplicate-parameter preserving payload so Authlib''s
  multi-param rejection actually fires.
- Opaque hash-only tokens minted THROUGH the validated grant path.

WP-C invariants Authlib cannot supply (S154 P0-1..P0-10)
- P0-1 cache keyed on a digest of the COMPLETE credential + expiry cap;
  same-id/wrong-secret rejected cold AND warm.
- P0-2 duplicate-email discloses no UUID/opt-out URL on either register
  path; dashboard and opt-out require the authenticated subject
  UNCONDITIONALLY (the flag-gated version left the default dark posture
  open to bare-UUID account tombstoning).
- P0-3 minting bound INTO the transactional code claim; wrong PKCE does
  not consume; barrier test proves exactly one concurrent winner;
  refresh rotation + OTP cap transactional.
- P0-4 issuer/audience/scope stamped and re-checked (absence DENIES);
  401 wrong issuer/audience, 403 insufficient_scope with challenge.
- P0-5 one bearer path accepts ACCESS+PAT, rejects REFRESH; revoking
  any credential tombstones the family AND descendants.
- P0-6 STARTTLS with a validating ssl context; single-recipient
  enforcement (a To: list smuggled the OTP past the allowlist).
- P0-7 independent default-off OAUTH_ISSUANCE_ENABLED blocks every
  mutating OAuth/DCR/PAT/ceremony path; metadata stays readable.
- P0-8 env-bound issuer/resource/origin/collections; staging must
  declare its own origin AND any non-production K_SERVICE must declare
  its environment; account collections namespaced so staging cannot
  read/create/tombstone production accounts; PRM never falls back to
  production.
- P0-9 exact redirect membership, no fragment/userinfo, agree-checkbox
  enforced, frame-ancestors/X-Frame-Options/no-store on ceremony pages,
  last-hop XFF (rate_limiter used the caller-controlled leftmost hop),
  global + per-action mint limits, unused-client expiry.
- P0-10 verified-mailbox ceremony; peppered OTP bound to session+purpose;
  tool_completed gains route + protocol_era with an env-bound label.

Adversarial-pass repairs (each with a discriminating regression)
- PAT bound to its parent grant: a stolen access token could be
  upgraded to a silent 180-day credential surviving the victim''s
  revocation.
- /oauth/pat now runs full bearer validation, not a bare store lookup.
- Revocation tombstones consulted at validation time close both the
  cross-instance cache window and the query-then-update race.
- Pre-registration subject hijack closed: legacy register paths mark
  records unverified and never hand out an identifier (also removing
  the account-existence oracle); the ceremony upgrades on mailbox proof.
- Email canonicalized everywhere (case split forked accounts).
- Firestore RPC failures re-raised as StoreUnavailable so an outage is
  a retryable 503, not 401 invalid_token.
- Array JSON-RPC params no longer crash the boundary; oversized bodies
  replay intact.

Tests: 1,343 passed / 3 skipped locally (21 OAuth-core, 18 boundary,
20 gate, bridge, parity). The single local failure is the documented
dev-env pin refusal (installed fastapi/fastmcp/starlette/uvicorn differ;
Authlib/joserfc/cryptography match) - CI installs the pins and is the
authority. Full ceremony verified end-to-end through the real ASGI app.

Deferred and NOT claimed complete: CIMD with SSRF-hardened retrieval and
the SDK-backed MCP 2026-07-28 lane remain after CS pass 1 and the
exact-client 2025-11-25 matrix; physical-deletion TTL receipts remain a
retention follow-up.

NO merge, staging, deployment, credential issuance, policy publication,
notice, or activation. Both gates default OFF; production untouched at
v0.5.62.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
@creator35lwb-web creator35lwb-web changed the title feat(auth): registration gate for tool execution — dark by default feat(auth): Authlib OAuth 2.1 core + S154 P0 repair — dark by default Sep 1, 2026
registration_gate_enabled,
)

_PRM_URL = "https://verifimind.ysenseai.org/.well-known/oauth-protected-resource"
"code", grants authorization_code/refresh_token, PKCE S256-only.
"""

from typing import List, Optional
Comment on lines +17 to +22
from authlib.oauth2.rfc6749 import (
ClientMixin,
InvalidClientError,
InvalidGrantError,
OAuth2Request,
)
Comment thread mcp-server/tests/unit/oauth_fakes.py Fixed

import base64
import hashlib
import re

warnings.filterwarnings("ignore", category=DeprecationWarning)

from verifimind_mcp.oauth import config, core, stores
Independent CS review (Antigravity, exact head 2f44433) returned
BOUNDED-HOLD with four findings; the crypto core, tombstoning, PKCE
S256-only, and OCC token issuance were verified holding. Each finding
repaired with a discriminating regression (tests/unit/test_cs_review_findings.py):

- F1 dark-issuance completeness: the legacy POST /register and POST
  /early-adopters/register handlers lacked the issuance gate, so a
  public caller could still write Firestore records while dark. Added a
  _registration_dark_response() guard to both (scoped to account
  issuance; the feedback channel is not an issuance path). Dark -> 503,
  zero write; issuance-on -> proceeds.
- F2 anonymous-registration orphaning: the oracle fix returned uuid=""
  for EVERY new registration, orphaning the maximum-privacy no-email
  user who then had no way to recover their identifier. register_user
  now branches on email: absent -> return the UUID (no email = no
  oracle/hijack surface); present -> withhold (uniform, oracle-safe).
- F3 subject-hijack data injection: _resolve_or_create_subject adopted
  an unverified record's attacker-injectable display_name/name/feedback
  on mailbox proof. Those fields are now wiped when an unverified record
  is upgraded to verified; the UUID is adopted for continuity but no
  caller-set data survives the verification boundary.
- F4 environment-isolation leak: registration_gate._denial_payload used
  a hardcoded production PRM/register URL, pointing a staging client at
  the production authorization server (lockout loop). Now resolved from
  config.current_environment(); a misconfigured env omits the PRM rather
  than leaking production.

Tests: 1,351 passed / 3 skipped locally (8 new CS-finding regressions);
sole local failure remains the documented dev-env pin refusal. Live
smoke through the real ASGI app confirms dark blocks the write, the
anonymous path returns its UUID, and the email path withholds.

Back to independent CS for safe-to-stage re-review at the new exact
head. No merge, staging, deployment, credential issuance, policy
publication, notice, or activation. Both gates default OFF; production
untouched at v0.5.62.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
Comment thread mcp-server/src/verifimind_mcp/middleware/registration_gate.py Fixed
Comment thread mcp-server/src/verifimind_mcp/middleware/registration_gate.py Fixed
creator35lwb-web and others added 2 commits September 2, 2026 22:42
…nse evidence (T S156)

Bounded gate-truth repair against exact parent 7a0c462
(T S156 brief, D-156-1..7; activated by Alton 2026-09-02). One new SHA; no merge,
staging, deployment, activation, routing, billing, pricing, policy, notice, or
release-identity change.

registration_gate._denial_payload: the two complementary `if` statements become one
exhaustive if/else so `error`/`hint` are provably bound on every path - the
github-advanced-security "CodeQL" results check at 7a0c462 FAILED with two
py/uninitialized-local-variable errors (lines 206-207) plus eight notes.
`_env_urls()` is hoisted above the decision (pure; already unconditional). Both denial
payloads are byte-identical to 7a0c462 (1,656-case differential, 0 mismatches;
`_env_urls` exactly once per call); any non-cross-subject reason still resolves to
authentication-required.

tests/unit/test_gate_truth_denial_payload.py (16): exact payload pins; every reason
binds error/hint; environment binding for BOTH payloads; the REAL `_env_urls` fallback
path never advertises a production PRM (class sweep: registration_gate.PRM_URL must
stay dead); a definite-assignment structural pin (one `if`, terminal `else`, every arm
definitely binds both - FAILS on 7a0c462, if/elif-no-else, nested-if, pre-init mask;
PASSES on sound tuple/annotated refactors); CPython LOAD_FAST_CHECK oracle (3.12+).

THIRD_PARTY_NOTICES.md: verbatim LICENSE bytes for Authlib 1.8.0, joserfc 1.7.5 and
cryptography 46.0.1 (LICENSE / LICENSE.BSD / LICENSE.APACHE) with SHA-256 receipts
bound to the PyPI artifact digests (wheel + sdist, verified), PEP 740 Trusted-Publisher
attestations (authlib/authlib pypi.yml; authlib/joserfc pypi.yml; pyca/cryptography
pypi-publish.yml), and the authlib v1.8.0 tag commit 1a86748b.
tests/unit/test_third_party_notices.py re-verifies every block byte-for-byte against
the installed pinned distribution (duplicate/stale blocks rejected).

The eight notice-level CodeQL notes are inspected and left untouched; their bounded
disposition is recorded in the PR body. No authentication, authorization, routing,
provider, billing, pricing, release-identity, or activation semantics changed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
…und XFF trust (CS round 2)

Bounded availability repair against exact parent d125bce,
activated by Alton 2026-09-03 on the RNA S158 record. One new SHA; no merge, staging,
deployment, activation, routing, billing, pricing, policy, notice, or release-identity
change. No authentication or authorization semantics change.

F1 (OOM): McpAuthBoundary._gated_call_peek buffers at most _MAX_PEEK_BODY. Once the cap is
crossed it stops reading, marks the request protected, and the replay SPLICES - buffered
messages first, then ALWAYS the live receive - so an authenticated caller's large body
arrives intact (the S155 truncation concern) while an anonymous caller can never make the
process buffer more than the cap; the 401 goes out with the remainder unread (safe under
the pinned uvicorn 0.52.4: remainder discarded, keep-alive preserved). The same hand-over
removes a pre-existing S155 defect found by the F1 adversarial lens: an endless empty
http.request sentinel after the buffer made the SSE disconnect listener spin and froze the
event loop on any anonymous non-gated initialize in execution mode.

F3 (500 -> 503): stores.is_backend_failure walks __cause__ then __context__ (bounded) - the
Firestore transactional wrapper raises a plain ValueError *from* the last Aborted after
retries, and when BeginTransaction itself fails its rollback path raises
ValueError('...cannot be rolled back') chained only via __context__ (the F3 lens refuted
the first, cause-only repair on exactly that shape, end-to-end 500 through the real token
handler). One _guarded(op) decorator maps backend failures (google.api_core, google.auth,
grpc) to StoreUnavailable on _read, run_transaction, and every raw Firestore write/query;
application signals and programming errors pass through. The authorize ceremony renders the
honest 503 on any store outage (no more "invalid code" / "session expired" on outage),
cleanup drops are best-effort, and _resolve_or_create_subject maps backend failures to the
existing 503.

F2 (XFF trust depth): new utils/client_ip.py resolves the client with an explicit
TRUSTED_PROXY_HOPS (default 1 = the element the Google Front End appends last; 2 behind a
Google external Application LB, proven by the ingress receipt), shared by
rate_limiter.get_client_ip and oauth.endpoints._client_ip; a chain shorter than the trust
depth falls back to the direct peer, never a caller-supplied element; the startup banner
discloses the configured depth. Production receipt 2026-09-02: domain mapping via
ghs.googlehosted.com, 0 forwarding rules, 0 serverless NEGs -> depth 1. Staging must
re-bind to its own ingress.

tests/unit/test_cs_round2_availability.py (49): real-wrapper Firestore shapes over a
scripted GAPIC surface, chain-walk pins, a class sweep over ten raw store calls, ceremony
outage -> 503, bounded peek + intact splice + SSE hand-over, and the trust-depth matrix;
proven 33 failed / 16 passed against the d125bce modules and 49/49 on this head.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
Comment thread mcp-server/http_server.py Fixed
Comment thread mcp-server/src/verifimind_mcp/oauth/endpoints.py Fixed
Comment thread mcp-server/src/verifimind_mcp/oauth/endpoints.py Fixed
creator35lwb-web and others added 3 commits September 3, 2026 14:51
…s not classified as a secret

Second child SHA on top of d021f8c (parent d125bce). Pushed because the set-bound
check-suite read at d021f8c found the github-advanced-security "CodeQL" results check
red: py/clear-text-logging-sensitive-data (high) on the new startup-banner line. CodeQL's
sensitive-name heuristic (maybeSecret: "...|(?<!un|un_|is|is_)trusted(?!_iter)|...")
classifies any identifier, env-var name, or dict key containing "trusted" as a secret, so
disclosing TRUSTED_PROXY_HOPS - an integer 1..8 - tripped it. Two py/empty-except notes on
the best-effort session drops were introduced by the same commit.

No behaviour change: TRUSTED_PROXY_HOPS -> INGRESS_PROXY_HOPS (env var, function names,
dict key, documented in the module with the reason), and explanatory comments inside the
two except bodies. Tests and the banner text follow the rename.

The activation contract's "one new exact SHA" is therefore two SHAs; the deviation and its
cause are disclosed in the PR body and the Hub record. Nothing was asserted green at
d021f8c - the poller caught the red row before any evidence was written.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
…ronment isolation (CS round 3 / T S157)

WP-A - refresh revocation completion (T S157 Finding 1; CS round 3 F1):
- validate_refresh denies grant / parent-grant / subject tombstones after
  secret verification, the same coverage bearer validation already had.
- rotate_refresh_tokens re-verifies the presented secret INSIDE the
  transaction, derives grant / parent grant / subject from the persisted
  old-token record, refuses any caller identity that disagrees (and an empty
  persisted grant or subject), reads the three tombstone documents THROUGH
  the transaction before marking the old token or writing descendants,
  checks kind/expiry, and writes descendants under the persisted identity
  WITH the parent link. A revocation that lands between those reads and the
  commit either conflicts the commit (retry re-reads, sees it, denies) or
  waits for it; in both orderings the descendants carry the tombstoned
  grant/parent id and every validation path refuses them.
- Denials raise RefreshRejected, which save_token maps to Authlib's
  invalid_grant (400) - a denial, never a retryable 503. A replayed rotated
  token still revokes the whole family (RFC 9700 s4.14.2) and is then denied
  the same way (was: a 503 that invited retries against a dead family).
- Found by the S160 adversarial lens, HIGH at fecc67c: validate_refresh and
  contain_refresh_reuse bound the token KIND to the caller-controlled wire
  prefix, so an ACCESS token rewritten vmat.->vmrt. resolved to its own record
  (same id, same secret, one shared collection) and rotated into a 30-day
  refresh family. Both now gate on the PERSISTED kind; the rotation
  transaction re-checks it. Unreachable in production today (endpoints dark).
- Token format, hash-only storage, bearer/PAT behaviour, and rate limits are
  unchanged.

WP-B - environment isolation completion (T S157 Findings 2/3):
- registration.account_collection no longer swallows EnvironmentMisconfigured:
  a misdeclared staging identity stops BEFORE any client or I/O and never
  falls back to a production collection name.
- Every user-data reader/writer resolves its collection through that one seam
  first: register_early_adopter, register_user, get_ea_status, process_optout,
  submit_feedback (both feedback write sites), registration_lookup,
  rate_limiter UUID tier resolution, trinity_history read + write.
- http_server: EnvironmentMisconfigured -> explicit 503 service_misconfigured
  (Retry-After, no-store, no environment detail, "nothing was stored") on
  every HTTP surface; /register re-raises it instead of a generic 500.
- Production and local development keep the bare historical names; only a
  declared staging is prefixed. Anonymous feedback stays admitted while
  issuance is dark (T D-157-5). The quarantined coordination store is
  untouched.

Test fake: transactions now refuse a read after the first buffered write,
as the real client does (ReadAfterWriteError), so a reorder that would 500
in production fails here instead of passing.

Regressions - tests/unit/test_cs_round3_security.py (44): 33 discriminators
FAIL on the fecc67c tree (sparse worktree, imports bound) for their stated
reasons, including the deterministic two-party barrier race (tombstone
commits in the rotation's pre-commit window -> conflict -> retry -> deny,
nothing written) for grant, parent-grant, and subject tombstones, the
concurrent double-refresh, the re-prefixed access/PAT tokens, and every
misdeclared-staging surface (zero reads, zero writes, client never asked
for); 11 pins pass on both heads (incl. production callers keep bare names,
anonymous feedback admitted while dark, an outage stays an outage). Local
gates on this tree: unit 1,382 passed / 3 skipped / 1 documented dev-env
pin-parity failure; registration 84; integration 7 / 11 network-skipped;
Bandit clean at CI flags.

No merge, staging, deployment, authentication-gate activation, routing,
billing, pricing, policy, notice, or release-identity change. Parent:
fecc67c.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
…-only feedback attribution, uniform lane receipts, honest owner-route outages (CS round 4 / T S158)

Contract 1/2 - canonical verified identity + consent provenance (T S158 F1):
- One owner per email across BOTH registration lanes: a hash-keyed claim
  document (collection base "email_owners", environment-resolved) created
  with Firestore's atomic DocumentReference.create(); exactly one caller can
  ever create it. Both lanes scan both account collections for a legacy
  record first, then claim, then write the account record create-if-absent
  (a lane write can never clobber a record the ceremony healed); the loser
  receives the same uniform receipt.
- The verified resolver (registration.resolve_verified_subject, called by
  oauth/endpoints) adopts through the claim, backfills legacy records into
  claims, heals a claim whose record write never landed (lane-default tier),
  creates a new verified subject claim-first, releases a claim that names a
  subject-tombstoned identifier and mints fresh, and fails closed on a
  malformed claim or an unusable legacy record. Collections resolve BEFORE
  the client (closes the round-4 ordering residual).
- Mailbox proof is a state-transition sanitizer, not retroactive authorship:
  adoption neutralizes every caller-chosen field (profile, inline feedback,
  updates_consent -> False, tier -> lane default, pilot_source), stamps the
  verified actor's OWN Terms/Privacy acceptance, consent_source and
  registration moment (the earlier value kept as provenance), records what
  it neutralized, and detaches feedback linked before proof. Registration-
  path feedback is written with uuid=None and an explicitly unverified link.
- process_optout releases the mailbox's claim after tombstoning the subject,
  so an opted-out address can register again and no email hash lingers.
- Cohort (pioneer) rate tier requires a verified record; an explicitly
  unverified preregistration earns none (legacy records keep theirs).

Contract 3 - feedback attribution (T S158 F2):
- http_server._bearer_subject classifies absent / invalid / ok and lets
  StoreUnavailable, EnvironmentMisconfigured and programming errors
  propagate (no broad except); ea_feedback_handler returns 401 for a
  present-but-invalid Bearer and 503 for an outage, storing nothing; the
  authoritative uuid is the validated bearer subject or None, and a body
  uuid is kept only as claimed_uuid_unverified. Anonymous feedback stays
  admitted (T D-157-5).

Contract 4 - enumeration resistance (T S158 F3):
- One uniform receipt per email-bearing lane for new AND existing addresses
  (message, flags, tier, URLs, availability); the EA lane records feedback
  for both cases (unverified) so feedback_received is truthful and identical.

Contract 5 - availability truth (T S158 F4):
- Dashboard and opt-out answer a credential-store outage with a retryable,
  non-enumerating 503 (Retry-After, no-store; an honest outage page) and
  perform no history read or deletion; missing / invalid / wrong-subject
  credentials remain 401.

Test fakes: FakeDocRef.create() raises AlreadyExists like the real client;
FakeSnapshot.id. Legacy assertions follow the write primitive (set -> create);
the hand-rolled opt-out fake gains delete().

Regressions - tests/unit/test_cs_round4_identity.py (39): 32 discriminators
FAIL on the 2c43cd5 tree (sparse worktree, imports bound) at their stated
first assertion — including two real interleavings (a competitor claims
inside the lane's and the ceremony's window), a one-atomic-create-and-
nothing-else pin, the opt-out/purge lifecycle, the heal-vs-set clobber, and
the timestamp-echo oracle; 7 pins pass on both heads. Two adversarial lenses
ran before this commit and their refutations are folded in. Local gates:
unit 1,421 passed / 3 skipped / 1 documented dev-env pin-parity failure;
targeted 418; integration 7 / 11 network-skipped; Bandit clean at CI flags;
all 167 round-4 controls retained.

No merge, staging, deployment, authentication-gate activation, provider
routing, credential/spend, billing, policy, notice, or release-identity
change. Parent: 2c43cd5.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
Comment thread mcp-server/tests/unit/oauth_fakes.py Fixed
…e, resumable erasure, honest uniform storage outages (CS round 5 / T S159)

One bounded repair at exact parent 68daa5c
answering all four T S159 round-5 blockers. No merge, staging, deployment,
activation, provider routing, credential/spend, billing, policy, notice, or
release-identity change; both gates stay dark.

F-01 (HIGH) Refresh containment. save_token's second validate_refresh
    returned None for a token rotated after grant-authentication and raised
    invalid_grant without containing the race winner. The save-time None
    branch now mirrors the first-validation branch: contain_refresh_reuse
    revokes the grant family before invalid_grant. Containment fires only
    for a secret-valid already-rotated token (wrong-secret/unknown revokes
    nothing); a backend outage on either read raises StoreUnavailable and
    propagates (503), never collapsing into a denial.

F-02 (HIGH) ABA-safe ownership. The resolver released a tombstoned owner
    claim with a bare delete(), so two verified ceremonies could each erase
    the other's replacement and fork one mailbox into two verified subjects.
    _reclaim_tombstoned_owner reads the claim and installs the fresh subject
    in ONE transaction, bailing (re-read, adopt the winner) if the claim no
    longer names the tombstoned uuid it read; the fresh subject is minted
    inline through it, create-if-absent. _release_owner_claim deletes only
    while the claim still names the expected subject.

F-03 (MEDIUM) Resumable erasure. Opt-out scrubbed PII and revoked the bearer
    before deleting the claim; a late delete failure left the caller revoked
    (401 on retry), the claim present, the key gone, and a later retry
    reporting success with the claim still there. New order, by
    reversibility: de-identify; release EVERY claim naming the subject by a
    reverse lookup on the claim's own uuid field (durable cleanup identity
    that survives the scrub and also cleans the inherited pre-repair state);
    tombstone + revoke LAST. Success is tombstone-authoritative: once the
    marker every validation path consults has landed, a failed hygiene sweep
    no longer denies an erasure that happened. Scrub-before-release closes
    the release-to-tombstone window by construction.

F-04 (MEDIUM) Honest, uniform storage outages. Under reads-up/writes-down an
    existing address performed no write (201/200) while a new address hit
    claim_email().create() and escaped as 500 -- an existence oracle. Every
    email-bearing submission now performs one owners-collection write (new:
    create; existing: backfill a missing legacy claim, or re-assert an
    invariant field on the existing claim, which stores nothing new), so an
    outage fails both identically; both lanes then raise
    RegistrationStoreUnavailable and the handlers render ONE honest,
    retryable 503 (persisted:false, uuid "", Retry-After, no-store). A
    persistent quota or permission failure is loud, never a silent success
    (F-RES-1 preserved; the db-is-None construction branch keeps its
    ratified shape). Disclosed residual: a failure confined to the account
    collection alone remains asymmetric by construction and is not a
    realistic project-wide failure mode.

Adversarial lenses before this commit (separate agent contexts, read-only):
Lens A held F-01/F-02/F-03 across optimistic-fake and pessimistic-lock
models, warm-cache and PAT descendants, and every opt-out failure point,
and REFUTED the first F-04 draft (a uniform-success receipt on a write
outage claimed persistence that did not occur and would have turned quota
exhaustion into silent success) plus two LOW F-03 residuals (post-tombstone
sweep failure reported as failure with a dead bearer; a residual claim
after a ceremony in the release window with no reverse lookup) -- all
redesigned here. Lens B audited the tests: two incidental old-head
failures and six surviving mutants -- the suite was rewritten with
whole-body receipts, real-bearer proxies, known-positives, a
status-not-exception client, and its verified candidates; the final
18-mutant matrix has no survivor.

Tests: tests/unit/test_cs_round5_identity_availability.py (24) -- 18 fail
at 68daa5c at their stated first assertion, 6 pins pass on both heads;
unit 1,445 passed / 3 skipped / 1 documented dev-env pin-parity failure;
required targeted set + round-4 controls 206 passed; integration 7 passed /
11 skipped; Bandit: no new findings. Test fake: create() now passes a
store-level write gate before its existence check (as the real commit
does) and update() on a missing document raises NotFound; the legacy
opt-out fake speaks the transaction protocol.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
transaction refuses a read after its first buffered write.
"""

from typing import Any, Callable, Dict, Optional
Comment thread mcp-server/tests/unit/test_cs_round5_identity_availability.py Fixed
Comment thread mcp-server/tests/unit/test_cs_round5_identity_availability.py Fixed
Comment thread mcp-server/tests/unit/test_cs_round5_identity_availability.py Fixed
Comment thread mcp-server/tests/unit/test_cs_round5_identity_availability.py Fixed
…edback write, tombstone-authoritative completion (CS round 6 / T S159)

One bounded repair at exact parent 84fd926
answering all three T Round-6 blockers. No merge, staging, deployment,
activation, provider routing, credential/spend, billing, policy, notice, or
release-identity change; both gates stay dark.

R6-01 (HIGH) Terminal erasure vs stale writers. The legacy read, the subject
    tombstone check, and the claim write were three separate operations in
    both the registration lanes and the verified resolver, so a caller that
    already held a live legacy subject could create a claim naming it AFTER
    opt-out's reverse-query sweep and tombstone returned success: a query is a
    snapshot and cannot exclude a later writer. Erasure now commits an ERASURE
    SEAL before sweeping the claims, and every existing-subject claim create /
    backfill / reassert goes through one transaction that reads the seal AND
    the subject tombstone through itself (_assert_claim_for_existing_subject).
    Both markers enter the transaction's read set, so a concurrent erasure
    either conflicts the commit -- the retry then sees the marker and refuses
    -- or waits for it, in which case the sweep that follows the seal removes
    what the writer wrote. Either way erasure wins. The seal is read by no
    credential validation path, so writing it before the sweep does not kill
    the caller's bearer and the operation stays resumable. A brand-new subject
    still uses the plain atomic claim_email(): an identifier no one has seen
    cannot already be erased.

R6-02 (MEDIUM) Receipt truth vs partial persistence. The feedback add was the
    last write inside the fatal boundary, so a feedback-only failure returned
    the storage-outage receipt -- persisted:false, "no account was created" --
    while the account holding the address AND that feedback text was already
    committed. The feedback document is auxiliary: its failure is now
    non-fatal, reported truthfully as feedback_received=false for a new AND an
    existing address alike (so it discloses nothing), and the account stands.
    Every remaining 503 therefore comes from the claim or account write, which
    makes the receipt's absence claim provable -- and the regressions assert
    the absent account, not merely response equality.

R6-03 (MEDIUM) Tombstone-aware completion. Success after a failed hygiene
    sweep depended on an IMMEDIATE confirmation read; when that read failed
    too, the caller was told the erasure had not happened and to retry with a
    bearer the committed tombstone had already killed. stores now separates
    write_subject_tombstone() -- whose normal return IS the proof the marker
    landed -- from sweep_subject_credentials(), pure hygiene whose failure
    cannot un-do an erasure the tombstone already made effective. A failure of
    the tombstone write itself still raises: that commit is genuinely
    ambiguous, and the receipt says only that deletion could not be confirmed
    while naming the private rights channel, a continuation that does not need
    the bearer.

R6-05 (LOW, harness) The fake invoked its write gate twice per logical write;
    create()/update() now pass the gate exactly once and mutate through an
    ungated _apply_write, so a one-shot fault injection fires once, as one
    commit RPC would.

Deferred, recorded not fixed (T's instruction): R6-04, _count_tier_slots()
converting an aggregate-count exception to zero, so a count-only failure can
exceed the cohort cap. It is an availability/data-integrity issue with no
staging credential or privacy effect and belongs to its own bounded lane.

Tests: tests/unit/test_cs_round5_identity_availability.py 24 -> 30. At the
exact parent 84fd926: 6 fail / 24 pass. Four fail behaviourally at their
intended first assertion -- a claim naming the erased subject survives a
completed opt-out (registration lane and verified resolver), 503 instead of
201 for the feedback-only case, and processed=false after a committed
tombstone -- and two fail because the seal primitives do not exist there.
Retained: all 230 targeted controls (236 with the new tests). Unit 1,451
passed / 3 skipped / 1 documented dev-env pin-parity failure; integration 7
passed / 11 skipped; Bandit no new findings. Also cleared the four CodeQL
test-style notes S162 disclosed and deferred.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
)
feedback_stored = True
except Exception as exc: # noqa: BLE001
from .oauth.stores import is_backend_failure
type(exc).__name__,
)
except Exception as exc: # noqa: BLE001
from .oauth.stores import is_backend_failure
Comment thread mcp-server/src/verifimind_mcp/registration.py Fixed
# lane's honest not-saved contract rendered as a retryable 503.
logger.warning(
"Lightweight registration storage unavailable (error_type=%s)", type(exc).__name__,
)
# not-saved behaviour (re-raised to the handler).
if is_backend_failure(exc) and data.email is not None:
logger.warning(
"Lightweight record storage unavailable (error_type=%s)", type(exc).__name__,
return ops


_WRITE_OPS = {"create", "set", "update", "delete"}
…ils closed (S163 lens, HIGH self-regression)

Corrective commit to the CS round-6 repair. My own adversarial lens, run
against cedc24b before handing the head to round 7, refuted part of it.
Disclosed plainly: this bounded repair therefore took TWO commits rather than
the one the GO asked for. The alternative was to hand the independent seat a
head carrying a HIGH regression I already knew about, which is worse.

HIGH -- an interrupted erasure was forked into a fresh subject, stranding the
    original. cedc24b introduced the erasure seal by WIDENING one shared
    predicate (_subject_revoked -> seal OR tombstone). That predicate also
    gated the resolver's reclaim decision, which asks a different question:
    not "may I bind new state to this subject?" but "is this mailbox free
    again?". An erasure interrupted after the seal -- its claim release
    failed, so the caller keeps a live bearer and is expected to retry --
    then looked revoked to that branch. The next sign-in minted a FRESH
    subject with a new active account carrying the address in clear, and
    stranded the original: never tombstoned, credentials alive, unreachable
    from the owner route. At the round-6 parent the same state failed closed.
    That is a regression, and it defeats the resumability the seal exists to
    preserve.

    Split the predicate. _subject_revoked keeps seal-or-tombstone and answers
    "may I bind new state to this subject?" -- used by the transactional claim
    guard and the legacy-backfill refusal. New _subject_erasure_complete is
    tombstone-only and answers "is this mailbox free again?" -- the only
    predicate that may trigger a reclaim. A seal without a tombstone now fails
    closed with an explicit log, exactly as the parent did, and the caller
    finishes the erasure with the bearer that state deliberately left alive.

LOW -- write_subject_tombstone("") returned normally while _write_tombstone
    silently dropped the empty key, so "a normal return is the proof it
    landed" was false for that input -- precisely the contract R6-03 rests on.
    It now raises. Unreachable through the HTTP route today.

LOW -- subject_is_sealed_or_revoked had no empty-uuid guard where its
    non-transactional twin does, so a careless call site could read two absent
    markers and conclude "not erased" for a subject that does not exist. It
    now refuses, mirroring subject_is_erased.

LOW -- the hygiene-sweep handler caught bare Exception, swallowing programming
    errors behind a warning about a backend condition. Narrowed to
    StoreUnavailable; sweep_subject_credentials is guarded, so a real bug
    propagates again.

Not changed, and re-disclosed rather than certified: the account write exists
only on the new-address branch, so an account-collection-only outage still
separates new from existing (201 vs 503). The lens confirms this is identical
at the round-6 parent -- pre-existing residue, already on T's own deferred
list -- but cedc24b's framing ("every remaining 503 comes from the claim or
account write") asserted it away. It is a residual, not a closed item.

Tests: 30 -> 33. The new discriminator fails at cedc24b with "an unfinished
erasure handed the mailbox to a fresh subject"; two pins cover the finished-
erasure side (still reclaims, as S161/S162 established) and the empty-subject
refusals. The F-02 interleaving gate now wraps BOTH erasure predicates with
one shared counter, so the seam survives however the branch is spelled at
68daa5c, 84fd926 and here. Differential vs 84fd926: 8 failed / 25 passed.
Targeted set 239 passed (all 230 round-6 controls retained); unit 1,454
passed / 3 skipped / 1 documented dev-env pin-parity failure; integration 7
passed / 11 skipped; Bandit no new findings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
Non-transactional: a caller that also WRITES a claim naming this subject
must use ``_assert_claim_for_existing_subject`` instead, which re-reads
both markers inside the transaction that performs the write."""
from .oauth.stores import subject_is_erased
…lose four surviving mutants (S163 lens B)

Tests and comments only. No source change; the repaired behaviour at
07973b4 is unchanged, and every one of its 239 targeted controls still
passes.

My second adversarial lens audited the regression file and found that the
evidence I was about to hand the independent seat contained statements that
are not true. Correcting them is not optional, so this is a third commit --
disclosed, like the second.

FALSE CLAIMS CORRECTED
  * The module docstring said "tests marked pin: pass at both heads". Three
    tests labelled pin: FAIL at 84fd926 with AttributeError, because they
    touch store primitives this repair introduces. The docstring was
    therefore false about its own contract. Those three are now labelled by
    the head they discriminate against, and the docstring states plainly
    that a symbol-absence failure proves an addition, not a behaviour.
  * I claimed the 84fd926 differential was "six behavioural / two symbol".
    Measured, it is FIVE behavioural and THREE symbol-absent -- and one of
    the five (the interrupted-erasure test) fails there on its seal-state
    precondition rather than on its own stated outcome, so it is relabelled
    to cedc24b, the head where it fails at the intended assertion. Every
    artifact that repeated the wrong split is corrected with it.
  * test_anonymous_write_outage_is_not_a_success_receipt asserted only
    `status != 200` -- satisfied by an uncaught 500, which is exactly what
    the anonymous lane returns. A negative assertion was concealing the
    behaviour inside a class named "a storage outage is neither an existence
    oracle nor a false success". Renamed to what it proves, and it now
    asserts the 500 explicitly as the disclosed availability-truth residual
    it is, plus the property that actually matters: no phantom identifier.
  * _same_receipt's docstring claimed "every field" while skipping
    content-length whenever a field is dropped. Docstring now says which
    comparison runs and why.

FOUR SURVIVING MUTANTS CLOSED (each verified to fail with the mutation
applied and pass without it; tree restored byte-identical after each)
  * M4/M5 -- the transactional guard must read BOTH markers, and nothing
    distinguished them: the interleaving tests run a complete opt-out inside
    the writer's window, so the tombstone alone conflicts it and dropping
    either read still passed. Both one-marker states are reachable in
    production: an erasure interrupted after the seal leaves a seal with no
    tombstone, and revoke_all_for_subject -- plus every marker written
    before this repair -- leaves a tombstone with no seal.
  * M12 -- the committed-tombstone test failed only reads keyed subject_,
    while subject_is_erased reads erasure_ first, so a reinstated
    confirmation read would have been satisfied by the seal and the test
    would still have passed. Failing every marker read pins the contract:
    after the tombstone write returns, nothing is read.
  * M15 -- every owners write the count assertion sees goes through a
    transaction, single-gated before R6-05 too, so the fake's own fidelity
    change was unverified by its own suite.

Tests 33 -> 36. Differential vs 84fd926: 10 failed / 26 passed (was 8/25);
seven behavioural, three symbol-absent. Targeted set 242 passed, all 230
round-6 controls retained. Unit 1,457 passed / 3 skipped / 1 documented
dev-env pin-parity failure.

Lens B's remaining open items are recorded, not fixed: the slot-cap check
raises and returns 0 in every test because the fake has no count(), so the
cap is unenforced throughout the suite (this is the harness face of the
deferred R6-04); and the account-only, seal-to-tombstone-resumability, and
resolver-reassert prefixes need a contract decision before a test can pin
them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
@sonarqubecloud

sonarqubecloud Bot commented Sep 8, 2026

Copy link
Copy Markdown

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