Skip to content

perf(pipeline): harden transcript action workflow cleanup - #1248

Closed
groupthinking wants to merge 4 commits into
mainfrom
claude/determined-maxwell-lv8mlc
Closed

perf(pipeline): harden transcript action workflow cleanup#1248
groupthinking wants to merge 4 commits into
mainfrom
claude/determined-maxwell-lv8mlc

Conversation

@groupthinking

Copy link
Copy Markdown
Owner

Canonical issue

Closes # (none — surfacing pre-existing, unpushed branch work; link a canonical issue before promotion)

Outcome

Hardens cleanup in the transcript action workflow so failures during download/temp-tree teardown neither mask the original exception nor block the event loop:

  • Offload transcript download cleanup to a worker thread (avoid blocking the loop on filesystem teardown).
  • Make temp-tree cleanup total so a cleanup error can't mask the real exception.
  • Make download-artifact cleanup total for non-OSError failures too.
  • Replace the test's fixed poll budget with a wall-clock deadline (de-flakes timing assertions).

Scope

Risk

  • Risk level: low
  • Failure mode: cleanup path changes only affect teardown/exception-masking behavior; happy-path workflow output unchanged.
  • Rollback: revert the branch; no schema/state migration involved.

Verification

List exact automated and manual checks, tied to the current head SHA (627ebe2).

  • Focused tests — not run in this environment: the ephemeral sandbox lacks the full runtime dep tree (httpx, aiohttp, sqlalchemy, pydantic, …) and the editable install is blocked by a Debian packaging uninstall conflict. Each of the 4 commits ships accompanying unit tests; CI must validate.
  • Required CI
  • Review threads resolved

Production evidence

Not applicable — Python-only backend change; not exercised by the Vercel apps/web preview.

Agent handoff

  • One canonical issue is linked (none yet)
  • No competing PR implements the same issue
  • Acceptance criteria are satisfied (pending CI)
  • Required checks pass on the current head
  • Human decision is requested for promotion out of draft and merge to protected main

Context: opened by the unattended PR-remediation routine. These 4 tested commits already existed on the designated branch but had never been pushed or given a PR; this rescues them for CI + human review. Kept as draft — not marking ready and not merging autonomously.


Generated by Claude Code

groupthinking and others added 4 commits August 2, 2026 11:21
The `finally` block in `_fallback_transcript_with_gemini` deleted the
downloaded video and recursively removed its temp tree directly on the
event loop. `Path.exists`, `Path.unlink` and `shutil.rmtree` are all
blocking syscalls, and the temp tree can hold a merged mp4 plus unmerged
`.fNNN` fragments, so every request that reaches the Gemini video
fallback stalls the loop for the duration of the delete.

Move the cleanup into `_cleanup_download_artifacts`, a static helper that
runs the same logic under `asyncio.to_thread`. The call is wrapped in
`asyncio.shield` because the original inline code was uncancellable: a
bare `await` in a `finally` can be interrupted by a second cancellation,
which would turn a loop stall into a disk leak. Filesystem semantics are
preserved verbatim, including the `exists()` guards, `except OSError`
and `ignore_errors=True`.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The event-loop liveness test polled a fixed 500 iterations while rmtree was
parked on an unset Event. That is a ~5s budget expressed as an iteration
count, so a loaded CI box that is slow to hand asyncio.to_thread a worker
would fail the test for scheduling reasons rather than for the behaviour
under test.

Poll against time.monotonic() with a 30s deadline instead. The loop still
exits as soon as the worker starts (milliseconds in practice), so the test
is not slower; it simply stops being brittle under load.

Also assert the tick count directly. Each completed tick is one turn of the
event loop taken while the deletion was in flight, which is precisely the
property being proven, and it was previously only implied by reaching the
assertion at all.

The polling is deliberately retained rather than replaced with a blocking
wait on the Event: blocking the loop to wait for proof that the loop is not
blocked would invert the test. Re-verified against pre-change semantics --
reducing the helper to a direct _cleanup() call still yields 3 failed,
2 passed, so this test is no weaker than before.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The cleanup helper runs from a `finally` block. Its `Path.exists()` probes
performed a stat that can itself raise `OSError`, which would propagate out
of the `finally` and replace the exception already in flight.

Remove both probes. `unlink()` raises `FileNotFoundError` (an `OSError`,
already caught) for absent paths and `rmtree(ignore_errors=True)` is a no-op,
so the guards were redundant as well as unsafe.

Also harden the cancellation test's poll loop to a wall-clock deadline
instead of a fixed iteration budget, matching the sibling test.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The cleanup helper runs from a `finally`, so anything it raises replaces
the exception already propagating. Its guards caught `OSError` only, but
neither call is OSError-total: a NUL byte in a path makes `Path.unlink`
raise `ValueError: embedded null character`, and makes `shutil.rmtree`
raise the same from its internal `lstat` despite `ignore_errors=True` --
that flag suppresses `OSError` alone.

Both branches now catch `Exception` and log at debug with `exc_info`.
`CancelledError` is a `BaseException`, so cancellation still propagates.

Not reachable in production today: `temp_root` comes from `mkdtemp` and a
NUL `video_path` is already rejected by the `exists()` guard in
`_download`. This corrects a false totality claim in the contract.

Verified: reverting to the `OSError`-only guards fails the new test with
the escaping `ValueError` (1 failed, 6 passed); restored 115 passed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@vercel

vercel Bot commented Aug 2, 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 Ready Ready Preview, v0 Aug 2, 2026 5:12pm

@github-actions github-actions Bot added the python label Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@groupthinking, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5c1632a7-7732-4727-8e46-fc16599d4cae

📥 Commits

Reviewing files that changed from the base of the PR and between b6bbfb2 and 627ebe2.

⛔ Files ignored due to path filters (1)
  • tests/unit/test_transcript_action_workflow.py is excluded by !tests/**
📒 Files selected for processing (1)
  • src/youtube_extension/services/workflows/transcript_action_workflow.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Aug 2, 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 627ebe2.
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

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Agent Completion Truth Gate: BLOCKED

Reasons: invalid_payload

Machine-readable verdict
{
  "details": {
    "invalid_fields": [
      "issue.number",
      "policy.agent_login",
      "policy.run_id"
    ]
  },
  "reasons": [
    "invalid_payload"
  ],
  "verdict": "blocked"
}

Workflow evidence

Copy link
Copy Markdown
Owner Author

CI status + blocker (unattended remediation run)

Green / clean: lint-python, lint-frontend, gitleaks, bandit, npm-audit, python-safety, dependency-review, CodeQL, guards, validate, Canonical issue and evidence. (test, build, coverage, Security Scan still running at time of writing.)

Skipped (not a failure): CodeRabbit — excluded by label config; add a required label (e.g. python, tests) or comment @coderabbitai review to get a diff review.

Single blocker — agent-completion/truth-gate: invalid_payload on issue.number, policy.agent_login, policy.run_id.

I'm deliberately not clearing this autonomously. This PR rescues 4 pre-existing, tested-but-never-pushed commits on the designated branch (transcript-workflow cleanup hardening) so they aren't lost on container reclaim — it has no canonical tracking issue, and the provenance truth-gate requires one. Filling issue.number with a fabricated or unrelated issue purely to turn the gate green would defeat the gate's purpose, so I'm leaving it red and surfacing the decision instead.

Human decision needed (one of):

  1. If this work is worth keeping: assign it a canonical issue, then replace the agent-lock-example block in the description with a filled agent-lock-manifest (issue_number, agent_login, run_id) so the gate can progress.
  2. If it's not worth a tracking issue: close this PR — the commits remain safe on claude/determined-maxwell-lv8mlc.

Note: I couldn't run the focused tests locally (the ephemeral sandbox lacks the full runtime dep tree and the editable install is blocked by a Debian packaging conflict), so CI is the source of truth for the test job. Kept as draft; no autonomous merge to protected main.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

@groupthinking: I will review the PR diff. The provenance truth-gate blocker does not prevent code review.

✅ 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.

Copy link
Copy Markdown
Owner Author

Closing as superseded by #1245, which is now merged into main.

I opened this to rescue 4 tested-but-unpushed commits on claude/determined-maxwell-lv8mlc, believing they'd be lost on container reclaim. They weren't lost — they're the same transcript-download cleanup off-loading work that was being finalized in parallel as #1245 (Closes #1244), which merged at 2026-08-02T17:14 UTC in a more complete form (5 commits, 7 new regression tests). Verified in main: _cleanup_download_artifacts is present and commit 6847a1f …(#1245) is on the default branch.

This PR therefore adds nothing over main and its agent-completion/truth-gate blocker is moot. No canonical issue is needed. Closing; the branch can be deleted.


Generated by Claude Code

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant