Skip to content

fix: harden API-cost webhook outbox retries (MYX-79) - #869

Closed
groupthinking wants to merge 35 commits into
mainfrom
agent/harden-api-cost-outbox
Closed

fix: harden API-cost webhook outbox retries (MYX-79)#869
groupthinking wants to merge 35 commits into
mainfrom
agent/harden-api-cost-outbox

Conversation

@groupthinking

@groupthinking groupthinking commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Status: draft — do not merge

This advances #861 / #861 and progresses parent program #898. All current inline review findings are resolved, including the atomic usage/outbox transaction and canonical v1/HybridProcessor Gemini usage instrumentation. The exact head is green. The PR remains draft only until the protected staging/production proof gates below are complete.

Implemented

  • persists next_attempt_at and due/stale-work indexes;
  • retries automatically with bounded exponential equal jitter, maximum five attempts;
  • claims and completes rows with token-fenced compare-and-swap updates;
  • recovers stale and cancelled claims;
  • leaves rows pending when API_COST_WEBHOOK_URL is unset;
  • sends a stable api-cost:{utc_date}:{alert_type} event ID as Idempotency-Key and X-Event-ID;
  • moves synchronous SQLAlchemy work off the event loop;
  • uses StaticPool plus check_same_thread=False for supported in-memory SQLite worker-thread tests;
  • counts only successful, conditionally finalized webhook deliveries;
  • keeps production delivery out of the FastAPI lifespan and in the dedicated Cloud Run worker merged by feat: add production PostgreSQL substrate for API-cost worker (MYX-81) #877;
  • commits usage persistence, daily-budget recomputation, and conflict-safe alert-outbox enqueue in one database transaction, with success and rollback regression coverage;
  • preserves provider-reported Gemini token metadata and emits track_api_call from the canonical HybridProcessor path without allowing telemetry failure to discard a paid result.

Verification

Previous green head cd9964a05f200b5aa1d9fa71152a20ce44eaf23b:

  • CI, API cost PostgreSQL, Coverage, CodeQL, Security Scan, Secret Scan, and Dependency Review passed;
  • E2E was skipped by repository configuration;
  • the PostgreSQL matrix passed fresh, from-002, and round-trip migration/runtime gates;
  • all four active review threads were verified and resolved;
  • new regression tests cover worker-thread in-memory delivery and ensure failed deliveries are not counted as completed;
  • the PR diff is limited to the API-cost monitor and its focused unit tests;
  • no deployment, credential mutation, merge, or branch deletion was performed.

Historical execution head 45edc01037d72e7d2d9a56e18b2d5c2f6bb4ba76:

  • commits 3b4d66e and 1c3ee57 preserve Gemini provider usage metadata and call the durable API-cost tracker from the canonical HybridProcessor path;
  • commits adbfb6b and 45edc01 add provider-metadata and non-fatal telemetry regression tests;
  • 248 focused Gemini/HybridProcessor tests passed locally;
  • exact-head CI 29810092858, API cost PostgreSQL 29810092890, Coverage 29810092885, CodeQL 29810092856, Security Scan 29810092921, Secret Scan 29810092855, and Dependency Review 29810092888 passed; E2E 29810092846 was repository-skipped;
  • zero review threads are unresolved.

Remaining merge blockers

  • the merged feat: add production PostgreSQL substrate for API-cost worker (MYX-81) #877 substrate still needs a protected staging run with the real Cloud SQL identities/secrets and revision-replacement durability proof;
  • webhook activation requires a separately reviewed, pinned secret/config change and rollback drill;
  • production-shaped overlapping-worker, crash-boundary, timeout, ordering, exhaustion, and multi-batch evidence must be attached to the exact final head.

Scope

Closes #861 only after every remaining gate is complete; progresses #898 and #861. Builds on merged #877 and reopened corrective issue #868. The PR must remain draft and unmerged until those gates are complete.

Coverage evidence correction — 2026-07-22

The previously listed Coverage success predates the authoritative workflow installed by #921. That workflow could return green after pytest collection failed because failures were suppressed. Treat the old Coverage run as non-authoritative evidence, not as proof. This PR must receive a truthful Coverage result on a current synchronized exact head before it advances; this correction does not imply a regression in this PR's code.

Current exact-head reconciliation — 2026-07-22T10:23Z

  • External synchronization produced intermediate head 1fd74ffc6fb791797428a071f75f12b0afa2a013 immediately before fix(ci): make coverage and gh-aw canary authoritative #927 merged to main. It was incorrectly marked ready while protected proof remained incomplete.
  • The controller returned the PR to draft.
  • Authoritative Coverage run 29909459928 failed on that intermediate head: 13 failures, 1 collection error, and 88.39% against the superseded 90% floor. This was a real fail-closed signal, not an outbox-code regression.
  • Reconciled current main@5da61c595aa9dc848786e9e1fe99e40ad2a4fce0 into this existing canonical branch without force.
  • New exact head: fd7c82d268224709487cbf0f1c5865feeb77da2d; exact tree: 62bfb5ba79ed0b57b17aaa30926398cef405eaa4.
  • Diff against current main is exactly the eight declared outbox/Gemini implementation and test files; no workflow, gh-aw, coverage-controller, authentication, migration, or unrelated file is carried by this PR.
  • Exact-head CI 29911281817, Coverage 29911282072, API cost PostgreSQL 29911282046, CodeQL 29911282154, Security 29911282064, Secret Scan 29911281875, and Dependency Review 29911282177 pass. E2E 29911281973 is repository-skipped.
  • Coverage: 7,774 passed, 19 skipped, 5 xpassed; 22,571 statements / 2,655 missed = 88.2371%, above the exact 88.1833% floor. Artifact 8526104756, 2,033,370 bytes, digest 7e70286585bc730ff9ea1fc7751ddefb783a517bd54defe0a85923c83bd33376.
  • Zero review threads are unresolved.
  • Current-head Copilot review is still required.
  • Protected staging migration, revision A→B durability, production-shaped worker proof, webhook activation review, and rollback proof remain incomplete.
  • Exact-head Vercel deployment dpl_ooDwJ5UBLUgiD2S9kFTFv6Coc8da was canceled because the controller commit is unverified; obsolete intermediate head 1fd74ffc… had a verified READY preview but is not current evidence.

Controller execution receipt

Keep draft. The next executable transition is current-head review followed by protected staging/worker proof; do not merge, activate delivery, or create another implementation.

@linear-code

linear-code Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

MYX-79

@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v0-uvai Canceled Canceled Jul 27, 2026 9:50pm

@github-actions

Copy link
Copy Markdown

🔍 PR Validation

⚠️ Large PR detected (957 lines changed)

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA cc6fe44.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The API-cost monitor now uses a persisted, scheduled webhook outbox with managed worker lifecycle, bounded retries, stale-claim recovery, atomic delivery claims, and idempotency headers. FastAPI starts and closes the monitor through application lifespan management.

Changes

Webhook outbox delivery

Layer / File(s) Summary
Outbox schema and delivery configuration
src/youtube_extension/backend/services/api_cost_monitor.py
Adds persisted retry scheduling, due-delivery indexing, SQLite schema upgrades, retry configuration, and per-attempt webhook event context.
Worker lifecycle and stale recovery
src/youtube_extension/backend/services/api_cost_monitor.py
Replaces per-alert task spawning with a managed worker, wake event, bounded retry scheduling, and stale processing recovery.
Claim, delivery, and completion
src/youtube_extension/backend/services/api_cost_monitor.py
Adds atomic claim/completion operations, due filtering, exact-attempt matching, idempotency headers, and explicit missing-webhook handling.
Application lifecycle wiring
src/youtube_extension/main.py
Starts and closes the cost monitor through the FastAPI application lifespan, with startup cleanup and logging. Formatting-only changes preserve existing configuration and middleware behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FastAPI
  participant APICostMonitor
  participant OutboxWorker
  participant WebhookEndpoint
  FastAPI->>APICostMonitor: start()
  APICostMonitor->>OutboxWorker: run managed worker
  OutboxWorker->>APICostMonitor: process due outbox item
  APICostMonitor->>WebhookEndpoint: POST with idempotency header
  WebhookEndpoint-->>APICostMonitor: delivery result
  APICostMonitor->>OutboxWorker: persist sent or retry state
  FastAPI->>APICostMonitor: close()
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: claude

Poem

A webhook waits in a durable queue,
With retries timed and claims held true.
One worker wakes when alerts arise,
Stable keys guide each request’s flight.
At shutdown, the queue remembers you.

🚥 Pre-merge checks | ✅ 3 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning MYX-79 is mostly implemented, but MYX-81's production PostgreSQL/migration/runtime requirements are still unmet in the reviewable changes. Add shared PostgreSQL storage, migration-owned schema and deploy-time migrations, and a durable worker runtime that does not rely on SQLite or app-start DDL.
Enforce Copilot Verification ⚠️ Warning PR #869 has CodeRabbit reviews only; no review or approval by GitHub Copilot was found. Obtain an explicit GitHub Copilot review/approval on the PR, or remove this gate if Copilot approval cannot be produced.
Require Ai Unit Tests ⚠️ Warning PR labels are bug/priority:high/python/async-fix, not copilot-rabbit; tests were added, but the required label is missing. Add the copilot-rabbit label to the PR and keep the committed unit tests alongside the code changes before merging.
Description check ⚠️ Warning The description is detailed, but it misses required template sections like Canonical issue, Outcome, Risk, Production evidence, and Agent handoff/provenance. Add the missing template sections with a linked canonical issue, outcome, scope, risk/rollback, production evidence, and handoff/provenance details.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changes stay focused on the API-cost outbox, worker wiring, and minor entrypoint formatting; no unrelated work stands out.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title is concise and accurately summarizes the main change: hardening API-cost webhook outbox retries.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/harden-api-cost-outbox
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch agent/harden-api-cost-outbox

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/youtube_extension/backend/services/api_cost_monitor.py (1)

651-717: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Move the outbox worker off the event loop. recover_stale_deliveries(), claim, and completion all use a synchronous Session inside async worker methods. With the 30-second SQLite timeout, lock contention can freeze the FastAPI loop and delay unrelated requests/shutdown. Switch this path to AsyncSession/create_async_engine, or wrap each transaction in asyncio.to_thread() with a context-managed session.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/youtube_extension/backend/services/api_cost_monitor.py` around lines 651
- 717, Move the synchronous database work in recover_stale_deliveries and the
outbox claim/completion methods out of the event loop by using AsyncSession with
an async engine, or by wrapping each transaction in asyncio.to_thread with a
context-managed Session. Ensure all queries, updates, commits, rollbacks, and
session cleanup in these worker paths execute off-loop while preserving their
existing concurrency and recovery behavior.

Sources: Coding guidelines, Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/youtube_extension/backend/services/api_cost_monitor.py`:
- Line 220: Update the _worker_task attribute and the start() method’s
worker-task typing to use asyncio.Task[None] instead of bare asyncio.Task. Apply
the parameterized type consistently at the declaration and task
creation/assignment path while preserving existing worker behavior.

In `@src/youtube_extension/main.py`:
- Around line 49-67: Update _app_lifespan with the AsyncIterator[None] return
annotation and import the required typing symbol. In its shutdown finally block,
wrap cost_monitor.close() in exception handling, log cleanup failures with
logger.exception, and preserve any exception raised by the application body.

---

Outside diff comments:
In `@src/youtube_extension/backend/services/api_cost_monitor.py`:
- Around line 651-717: Move the synchronous database work in
recover_stale_deliveries and the outbox claim/completion methods out of the
event loop by using AsyncSession with an async engine, or by wrapping each
transaction in asyncio.to_thread with a context-managed Session. Ensure all
queries, updates, commits, rollbacks, and session cleanup in these worker paths
execute off-loop while preserving their existing concurrency and recovery
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 93728548-6ec0-452b-955a-3c078e02c588

📥 Commits

Reviewing files that changed from the base of the PR and between 832bef5 and 09d7891.

⛔ Files ignored due to path filters (3)
  • tests/unit/test_api_cost_monitor.py is excluded by !tests/**
  • tests/unit/test_api_cost_monitor_lifecycle.py is excluded by !tests/**
  • tests/unit/test_api_cost_outbox_worker.py is excluded by !tests/**
📒 Files selected for processing (2)
  • src/youtube_extension/backend/services/api_cost_monitor.py
  • src/youtube_extension/main.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • groupthinking/uvai-skills (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Vercel Agent Review
⚠️ CI failures not shown inline (1)

Commit Status: Vercel: Vercel

Conclusion: failure

Canceled from the Vercel Dashboard
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{py,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{py,js,jsx,ts,tsx}: Use Python 3.9+ and Node 18+ for development
Never hardcode API keys, database URLs, or secrets in code
Make minimal, surgical changes and avoid deleting working code unless fixing security issues

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py
**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.py: Always use type hints for Python functions
Use Black formatter with 88 character line length for Python code
Follow PEP 8 conventions for Python code
Use descriptive variable names and add docstrings to all public functions in Python
Group imports in Python: standard library, third-party, local
Use SQLAlchemy ORM and never write raw SQL queries
Use environment variables via os.getenv() or pydantic-settings to access configuration
Wrap database operations in try-except blocks and use context managers or FastAPI dependencies for connection cleanup
Implement comprehensive error handling with proper logging in all functions
Version APIs using /api/v1/ prefix for stability
Use JSON-RPC 2.0 protocol for all MCP communication
Follow the single-flow workflow: YouTube link → context extraction → agent dispatch → outputs
Use context managers for resource management in Python code
Implement comprehensive input validation and sanitize outputs for security
Use parameterized queries and SQLAlchemy ORM to prevent SQL injection
Define API request/response models using Pydantic for FastAPI endpoints
Store the single unified workflow as the only workflow; never introduce alternate flows or manual triggers
Use SQLAlchemy with connection pooling for database connections and manage sessions with context managers
Provide sensible defaults for non-sensitive configuration in Python settings
Maintain backward compatibility and do not break existing API endpoints
Include comprehensive logging for debugging in MCP implementations

**/*.py: Format Python code with Black using an 88-character line length.
Use Ruff with rules E, W, F, I, B, C4, and UP; E501 is ignored.
Use strict mypy checking with untyped function definitions disallowed.

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py

⚙️ CodeRabbit configuration file

Python backend code. Check for type hints, proper exception handling, async context manager usage, and potential blocking calls in async functions. Flag any bare except clauses or missing timeout parameters on network calls. CRITICAL: Flag any file that contains placeholder/stub implementations — especially in unified_ai_sdk. Any class or function that says "TODO: Replace with production implementation" or returns mock/fake data must be flagged as a blocking issue. Flag any code generation output that reaches users without AST validation or syntax checking.

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py
**/*.{py,js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Maintain >80% code coverage for new features

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py
**/*.{py,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{py,ts,tsx}: Keep frontend and backend data models synchronized using matching Pydantic (backend) and TypeScript (frontend) interfaces
Use type-safe interfaces for backend-frontend data exchange

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py
**/*

📄 CodeRabbit inference engine (Custom checks)

**/*: Strictly verify that GitHub Copilot has explicitly reviewed and approved the pull request; human approvals alone must not satisfy this check.
Before allowing a merge, require the copilot-rabbit label and AI-generated unit tests committed alongside the code changes; fail the check if either is missing.

**/*: Follow the documented event naming convention <domain>.<entity>.<action>, such as youtube.video.captured.
Use the service-container dependency injection pattern for backend dependencies.
Never infer SDK types from tests or API documentation alone; use backend response models as the authority.
When auditing branches, use the branch-cleanup skill and its six-gate fail-test harness; archive branches with git tag archive/<branch> before deletion, and do not rely on three-dot diffs or git merge-tree for orphaned branches.

For Vercel-specific work, include https://vercel.com/docs/llms-full.txt in the AI assistant context set.

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Format Python code with Black using an 88-character line length.
Sort Python imports with isort using the Black profile.
Use Ruff with E, W, F, I, B, C4, and UP rules; E501 is ignored.
Use mypy strict mode; untyped function definitions are disallowed.
Target Python 3.9 or newer.
Validate backend inputs with Pydantic and sanitize subprocess arguments.
Maintain strict mypy type safety in the Python backend.
Use the required Anthropic SDK parameters thinking={"type": "adaptive"} and output_config={"effort": "..."} with the current model string claude-opus-4-8; do not add TypeError compatibility fallbacks.

src/**/*.py: Do not introduce alternative workflows or manual triggers that bypass the single YouTube link → transcript → events → agents → outputs pipeline.
Use event names in the <domain>.<entity>.<action> format.
Use the service-container dependency-injection pattern for dependencies.
Use Pydantic input validation and sanitize subprocess arguments.
Production code must use real behavior only; do not add mock delays or fake data.

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py
**/*.{py,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,ts,tsx,js,jsx}: Do not use mock delays, fake data, or simulated responses in production code; production must remain REAL_MODE_ONLY.
Do not hard-code secrets, keys, or credentials; store them in .env files that are gitignored.

Do not include secrets or API keys in source code; load them from environment variables instead.

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py
**/*.{py,pyw}

📄 CodeRabbit inference engine (AGENTS.md)

Write Python code to remain compatible with Linux and Windows where possible, including correct handling of asyncio event loops.

Files:

  • src/youtube_extension/main.py
  • src/youtube_extension/backend/services/api_cost_monitor.py
🪛 ast-grep (0.44.1)
src/youtube_extension/backend/services/api_cost_monitor.py

[info] 635-635: use secrets package over random package
Context: random.uniform(0, half_cap)
Note: [CWE-330] Use of Insufficiently Random Values.

(avoid-random-python)

🔍 Remote MCP GitHub Copilot

Most useful review context

  • PR #869 is an open draft from agent/harden-api-cost-outbox into main, with 5 commits, 5 changed files, and +868/-89 lines. It explicitly says it advances MYX-79 but must stay draft until #868 / MYX-81 is complete.
  • The touched files are src/youtube_extension/backend/services/api_cost_monitor.py, src/youtube_extension/main.py, and three test files. The new tests cover schema upgrade, worker lifecycle, compare-and-swap claims, cancellation/stale recovery, backoff, and idempotency headers.
  • CodeRabbit’s auto-review only selected the 2 source files; all 3 test files were excluded by !tests/**, and the review is still marked as in progress / incremental.
  • The prerequisite issue #868 is open and says production still lacks shared persistent storage, an executable migration path, and a reliable worker runtime; it also calls out the current main branch as process-local SQLite with import-time create_all() and no durable worker deployment model.
  • Related PR #859 was merged on 2026-07-17 and already added once-per-UTC-day gating plus Slack/Discord payload compatibility in the same module, so #869 is hardening the retry/outbox path on top of an earlier webhook change.
  • Repo search shows track_api_call is used in official_api.py and real_ai_processor.py, and the production entrypoint is youtube_extension.main:app in the Dockerfile/CLI/docs.
🔇 Additional comments (4)
src/youtube_extension/main.py (1)

9-9: LGTM!

Also applies to: 21-22

src/youtube_extension/backend/services/api_cost_monitor.py (3)

11-15: LGTM!

Also applies to: 30-38, 56-61, 105-112


206-219: LGTM!

Also applies to: 221-221, 577-580, 592-649, 916-935


287-314: 🗄️ Data Integrity & Integration

Keep the SQLite compatibility shim. WebhookOutbox already declares next_attempt_at and ix_webhook_outbox_due for new databases; this startup path only patches older SQLite files so existing installs keep working.

			> Likely an incorrect or invalid review comment.

Comment thread src/youtube_extension/backend/services/api_cost_monitor.py Outdated
Comment thread src/youtube_extension/main.py Outdated
@github-actions

Copy link
Copy Markdown

🔍 PR Validation

⚠️ Large PR detected (1050 lines changed)

@linear-code
linear-code Bot marked this pull request as ready for review July 18, 2026 05:58

Copy link
Copy Markdown
Owner Author

Addressed the current CodeRabbit findings on head 3ae774c0:

  • parameterized the managed worker as asyncio.Task[None];
  • annotated the FastAPI lifespan as AsyncIterator[None] and prevented shutdown cleanup failures from masking application errors;
  • moved stale recovery, due-row selection, claims, and completions to worker threads so synchronous SQLAlchemy work no longer blocks the event loop;
  • added regression coverage for cleanup error preservation and off-loop database transactions.

Local Black, Ruff, and py_compile checks pass. The two superseded inline threads are resolved. This PR intentionally remains draft: #868 / MYX-81, atomic usage+outbox persistence, and canonical HybridProcessor instrumentation are still merge blockers.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

@groupthinking I’ll review the updated head, focusing on production-impacting correctness, concurrency, and retry/lifecycle behavior.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@groupthinking
groupthinking marked this pull request as draft July 18, 2026 06:03

Copy link
Copy Markdown
Owner Author

Controller receipt — CI compatibility correction

  • Agent login: OpenAI
  • Run ID: eventrelay-blocker-watch-20260725T0015Z
  • Canonical branch/PR: agent/harden-api-cost-outbox / fix: harden API-cost webhook outbox retries (MYX-79) #869
  • Claimed timestamp: 2026-07-25T00:15:19Z
  • Latest heartbeat: 2026-07-25T00:22:00Z
  • Exact head: 9276cf11e129cacc30d879dd9c47ba90a2971f12

The prior exact head failed CI because six established callers intentionally omit a model and therefore use the legacy sentinel model="default". The new explicit-unknown-model guard incorrectly treated that sentinel as a typo.

Applied the smallest focused correction on the canonical branch:

  • explicit unknown provider models still fail closed;
  • the legacy default sentinel retains service-default costing;
  • added a regression test for the compatibility contract.

This supersedes c6d123b1. Review threads remain unresolved until exact-head workflows complete.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 25, 2026
@linear-code
linear-code Bot marked this pull request as ready for review July 25, 2026 00:41
@groupthinking
groupthinking marked this pull request as draft July 25, 2026 00:41

Copy link
Copy Markdown
Owner Author

Controller receipt — exact-head completion

Completed and verified:

  • explicit unknown Gemini models fail closed; the intentional default sentinel preserves legacy unspecified-model calls;
  • Gemini 3.5 Flash pricing is normalized from the official per-million-token rate;
  • thought tokens are billed as output;
  • outbox claims are UUID-token fenced through claim, completion, recovery, and release;
  • CI 30136594736, Coverage 30136594795, API cost PostgreSQL 30136594742, CodeQL 30136594768, Security 30136594743, Secret Scan 30136594746, and Dependency Review 30136594731 pass;
  • E2E 30136594730 is repository-skipped;
  • all three current review findings are resolved with exact-head evidence; zero review threads remain unresolved.

The exact-head Vercel preview was canceled by unverified-commit policy. #869 was automatically promoted after checks and has been returned to draft because protected staging migration, revision-replacement durability, production-shaped worker scenarios, webhook activation review, and rollback proof remain incomplete.

This partial implementation progresses #898 and does not close #861.

@vercel vercel Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional Suggestion:

A supported/selectable model (gemini-2.5-flash) missing from the pricing table causes calculate_cost to raise ValueError, converting an already-successful, already-billed Gemini response into success=False and discarding the paid result.

Fix on Vercel

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comment thread src/youtube_extension/backend/services/api_cost_monitor.py Outdated
Comment thread src/youtube_extension/backend/services/api_cost_monitor.py
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 27, 2026
@linear-code
linear-code Bot marked this pull request as ready for review July 27, 2026 21:50

Copy link
Copy Markdown
Owner Author

Controller reconciliation — 2026-07-27T21:53Z

  • Returned this unsafe ready-state transition to draft.
  • Advanced the existing canonical branch with focused commits 3093abf8 and cc6fe44a.
  • Exact head: cc6fe44ab2d6092b2f550d8c1bec74e1a90e6db1.
  • Restored the SQLite due index contract to (status, next_attempt_at, retry_count, id) and updated its focused regression assertion.
  • Changed stale-claim selection and compare-and-swap fencing to claimed_at, preserving the NULL legacy-claim branch and matching (status, claimed_at, id).
  • Both exact review findings are proven fixed and resolved; all 11 review threads are now resolved.
  • Exact-head CI, Coverage, CodeQL, Security, and API cost PostgreSQL workflows are currently running. Dependency Review and Secret Scan pass; E2E is repository-skipped.
  • Protected staging/worker, webhook activation, rollback, and current-head independent review gates remain incomplete. Keep draft.

Execution receipt

Next executable action: wait for the exact-head workflows, then record their terminal results. Do not merge or activate production delivery.

Copy link
Copy Markdown
Owner Author

Exact-head workflow completion — 2026-07-27T21:58Z

Exact head remains cc6fe44ab2d6092b2f550d8c1bec74e1a90e6db1.

Keep #869 draft. The two outbox findings remain fixed and all 11 threads resolved, but exact-head CI cannot become green until the separately owned TypeScript rollback blocker is repaired through a valid focused canonical unit.

Copy link
Copy Markdown
Owner Author

Closing in favour of #1376

Both PRs implement the durable API-cost outbox / canonical usage tracking. Measured against current main:

#869 (this) #1376
Files changed 301 8
Lines +29,007 / −8,479 +1,243 / −100
Commits ahead 1,457 2
Commits behind main 55 0
Test files touched 134 5

This branch has drifted to the point where its diff is no longer a review artifact — 301 files and 29k lines for a focused outbox change means the branch has absorbed most of main's history rather than tracking it. #1376 expresses the same intent in 8 files, is current with main, and carries focused test coverage.

Keeping the more complete implementation, which here means the one whose diff actually describes the change. Closing this; #1376 is canonical for GRV-79 / the API-cost outbox work.

No code is lost — the branch is retained and will be archive-tagged before any branch deletion.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Correction to the numbers above. The close stands, but I gave the wrong mechanism.

I described this branch as having "drifted" to 301 files. That file count was wrong — it came from a diff computed against an empty merge base, which silently degraded to a working-tree diff.

The actual situation is starker:

$ git merge-base origin/main origin/agent-harden-api-cost-outbox-17376027973963893260
(empty — no common ancestor)

This branch shares no ancestry at all with current main. It predates the secret-purge force-push described in CLAUDE.md, so it is orphaned rather than merely stale. There is no rebase that recovers it — the histories are unrelated.

That strengthens rather than weakens the disposition: #1376 is not just the tidier implementation, it is the only one rooted in current main. Closing stands, and the branch is retained for archive-tagging.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Execution: durable API-cost outbox and canonical usage tracking (PR #869)

3 participants