feat(reporting): ingest receipts with authenticated durable replay - #1188
Conversation
Persist exact mixed-batch ordinals, immutable financial evidence and private captured status inputs through the existing account-locked materializer store. Bind authenticated MCP/A2A receipt ingress to lossless whole-request identity. Refs #1167 B2.2 of 4 within B2 of B1/B2. Frozen feed, versioned public status and production capability activation remain gated on B2.3 and B2.4.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — additive B2.2 receipt-ingress slice, no blocking findings.
What I checked:
- New
adcp.reporting.receiptsmodule, 405-line ingestion SQL, and server integration (a2a_server, mcp_tools, serve, idempotency) — all high-risksrc/adcp/server/**files are(modified), but the reviewer found no medium-or-higher concern on any of them, so the modifications are presumed contract-preserving. - Commit correctly typed
feat(reporting):with no public-surface break, so the semver signal holds. - No discriminated-union fallback removal, no type-layering violation (no direct generated imports), no generated-code hand-edits.
- No credentials in
ctx_metadata; auth resolver, idempotency bypass, per-ordinal durability, and SQL corruption-detection predicates all fail-closed. - CI gates remain armed (no disabled tests, no blanket ignores).
Medium findings (1 — non-blocking):
- Observability gap: a bare
exceptfolds durable-path failures into an opaque retryable code with no server-side capture. Worth tightening in a follow-up, but a single medium with no escalation category (not data-loss/schema/infra) falls through to approve.
Decision-table walk: no critical/high (row 1 no), gated_paths false (row 2 no), no (deleted) high-risk reason (row 3 no), the single medium is not data-loss/schema/infra category (row 4 no), high-risk (modified) present but only 1 medium — row 5 requires a medium, and it fires... re-check: row 5 = high_risk true AND (modified) present AND any medium. The one medium finding is on the durable receipt path. However it was posted inline against the receipts module code, not the modified src/adcp/server/** files. Since it is not attached to a modified high-risk file and is a single non-category medium, this falls through. No no-auto-approve team, fewer than 3 mediums → row 9 approve.
…verage Two owned C1 evidence gaps had no executed coverage at the candidate head. Literal-SQL relationship negatives substituted absent IDs rather than existing rows owned by another valid target, and the damaged-chain matrix built revision receipts only, leaving the adjustment-specific adjusts_reporting_revision_id predicate unexercised. No production defect was reproduced and no production code is changed. - Add foreign_targets(): a second complete, wholly valid obligation, official revision, materialization and adjustment in the next schedule period, plus a second consumer holding its own artifact for the caller's revision. - Assert existing-but-foreign account/consumer/obligation/revision/ materialization/adjustment references fail indistinguishably from absent records across memory and PostgreSQL in both notification modes, with no receipt, caller-feed or capture write and identical durable replay. - Pin the exact rejecting literal-SQL predicate per reference so this slice's tuples are attributable rather than merely covered by the inherited reconciliation guards. - Extend damaged_chain() to adjustment receipts and add memory and literal-SQL fork/cycle/gap/cross-target/accepted-predecessor/ disconnected-cycle cases. The adjustment chain key binds only reporting_adjustment_id, so a divergent official target stays inside the requested chain and no fork, gap or leaf-count rule can substitute. - Add function-body schema damage to the fail-closed migration matrix. Every object stays present and enabled while a financial predicate body is silently replaced, which dropped objects and disabled triggers cannot represent. Refs #1167 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean pass, no blocking or medium findings.
Reviewer reports a clean pass. The delta is test-only (three files under tests/conformance/reporting/) — no source, wire shape, or public surface changes, so no semver signal is required. Verified: migration damage cases replace real SQL function bodies (signatures match exactly, so CREATE OR REPLACE rewrites in place rather than overloading), foreign-target tests assert exact SQL predicate messages and byte-identical errors for absent vs. misowned rows, and the adjustment-chain cross_target fault branches correctly on adjusts_reporting_revision_id.
Decision-table walk:
- Rows 1: no critical/high findings.
- Row 2:
gated_pathsis false (andreview_decisionis APPROVED anyway). - Row 3: high_risk is true, but no
(deleted)reasons — all four server files are(modified). - Rows 4/5: no medium findings, so no escalation trigger.
- Row 6: prior decision was
approve, notescalate. - Row 7: no no-auto-approve team match.
- Row 8: fewer than three medium findings (zero).
- Row 9: approve.
high_risk fired only on modified src/adcp/server/** files, but the reviewer surfaced no medium-or-higher concern there, so the modifications are presumed safe. Prior run was also a clean approve on a different head SHA; this run remains clean.
…te predicate The six foreign-target cases held account_id fixed, so no literal-SQL case substituted a wholly valid target owned by another account. Store-level and mounted authorization denials cover a different boundary and never reach an INSERT, so they are not evidence for the write predicate itself. No production defect was reproduced and no production code is changed. - Add foreign_account() and extra_materialization(): one more complete, valid account with its own configuration, obligation, official revision, verified materialization and adjustment, plus a distinctly named second materialization so a foreign-account artifact reference is expressible. - Add test_literal_sql_rejects_valid_other_account_targets over the exact revision, obligation, materialization and adjustment references, asserting the exact rejecting predicate and an unchanged database image. - Narrow two comments that could be read as claiming exclusive necessity. The adjustment ownership predicate is attributable, not exclusively necessary, and the fork variant is refused by the unique successor index during owner seeding rather than at a trigger-enabled INSERT. Refs #1167 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean subsequent pass, no blocking or medium findings.
This is a subsequent review (prior outcome: approve). The delta since the last pass is test-only: two helpers (extra_materialization, foreign_account) added to tests/conformance/reporting/_receipt_support.py plus one parametrized test (test_literal_sql_rejects_valid_other_account_targets) and two doc-comment refinements in test_reporting_receipt_graph.py. The reviewer verified the foreign-account probe uses a genuinely distinct account (acct_b vs acct_a) and each fault pins the exact PG predicate message with a durable no-op-on-rejection check.
Decision-table walk:
- Rows 1: no critical/high findings.
- Row 2: gated_paths is false (and review_decision is APPROVED anyway) — no gate.
- Row 3: no
(deleted)high-risk reasons — all four high-risk matches undersrc/adcp/server/**are(modified), but this delta touches no src/ files at all. - Rows 4–5: no medium findings, so no data-loss/schema/infra or high-risk-modified medium escalation.
- Row 6: prior decision was
approve, notescalate— sticky escalation N/A. - Row 7: no no-auto-approve team match.
- Row 8: fewer than three medium findings (zero).
- Row 9: approve.
high_risk is true only as a heuristic flag on prior-touched server files; the current delta is test-only and the reviewer surfaced nothing actionable. Falls through to row 9.
CI caught `test_a_worker_that_releases_each_turn_reaches_every_accounts_generation[postgres]` leasing only `acct_a` across all nine release/release turns while the in-memory store stayed fair. The PostgreSQL store ordered candidates by `lease_expires_at NULLS FIRST` alone, and `release_period_close` clears that column, so every released generation tied at NULL and whichever tuple the scan yielded first was re-leased forever. The index comment already promised "least recently worked" ordering the query could not deliver. The failure was latent and layout-dependent, which is why it passed on earlier heads. Both stores also preferred an unheld generation over an expired one ahead of fairness, so a peer released on every turn stayed permanently NULL and outranked a generation whose worker crashed, which could then never close another period. - Add an additive, defaulted `lease_turn` column, a monotonic sequence and a distinctly named index. The name is new because `CREATE INDEX IF NOT EXISTS` would not upgrade an existing same-name index on an installed schema. - Stamp the turn on acquisition, not release: release only clears the lease, so there is nowhere else to record it. - Make the turn the primary ordering term in both stores, with expiry and the full generation key breaking exact ties, so the order is total, identical across backends and independent of physical layout or acceptance order. - Cover it behaviourally on both backends: release-each-turn rotation, a crashed generation not starved by a releasing peer, acquisition-time turn advance, key-order independence from acceptance order, and migration onto an already installed older schema with a mixed old/new writer. Rows written by older writers default to the never-leased rank, so nothing is starved. No required manifest key changes: the 187-object materializer and 102-object receipt manifests validate unchanged, and the retained-evidence migration comparison still fails if an upgrade stamps a non-default turn. Refs #1167 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean pass with no blocking or medium findings.
Checked:
lease_turnround-robin fairness rank added to period-close leasing: correct ordering (turn primary → unheld/expired NULLS FIRST → generation key), verified equivalent across SQL and in-memory stores.- Anti-starvation property for crashed low-turn generations, covered by a dedicated test.
- Additive, defaulted migration with a distinctly-named index — non-breaking.
- No public-surface/semver, wire-shape, discriminated-union, or generated-code concerns.
- No credentials-in-
ctx_metadataissues; server-path modifications preserve contracts.
Decision-rule walkthrough: no critical/high findings (row 1 n/a); gated_paths false / review_decision APPROVED (row 2 n/a); no deleted high-risk paths (row 3 n/a); no medium findings at all (rows 4, 5, 8 n/a); prior decision was approve so no sticky escalation (row 6 n/a); no no-auto-approve team match (row 7 n/a). Falls through to row 9.
high_risk is true only via (modified) server files, but the reviewer surfaced no medium-or-higher concerns on them, so they are presumed safe. Net improvement / no regressions from the prior approve.
CI caught `test_a_worker_that_releases_each_turn_reaches_every_accounts_generation[postgres]` leasing only `acct_a` across all nine release/release turns while the in-memory store stayed fair. The PostgreSQL store ordered candidates by `lease_expires_at NULLS FIRST` alone, and `release_period_close` clears that column, so every released generation tied at NULL and whichever tuple the scan yielded first was re-leased forever. The index comment already promised "least recently worked" ordering the query could not deliver. The failure was latent and layout-dependent, which is why earlier heads were green. Both stores also preferred an unheld generation over an expired one ahead of fairness, so a peer released on every turn stayed permanently NULL and outranked a generation whose worker crashed, which could then never close another period. The rank is held outside the enumerated reporting_* catalog on purpose. schema_objects() enumerates every reporting_* table plus its columns, constraints, indexes and triggers, and the A/B+C suites compare the installed set to their manifests exhaustively, so a new column on reporting_configurations or a new reporting_* table breaks exact object identity for every older binary, while adding it to a required manifest would make old installations fail validation. - Add a private adcp_reporting_configuration_lease_turns table and sequence, joined lazily and upserted in the same transaction as the acquisition, so the rank can never advance without the lease nor the lease without the rank. - Stamp the rank on acquisition, not release: release only clears the lease, so there is nowhere else to record it. - Make the rank the primary ordering term in both stores, with expiry and the full generation key breaking exact ties, so the order is total, identical across backends and independent of physical layout or acceptance order. - Cover it behaviourally on both backends: release-each-turn rotation, a crashed generation not starved by a releasing peer, acquisition-time rank advance, key-order independence from acceptance order, same-transaction commit and rollback under autocommit, inert orphan ranks, no enumerated catalog object, and migration onto an already installed older schema with a mixed old/new writer. Measured on a freshly migrated schema: the ledger to materializer upgrade preserves all 453 enumerated objects byte-identically and adds exactly 187, all reporting_materializer_*; the materializer to receipt upgrade preserves all 640 and adds exactly 102, all reporting_receipt_ingestion_*. The three private objects enumerate as zero. Because they are invisible to the manifests, readiness cannot assert them: create_schema() must run before a worker starts, and the first lease otherwise fails loudly rather than degrading silently. Refs #1167 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — subsequent pass on PR #1188, clean diff with no blocking or medium findings.
What I checked:
- No author no-auto-approve team match;
gated_pathsis false andreview_decisionisAPPROVED— no hard gate applies. high_riskis true (server files undersrc/adcp/server/**modified), but the reviewer surfaced no medium-or-higher concerns on those modifications, so row 5 does not fire.- No
(deleted)entries in high_risk_reasons, so row 3 does not apply. - Reviewer verified the delta: period-close fairness rank relocated off
reporting_configurationsinto a privateadcp_-prefixed table joined lazily at lease time; the enumeratedreporting_*catalog contract stays byte-identical (schema_objects filters onreporting_prefix); lease + rank stamp commit atomically in one transaction;FOR UPDATE OF c SKIP LOCKEDprevents rank-table race;COALESCE(t.lease_turn, 0)preserves never-leased-first fairness. - Prior decision was
approvewith no findings; this pass also has no findings — net stable.
No findings at any severity → rows 1–8 do not fire → row 9 approve.
The base branch was changed.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean subsequent pass.
No blocking findings. No medium findings. This is a follow-up to a prior approve (head 74b338d); the current run surfaces no critical/high/medium findings and no prior blockers to re-surface.
Decision-table walkthrough:
- Row 1 (critical/high): no findings — skip.
- Row 2 (gated_paths + not APPROVED): gated_paths is false — skip. (review_decision is REVIEW_REQUIRED, but the path gate does not apply.)
- Row 3 (deleted high-risk path): all high-risk reasons are (modified), none (deleted) — skip.
- Row 4/5 (medium + sensitive category / modified high-risk): no medium findings — skip.
- Row 6 (sticky escalate): prior decision was approve, not escalate — skip.
- Row 7 (no-auto-approve team): no team match — skip.
- Row 8 (≥3 medium): zero medium findings — skip.
- Row 9: approve.
The high_risk flag is true only because server/** files were modified, but the reviewer found no medium-or-higher concerns on them, so per the high_risk-flag guidance the modifications are presumed safe. Changes are comment-only production edits, rc.3→rc.6 test-fixture alignment, a fail-closed CI gate extension adding two receipt conformance lanes, plus codegen renumbering churn from integrated main.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean pass, no blocking or medium findings.
This is a subsequent review of PR #1188. The prior decision was approve; the developer pushed new commits with a small delta of two files:
.github/workflows/ci.yml— correctly fixes receipt test selection.pytest --ignoreis a no-op against explicit paths, so rolling is excluded via a bash loop with a fail-closed empty-array guard; rolling still runs in its own dedicated Postgres job. CI gates remain armed.tests/conformance/reporting/test_reporting_receipt_migration.py— formatting-only paren wrap of implicit string concatenations, no semantic change.
The reviewer surfaced no findings. high_risk is true because four src/adcp/server/** files were modified, but per the decision rules a modified high-risk file with no medium-or-higher finding is presumed safe — no escalation on the flag alone. gated_paths is false, no no-auto-approve team match, no deletions in the reasons list.
Decision table walk: rows 1 (no critical/high), 2 (gated_paths false), 3 (no deletions), 4/5 (no medium findings), 6 (prior was approve, not escalate), 7 (no team gate), 8 (fewer than three mediums) all fail → row 9 approve.
Authenticated receipt ingestion
Adds B2.2 receipt ingestion to the existing durable materializer store and the SDK's real MCP/A2A mounts. Mixed revision/adjustment batches retain immutable per-ordinal outcomes and replay the original response, timestamps and item errors after restart. Every call, including replay, resolves the authenticated account and canonical consumer again; revocation cannot be bypassed by a cached request context or either generic idempotency cache.
Raw JSON preserves exact integral values and Unicode identity before mutation. Accepted ordinals atomically commit the receipt, feed/status work, private captured input and durable result on the account-locked connection. Memory transactions restore all affected collections and sequence heads. Later materializer rejection does not erase an already accepted receipt. Receipt submission does not schedule a materializer retry.
The isolated 102-object receipt manifest is additive. Existing B 464, C 249, selector 10 and materializer 187 manifests remain unchanged. The original feature's Core lease-fairness correction uses the private
adcp_reporting_configuration_lease_turnstable and sequence, preserving historicalreporting_*catalog shapes. Deployments must runcreate_schema()before a worker; historical readiness manifests intentionally cannot certify that private table.Composition and corrections
Original feature head
74b338d81653f4f65bc1520e638da10225f199f3is preserved. Composition160291f1ccbea650c1bc41c9b1d6745562316a76has ordered parents[74b338d8, 3fd62121], followed by sole-parent correctionsc7c4f09d,c4494c63,4d13d4ab,5b23778fand624c1001. All 51 original feature paths remain; none was added or lost through composition.Current head:
624c1001ed42a210462ddb6de76806f9c1a81fc8, tree76c64bb94d8b92faed644158317bb5372b64fcb9. Cumulative composition-to-head correction: 23 files, +181/-101;git diff --binary --abbrev=8SHA256981587e5649a9722b6867545692ccb7766ba5f3b397437f4e2f7d52e200e9bb2. Full PR: 51 files, +7327/-77. The last two corrections touch only the workflow and one migration test (2 files, +29/-10); their cumulative patch SHA256 isbfc7796297b9902fd7ffd34b05c705fe4edc42cd924f7402f611fc004a2e18ad.Eleven paths overlap integrated main; only the workflow and materializer rolling helper required textual conflict resolution. The helper keeps the receipt feature's optional frozen SHA override and main's locale-neutral database requirement. Main's rc.6 contract, waiver bindings, checkpoint participation, capability ownership correction and native timestamp scheduler ordering are retained. Across the corrective children, production source changes are eight explanatory comments in four files: full ASTs and constants are identical. SQL, all five manifests and all fingerprint values are unchanged.
The required
Postgres conformance tests (Postgres 16)aggregate now needs the main PG matrix, status, materializer, receipt and receipt-compatibility jobs. It always runs and accepts only exact success from every dependency, including both receipt jobs. Its name andpermissions: {}remain intact. Both receipt jobs receive onlycontents: read. Core excludes the receipt glob during directory traversal. Receipt execution builds a Bash array from the full receipt glob, removes the rolling file before invoking pytest, and fails explicitly if the array is empty. The rolling file runs only in the compatibility job. This corrects the prior unquoted-glob plus--ignorecommand: Bash made rolling an explicit initial path, which bypassed pytest’s traversal exclusion. A new receipt file is automatically included without adding a filename to the workflow.The workflow has 17 job keys expanding to 22 jobs. Existing core/process/status 15-minute job limits, materializer 35-minute job/30-minute suite/2-minute fetch, Python 60-minute job/45-minute suite and the separate 1-minute A fetch are unchanged. Receipt jobs retain their original 35-minute job/30-minute suite budgets; compatibility fetch remains 2 minutes. The original receipt feature's private test harness preserves the pytest exit code and normal umask while isolating test scratch directories.
Frozen A/B/C/B1/B2.1 pins advance to integrated
17ee407a,0f34c666,967b6e28,5487f2bd,3fd62121. Three earlier foundation pins are unchanged. Removed obsolete C-only service flags; no locale restriction replaces them. The integrated A fingerprint and B2.1 timestamp fixes are required rather than masked by a collation pin.41 operational awaits in 15 test/helper files now execute before their assertions, including receipt replay, leasing, commits, mounted calls and process joins. Symmetric inverse AST reconstruction preserves arguments, expectations and adjacency; each selected assertion has one unconditional await, with no conditional or multiple-await promotion. No embedded source literal changed. Pure reads and
pytest.raisescancellation joins remain in place.Initial transport fixtures still negotiated rc.3 after main advanced to rc.6. Valid request fixtures, pinned-mount controls, rolling receipt requests and documentation now name rc.6; production version enforcement is unchanged. No old-version allowance or validation relaxation was added.
Validation
c4494c63, base/memory selection: 319 passed and 361 intended URL-absent skips in 38.60s. Native libcen_US.utf8PG selection: 680 passed, zero skips in 614.78s (629.60s outer), under the unchanged 900-second local ceiling. Raw PG stdout SHA256690edd81bd110ab9862c09cad3f1cbcc45320b324978848bdf49a23f62775547.4d13d4abchanges only one additional installed-probe request literal from rc.3 to rc.6. Production and the native selection's files are byte-identical toc4494c63; the native results remain attributed to that actual execution head.4d13d4ab, both current VCS/sdist installed controls passed on Python 3.10.21, including isolated receipt replay and strict adopter checks: 2 passed in 124.53s (138.62s outer). Raw stdout SHA256d9c6cebc078df8684becf48dcd1e2eb48029c685f4d75f990d81486321c4b9c1. The first private harness invocation lacked pytest on PATH and executed no test; its original diagnostic is retained separately.Normal commit hooks passed. Four long damage SQL entries now have explicit parentheses; the full test AST and all 41 constants are identical, preserving eight complete SQL statements and 16 parameter cases. No commas, SQL bytes or assertions were changed. The actual aggregate shell still rejects every combination other than five successes across 3,125 success/failure/cancelled/skipped/empty cases.
The original 2,929-identity collection was categorized by a private Python script into intended lanes; it did not prove the old workflow selectors. That claim is corrected. Actual command collection now demonstrates the defect and repair: original receipts command selects 645 cases including eight rolling; corrected receipts command selects 637 with rolling absent from its initial arguments; unchanged compatibility selects eight. These actual node-ID sets are disjoint and union-equal the full 645-case receipt collection. The empty-selector control exits 1 before pytest. Only collection ran, with the PG URL present; no test bodies or database work executed. Existing core/status/materializer/process selectors are unchanged.
Original local failures are retained:
c7c4f09dbase run 37 failed/282 passed/361 URL-absent skips exposed the stale rc.3 fixtures. Its PG run ended 104 failed/427 passed/149 errors and includes a PostgreSQL WAL panic from disk exhaustion; it does not qualify PG. Removing disposable cached SDK builds recovered space before fresh corrected runs. Original logs and cleanup incidents remain separately attributed.Original exact-
4d13d4abreceipts job107733479789in run36029237565failed with 637 passed and eight rolling-fixture setup errors. Its complete raw log SHA256 isfd74c453b3399b374f1a3fb011b565f71c43be4e27b7f07cbbe8369ef3fc1b22. The harness argv proves the rolling file was both explicitly supplied and named in--ignore. The originalb21-exact-git-archiveexit 128 has only a redacted stderr descriptor; its lower-level cause is not inferred. The separate compatibility job succeeded but does not repair the failed lane. No original retry or deadline change occurred. The original run is terminal failure: 22 jobs, 20 success and two failures (receipts and the strict aggregate, which correctly rejected RECEIPTS_RESULT=failure). Guard seal5212fb80124c336aeceaa3fff648f2ced63658bff7d8ab411e469e1f564e553bremains NOT_READY_ORDINARY_GATES; later corrections do not revise it.Complete installed PostgreSQL/process and eight frozen-artifact controls remain required in fresh exact-head CI. The two successful local public installed routes do not supply those other controls. Local native tests and catalog observations do not substitute for installed controls or whole CI job lifecycles. No historical PR/head's tests, CI, review or scan acceptance transfers.
Security, review and scope
Fresh Gitleaks 8.30.1 on this exact head scans all 51 current files (1,097,119 bytes) plus the complete eleven-commit merge-parent history (146,208,825 added bytes), with unchanged upstream rules, no repository ignore/baseline/inline allow/input-size cap. Two current-file and 86 history matches are individually bound to exact objects and adjudicated as test idempotency identifiers, schema examples or a verified file digest; zero remains unadjudicated. Large positive and negative controls passed. Report SHA256
863a8d1bf584afb273d65cdc74a7300073d2afc8b4b35e5e622ca8d71228a115. This is not a proof of universal secret absence, not GitGuardian success and not an approved scan substitute for this PR. Fresh GitGuardian, CodeQL, Ladon, source review and ordinary CI are independent gates.The original genuine CodeQL check
107733886681succeeded but reported one medium wildcard-bind alert atserve.py’s socket bind. This is a true positive about inherited documented server configuration: the eight listener/auth functions are AST-identical to main, but authentication remains optional and the receipt skill adds an opt-in body path behind that listener. Five explicit Bandit B104 suppressions explain why passing Bandit does not clear the CodeQL finding. The exposure remains recorded open; no risk acceptance, scanner dismissal or named exception has been applied. GitGuardian’s size-skipped result likewise supplies no clean-scan credit.B2.2 does not activate Managed Delivery, Reconciled Billing or the new store's Core notification advertisement. Epoch-0/quarantine and the advertisement veto remain. B2.3 owns authorized combined feeds/checkpoints/dependency closure; B2.4 owns versioned public status, ownership walks and per-offering capability proof. Financial history is not pruned or dropped for rollback.
Release notes must retain all rolling exclusions: pre-
17ee407aA, pre-0f34c666B, pre-967b6e28C, pre-5487f2bdB1, and now pre-3fd62121B2.1 are unqualified. A aggregate notification readiness after C remains intentionally closed; compatible subset readiness and draining old autonomous writers remain required. Preserve the historical catalog-cost risk without #1191 memoization credit and the inherited fail-closed pagination limitation without a snapshot-retention promise. #1172 delivery/outbox/lease work, #1199 and TS holds remain separate. Source integration is not artifact, release or activation acceptance.Refs #1167, #1168. Guard #1201 remains draft and last; legacy workflow 204238826 remains disabled.