feat(reporting): expose durable webhook activity - #1183
Conversation
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — #1168B durable webhook-activity slice reviewed clean.
The change is additive throughout: no public export removed or renamed, new worker/server kwargs are keyword-only and default to None under a feat: prefix, no hand-edits to generated code, no credential path through ctx_metadata, and forward-compat on discriminated unions is intact. The reviewer covered the largest new files (activity.py, _activity_pg.py), the _schema.py manifest rewrite, the additive SQL migration/guards, and the reservation/ACK fail-closed windows.
Medium findings
- src/adcp/decisioning/handler.py:2062 —
get_adcp_capabilitiesre-runs a full ~450-object schema catalog validation per discovery call whensupports_webhook_activityis claimed (perf, non-blocking).
Decision path: no critical/high findings (row 1 n/a). gated_paths is false (row 2 n/a). high_risk is true but all high-risk entries are (modified) with no medium finding on those files — the single medium is on handler.py, but it is a perf/LLM-context scope concern, not a data-loss/schema/infra category, so rows 3–5 do not fire. No prior escalation (row 6 n/a). No no-auto-approve team match (row 7 n/a). Only one medium finding, below the three-medium threshold (row 8 n/a). Falls through to row 9 → approve.
2b6a09a to
d4c10cf
Compare
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — durable webhook activity slice, subsequent pass.
The reviewer read every changed source file and surfaced no critical, high, or medium findings. Key invariants checked and holding:
- Worker: reservation-before-HTTP crash honesty and bounded timeouts (worker.py)
- Fail-closed durable-only capability gating (support.py / _capabilities.py)
- URL sanitization and identity-agreement guards
- Additive/immutable SQL triggers and per-object schema-manifest rewrite
- Forward-compat, import-layering, credential (ctx_metadata), and CI-gate invariants intact
- Additive semver signal correctly carried on the
feat:commit
Decision-table walk:
- Rows 1–2: no critical/high findings; gated_paths is false and review_decision is APPROVED — no gate applies.
- Row 3: no
(deleted)high-risk reasons (all(modified)). - Rows 4–5: no medium findings, so no medium+sensitive or high-risk-modified+medium trigger.
- Row 6: prior decision was
approve, notescalate— sticky rule 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 from (modified) decisioning/server files, and the reviewer found no medium-or-higher concerns on them, so the modifications are presumed safe. Prior run also approved with no findings; this pass confirms the net state remains clean.
Persist fenced HTTP attempt reservations and outcomes, project bounded activity for visible accounts and the canonical authenticated consumer, and validate durable capability wiring. Preserve legacy account/store interfaces and require the additive migration and A-worker activation barrier before enabling activity. Bound the subscription URL on its canonical form so a registration that would only fail after percent-encoding is rejected with a secret-free diagnostic rather than quarantining every later expansion. Bundle the deterministic required-object manifest and cover migration, installed wheel/sdist compatibility, process crashes, concurrent transport logging, and the existing reviewed-A binary. Keep status projection and clock sweeps for C. Refs #1168 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
d4c10cf to
198d50e
Compare
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean subsequent pass, no blocking or medium findings.
This is a subsequent review on top of a prior clean approve. The delta since the last approved head is a single test-only change: a scanner-safe JWT-shaped sanitizer fixture in tests/conformance/reporting/test_reporting_activity_projection.py that constructs an inert 8-byte ES256 JWT at runtime and asserts shape plus constant redaction. No production code changed in this delta.
The previously flagged sanitize_activity_url concern is closed in this head — routing.py now bounds and stores the httpx-canonical subscription URL with idempotent re-validation coverage.
High-risk flag: true, but only (modified) reasons on src/adcp/decisioning/** and src/adcp/server/**. Per the change-kind rules, modifications with no medium-or-higher findings are presumed safe; there is no deletion and no medium finding to trigger escalation rows 3/5.
Gate checks: gated_paths is false; review_decision is APPROVED; no no-auto-approve team match. No critical/high/medium findings.
Decision table: rows 1–8 do not fire (no findings, no deletions, no team gate, prior decision was approve not escalate). Falls through to row 9 → approve.
The base branch was changed.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — subsequent pass on PR #1183, clean.
This is a follow-up to a prior clean approve (head 198d50e). The delta since then is dominated by main-merge churn (already-reviewed #1178 reporting service/outbox work and the TargetingOverlayInput export). The genuinely authored surface is the five-file corrective child 46191d3: 11 checkpoint manifest entries in required_schema.json, CI/test predecessor repins to 17ee407, optional-driver import relocations into fixture context managers for correct skip ordering, and a new independent index-fingerprint regression test.
All corrective changes are manifest/CI/test-only and internally consistent. The reviewer surfaced no Critical/High/Medium findings, and no prior blockers existed to re-evaluate.
Decision-table walk:
- Row 1 (critical/high): no findings — skip.
- Row 2 (gated_paths): false — skip.
- Row 3 (high-risk deleted): high_risk is true, but all three high-risk reasons are
(modified), none deleted — skip. - Row 4 (medium + data-loss/schema/infra): no medium findings — skip.
- Row 5 (high-risk modified + medium): high-risk modified files exist, but no medium finding — 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 — skip.
- Row 9: approve.
The high-risk flag on decisioning/server files is a look-closer signal only; those files are modified but carry no reviewer-flagged concern, so the modification is presumed safe. Falls through to row 9 = approve.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean pass on the delta.
Checked:
- CI pg-conformance job split into a fail-fast:false core/process matrix with a required-gate aggregator that reuses the prior job name and fails closed unless every lane succeeds; coverage union preserved and unknown lanes fail-closed.
- Test files mechanically hoist side-effecting awaits out of assert expressions (-O-safe) with polarity and ordering intact; one example gains a clarifying comment.
- high_risk is true only from (modified) matches on decisioning/handler.py, decisioning/serve.py, and server/principal.py — no medium-or-higher findings on those files, so the modifications are presumed contract-preserving (row 5 does not fire).
Decision-table walk: no critical/high findings (rows 1, 6 clear); gated_paths is false (row 2 N/A); no (deleted) high-risk reason (row 3 N/A); no medium findings in any escalation category (rows 4-5 N/A); no no-auto-approve team match (row 7 N/A); fewer than three medium findings (row 8 N/A). Falls through to row 9.
Prior decision was approve; nothing to re-surface. No blocking findings.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean delta pass since prior approve.
Two changes since the last approved head (03c49f1):
.github/workflows/ci.ymladds correct least-privilegepermissionsblocks (contents: readon the pg-conformance checkout job,permissions: {}on the fan-in summary job).- The reporting transport-logging test gains two comments documenting the intentional nested
ContextExitErrorpasshandlers.
No behavior change, no public-surface/wire/generated-code impact. No Critical, High, or Medium findings. high_risk is true only because modified files match src/adcp/decisioning/** and src/adcp/server/** globs, but the reviewer found no medium-or-higher concerns on them, so the modifications are presumed safe (not escalation-worthy on the flag alone). No gated paths, no no-auto-approve team match. Prior decision was already approve.
Decision rules top-down: rows 1–8 do not fire → row 9 approve.
Reporting webhook attempts become durable, account-scoped activity that buyers can read through
list_accounts(include_webhook_activity=true). A worker crash retains an honest pending reservation; a retry keeps the notification, prepared body and idempotency key while allocating the next HTTP attempt.Refs #1168. This is the #1168B activity slice following merged #1178. Status notification projection, fingerprint deduplication and clock sweeps remain #1168C.
The sixth packaged migration adds B tables, indexes, constraints and guard functions without rewriting or backfilling A evidence. Required-object validation permits unrelated adopter objects while strictly checking every required object's fingerprint and enabled state. The typed example and migration guide cover registration, mounting, readiness, draining and shutdown.
Activation order: migrate while A serves its existing configuration, then drain or replace A workers before enabling B activity and canonical URL-principal subscriptions. A workers do not record activity and A registration rejects URL principals; mixed A/B workers cannot establish complete activity coverage.
Current-main composition
Original feature
198d50e61c74fb82aedbf2c77e06a0e200b91db6is preserved. Ordinary mergebddbf1c6f9077939b4f67e98bac0cd8054431722has ordered parents[198d50e6, 17ee407a], where17ee407ae3978c8a2bb54437287afbf9dafb8130is the actual merged #1178 main. Corrective child46191d308ad949bff21696f04c4b4892e0f6884ahas tree8ab1d6f67ada5f381203a77c2c6412a6fe354b9e. Two further ordinary children preserve that ancestry:655e9c27742faec089259129cfc47f8698f7e1adseparates operational test calls from assertions;03c49f178d1d51857cdb7afb06bc601bc770b0cadistributes the PostgreSQL workload. Three further ordinary children restrict the PG aggregate token, document two injected test-exception handlers and restrict the PG execution tokens. Current head isd0d74b9c90c3eea1758da5e794c33e4f7a6fc67e, treee70551bc2b5c7cc7e69ee7a910f746e749f72b11. All original 31 feature paths remain; the complete diff is 31 files, +5818/-446. No parent was amended, rebased or rewritten.The schema-inspector conflict retains B's original per-object implementation byte-for-byte. Each named object is hashed independently, so cross-row database collation no longer enters its fingerprint. Main's ledger DDL, settling, currency, evidence, strict checkpoint damage controls and previously corrected test harness remain intact.
The five-file corrective child (+94/-31) addresses the observed composition failures:
17ee407a, including its checkpoint and locale-readiness corrections. The guide explicitly identifies this predecessor. The older21bf443esubprocess failures remain retained; current-predecessor greens do not qualify that historical snapshot or erase its locale-sensitive contract.Test and CI follow-up
The reviewed test/workload follow-up
46191d30..03c49f17is five files, +150/-96, patch SHA256eec126799029a678d2364665919793c55e72af791d788a8383a74200313ff14f. SDK implementation, required manifest, dependencies and documentation are identical to46191d30.pytest.raisesremain unchanged.9rLBwas already resolved with a separate fix(reporting): cache schema proofs and retain safe receipt diagnostics #1191 follow-up; its fix remains absent here and receives no credit on this PR.Postgres conformance tests (Postgres 16)required name and accepts only success from the complete matrix, failing for cancellation, failure, skips or an empty result. CI expands from 16 to 18 jobs. This increases runner allocation, not individual execution deadlines.Original ordinary CI remains cancelled. Run
35832702909, attempt 1, PG job107088786196checked out merge384544f9. Its original log printed1492 passed, 1 warning in 840.82sat 07:52:48Z, then the job exceeded its 15-minute maximum and ended CANCELLED. The raw log SHA256 isf7d270a2d62cd6612b9394e35c1eb5f818fc35af3d220c83ff007f2fb32bcd16. The printed result is retained alongside the cancellation; it is not passing CI. The original guard closed NOT_READY with two open documentation findings. No workflow rerun, cancellation, timeout increase or waiver was used.Local test-only child
655e9c27passed the two base-installation files (52 passed / 85 intended optional-PG skips, 16.259s under 180s) and all three changed test files on fresh PG16.14/libc en_US.utf8 (143 passed / zero skips, 155.485s pytest / 168.396s outer under 600s). Both runs and clean teardown retain their actual child identity.Local executions on predecessor
03c49f17/ tree56c58c28used separate fresh PostgreSQL 16.14/libc en_US.utf8 clusters and CPython 3.12.14. Each pytest invocation retained a 900-second ceiling:89db5fc6948576b2f76453493020185c43f7a249a34b446c3f298665b3c6ddcdec0dae9d47efb7a2a4b026d79735a23c13ed745e0da890fef6809c5b8b9cba52Printed pytest times were 309.50 seconds for core and 308.65 seconds for process; the table retains the separate JUnit suite times. Both clusters had zero remaining clients, their databases were dropped, and normal shutdown completed. These are local execution results; fresh CI must qualify checkout, dependency/service setup, tests and teardown within each whole-job 15-minute limit. The original CI process-file result boundary spanned about 381 seconds, motivating the partition without claiming isolated per-case timing or transferring a cancelled result.
Static verification also exercised the aggregate success/failure/cancelled/skipped/empty branches. Private helper errors are retained separately: shell continuation parsing, a URL-absent collection that intentionally omitted 130 PG-module cases, and an attempted comparison against GitHub-masked synthetic parameter labels. The corrected URL-present collections are 1,492 / 1,485 / seven with zero overlap or missing cases; no test body was executed during collection. The masked original log cannot establish equality of unredacted case IDs. These are validation-helper limitations, not SDK failures or transferred CI results.
Narrow permissions and test-comment correction
The final append over
03c49f17touches two files, +5/-0, patch SHA256989b4fb459db1f156ff46140c148d72f95290648b3b9e37a1cbef586091de2b5. The three commits are strictly linear:d0d74b9c→312985b0→040639ff→03c49f17.permissions: {contents: read}on both PG execution lanes for checkout and the pinned predecessor fetch. Setpermissions: {}on their aggregate, which reads the matrix result without repository access. These address both genuine CodeQL annotations on exact03; its successful check conclusion did not mean zero alerts. All other parsed workflow values, commands, services, lane dispatch, aggregate conditions and deadlines are identical to03. Other inherited workflow permission gaps remain outside this correction.ContextExitErrorhandlers in the logging test: one permits outer-context checks after the injected inner exit, and the other permits post-exit restoration checks after the injected outer exit. Full Python AST, including every constant, is identical. The SDK does not raise this test sentinel; both handlers deliberately let the subsequent assertions execute.Static comparison and normal commit hooks passed. No SDK/test execution was added for this permissions/comment-only delta. Fresh ordinary CI, complete scanner-output and review-thread readback, and bounded delta review must bind this head; no03 result transfers.
Exact03 original CI completed successfully, with ordinary findings retained. Run
35835555465, attempt 1, completed all 18 jobs successfully. Both execution jobs checked out mergec2e1aba0: core passed 1,485 tests with one warning in 448.16 seconds, and process passed seven in 283.58 seconds. Their full job lifecycles were 531 and 353 seconds, respectively, within the unchanged 15-minute ceilings. The required aggregate succeeded withPG_RESULT: success. Raw lane log SHA256 values areeca061db9fb758dfa2b8cc762541d9f95dbe5b4dae393e0626d05d42deb4818fandb375983fbd957df8b558a7ee7ca2e7d82fd36d89fb432f54367c28480e16942d; aggregatec73540ecf27390791dcc87f8955ca005373a5635028d09da7960ed9d62367ca8.The exact03 CodeQL check succeeded while reporting two genuine permission alerts; two test-handler documentation threads also remained open before this append. The original03 results and findings are preserved in guard disposition
37dcda59bcffe5e5be719cb660cdd321b498592fdf5e2c0f0e92c28cd0a619cb, closed NOT_READY with 14 threads / 11 resolved / three open. The earlier461 job remains CANCELLED. Neither head's execution or review is attributed to this final child.Preserved composition validation
The full PostgreSQL invocation took 610.29 seconds including launcher overhead. Its original stdout SHA256 is
e306acdc526f722c784ea5fce0501e13e4122c45607d12f3a2722f98ad1a8048. Both actual-binary rolling directions passed in all three database regimes. Wheel/sdist and crash/restart tests are included in that full selection. Final database clients were empty; the fresh cluster stopped normally.Execution provenance: these local runs used retained child
2c5d284f9f2213c1c0c30e3a32fba18fcb664173, tree8ab1d6f67ada5f381203a77c2c6412a6fe354b9e. Published corrective commit46191d30has exactly the same tree and an empty source diff relative to that local execution identity. A prepublication check caught root's rejectedmerge(reporting): ...commit title. Root preserved that unpublished lineage, made a fresh ordinary merge from the original feature and the same main using the acceptedMerge branch ...title, and appended the identical corrective patch. No commit was amended, reset, rebased or force-pushed; the validator was not changed. Local logs retain their actual execution head; fresh CI and independent review must bind the final published head.The required-correct controls ran before correction on unchanged composed parent
71b330ab: base-only activity migration reported 16 failed / 17 skipped (optional-driver imports before fixture skipping); PostgreSQL reported 14 failed / 70 passed. Those include six historical manifest equalities, four checkpoint damage refusals, exact-manifest equality, the inherited aggregate-index key, and two historical-A subprocess exits. The subprocess helper discarded their stderr, so those exits alone do not establish a lower-level cause. Their original streams remain unchanged.Fresh C, libc
en_US.utf8and ICUen-UScatalogs each match all 464 objects with strict readiness. The recorded local executions bind the source tree above, CPython 3.12.14 and PostgreSQL 16.14. The C/ICU focused selections retain the 600-second limit; the broader libc CI PostgreSQL selection retains 900 seconds. Counts are per invocation, not additive. Ordinary CI and independent composition review must bind this new published head and its actual main composition; no original-head CI or review result transfers.Earlier extensive local and independent results on
198d50e6remain historical and are preserved in the original PR body/evidence. They are not labelled as execution on this composition. No installed main artifact, registry release, published-byte pin, full #1199 acceptance or TS calendar/interop clearance is claimed.Remaining scope and risks
principal_reference; principal IDs are omitted from the wire projection. This existing defense-in-depth limitation remains separate.