Skip to content

feat(video): normalize async job resource state - #912

Merged
seonghobae merged 17 commits into
mainfrom
feat/normalized-video-job-resource-20260829
Aug 30, 2026
Merged

feat(video): normalize async job resource state#912
seonghobae merged 17 commits into
mainfrom
feat/normalized-video-job-resource-20260829

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Separate new video ownership records from first-complete usage observations in the existing job-registry boundary.
  • Persist the latest provider lifecycle value without inferring a gateway status enum.
  • Keep provider-affine polling/content retrieval and opaque gateway identifiers unchanged.
  • Preserve reads and usage updates for pre-normalization video_job_owners records.
  • Add atomic Valkey first-write-wins storage for concurrent usage observations.
  • Refresh PRD/TRD/UML/ADR/doctoring/changelog/gap documentation with the durability boundary.

Validation

  • python -m pytest -q — 2527 passed in 704.59s
  • Focused video/registry/HTTP suite — 70 passed
  • Final registry focused suite — 24 passed
  • ruff check on changed Python files
  • semgrep --config p/python contextual_orchestrator/video_jobs.py — 0 findings
  • python -m compileall -q contextual_orchestrator
  • git diff --check

Based on protected main b21645116b352967e50fc497b87eb745b9cc8c61.


Devin Review

Summary by CodeRabbit

  • 새 기능

    • 비동기 비디오 작업의 소유권, 사용량, 제공자 상태를 별도로 저장하고 조회합니다.
    • 제공자 상태와 최초의 완전한 사용량 관찰을 안정적으로 보존합니다.
    • 기존 비디오 작업 기록과의 호환성을 유지합니다.
    • 작업 조회 및 후속 요청에 불투명한 게이트웨이 식별자를 사용합니다.
  • 문서

    • 비디오 작업의 제공자 고정 처리, 내구성 요구사항 및 저장 모델을 문서화했습니다.
  • 버그 수정

    • 동시에 저장될 때 최초 기록이 유지되도록 개선했습니다.

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 6 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b6dc709-14c2-4b91-aa78-81b0d62d74f5

📥 Commits

Reviewing files that changed from the base of the PR and between 85dd7b8 and 3cdfff7.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • contextual_orchestrator/video_jobs.py
  • docs/architecture.md
  • docs/doctoring/video-job-resource.md
  • docs/model-group-product-technical-spec.md
  • docs/planning/adrs/0037-provider-affine-video-job-ownership.md
  • docs/product-technical-gap-baseline.md
  • docs/product_planning.md
  • tests/test_video_job_registry_atomicity.py
  • tests/test_video_jobs.py
📝 Walkthrough

Walkthrough

비디오 작업 레지스트리가 소유권, 사용량, 제공자 상태를 별도 레코드로 정규화합니다. 최초 완전 사용량은 보존하고 최신 상태는 갱신합니다. 기존 소유자 레코드의 읽기 호환 경로와 Valkey 원자 저장을 추가합니다.

Changes

정규화된 비디오 작업

Layer / File(s) Summary
최초 저장 원자성
contextual_orchestrator/batch_job_registry.py, tests/test_batch_job_registry.py
ValkeyJsonMapping.set_if_absentHSETNX로 최초 값만 저장하고 성공 시 만료 시간을 갱신합니다. 테스트가 반환값과 최초 값 보존을 검증합니다.
비디오 작업 레코드 정규화
contextual_orchestrator/video_jobs.py, tests/test_video_jobs.py
VideoJobRecord, VideoJobUsage, VideoJobLifecycle을 추가합니다. 등록과 제공자 결과 관찰은 별도 매핑을 사용합니다. 조회는 레코드를 조합하고 레거시 VideoJobOwner를 지원합니다. 테스트가 등록, 상태 갱신, 최초 사용량 보존, 레거시 조회를 검증합니다.
계약 및 기술 문서 정렬
CHANGELOG.md, docs/architecture.md, docs/doctoring/video-job-resource.md, docs/model-group-product-technical-spec.md, docs/planning/adrs/0037-provider-affine-video-job-ownership.md, docs/product-technical-gap-baseline.md, docs/product_planning.md
비동기 비디오 작업의 불투명 ID, 제공자 고정 후속 요청, 정규화된 저장 모델, 관찰된 상태, Valkey 내구성 조건을 문서에 반영합니다.

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

Merge Risk: 🟡 Moderate · up to 85dd7

The PR separates ownership, usage, and lifecycle records while preserving legacy reads and atomic usage recording. However, provider observations may be persisted before identity validation, delayed responses can overwrite newer lifecycle state, and partial registration or rollback can leave jobs difficult to recover; these risks should be fixed or explicitly accepted before merging.

Sequence Diagram(s)

sequenceDiagram
  participant 호출자
  participant VideoJobRegistry
  participant ValkeyJsonMapping
  호출자->>VideoJobRegistry: register 작업 제출
  VideoJobRegistry->>ValkeyJsonMapping: VideoJobRecord 저장
  VideoJobRegistry->>ValkeyJsonMapping: 최초 VideoJobUsage 저장
  VideoJobRegistry->>ValkeyJsonMapping: VideoJobLifecycle 저장
  호출자->>VideoJobRegistry: observe_provider_result 호출
  VideoJobRegistry->>ValkeyJsonMapping: 최신 상태 저장
  VideoJobRegistry->>ValkeyJsonMapping: 기존 완전 사용량 유지
  VideoJobRegistry-->>호출자: 조합된 VideoJobOwner 반환
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 4 files. (7 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 비동기 비디오 작업 리소스 상태를 정규화하는 이번 변경의 주요 목적을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 4 files. (7 skipped: 7 unsupported.)

✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/normalized-video-job-resource-20260829

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.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 0 new potential issues.

Devin Review

devin-ai-integration[bot]

This comment was marked as resolved.

@seonghobae
seonghobae enabled auto-merge (squash) August 29, 2026 03:45
@opencode-agent
opencode-agent Bot disabled auto-merge August 29, 2026 08:35
@seonghobae
seonghobae enabled auto-merge (squash) August 29, 2026 08:38
devin-ai-integration[bot]

This comment was marked as resolved.

@opencode-agent
opencode-agent Bot disabled auto-merge August 29, 2026 09:11
devin-ai-integration[bot]

This comment was marked as resolved.

@github-actions

Copy link
Copy Markdown
Contributor

Conflict inventory (fail-closed diagnostic).

Exact PR head: 1c52fc85f93fa58c88a26a42c9dce40d5c0a1d0e
Protected main: 9bbc9e23e2f7f12bd6583e13370c7917c3e3ef79

docs/architecture.md
docs/product-technical-gap-baseline.md

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 0 new potential issues.

Devin Review

@devin-ai-integration devin-ai-integration 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 0 new potential issues.

Devin Review

@devin-ai-integration devin-ai-integration 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 2 new potential issues.

Devin Review

reported_usage = self._provider_usage(provider_result)
# Core ownership is written first: a failure after provider acceptance
# must never leave the job without a gateway-addressable owner.
self._records[gateway_job_id] = record

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.

🔴 Rolling upgrades hide new video jobs

During mixed-version deployments, register writes new jobs only in the normalized registry. Older replicas return 404 when callers poll those accepted jobs.

Prompt for agents
Make the video-job storage transition safe for mixed-version replicas sharing Valkey. New code currently writes only video_job_records, while pre-upgrade replicas read only video_job_owners, causing intermittent 404 responses during rolling deployment. Add an explicit compatibility strategy, such as temporary dual writes with first-write usage semantics or a deployment migration/read cutover protocol, and cover new-writer/old-reader behavior in tests.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +197 to +205
def _usage_document(self, gateway_job_id: str) -> dict[str, int] | None:
"""Read one normalized usage row as the public token-count shape."""
usage = self._usages.get(gateway_job_id)
if usage is None:
return None
return {
"prompt_tokens": usage.prompt_tokens,
"completion_tokens": usage.completion_tokens,
}

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.

📝 Info: Split retention remains aligned

Each measured-job lookup reads ownership and usage, refreshing both Valkey hashes. Jobs without usage refresh only their ownership record.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Resolve conflicts in docs/architecture.md and
docs/product-technical-gap-baseline.md (both additive continuation entries
from independent PRs). Also fix the legacy-owner-payload doc wording flagged
by CodeRabbit: observe_provider_result writes back a first-complete usage row
for legacy video_job_owners records, so it is a read-and-update compatibility
path, not read-only. Verified the flagged OpenRouter video-generation doc
link and the 2026-08-29 KST slice date are both accurate on current head; no
change needed there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Gs7KmNvH75nxz1sL8mKjw

@devin-ai-integration devin-ai-integration 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 2 new potential issues.

Devin Review

Comment on lines +127 to +135
except Exception: # noqa: BLE001 - ownership is already durably committed
# Returning the opaque gateway id is the recoverability boundary:
# the caller can poll the accepted provider job and retry the
# companion observation, while billing still receives the
# provider response through the coordinator's independent sink.
_LOGGER.warning(
"video usage persistence failed after ownership commit; "
"follow-up polling may retry"
)

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.

🟡 Failed storage loses first usage report

When initial usage storage fails, register returns without retaining the observed counts. A later poll can persist revised counts, diverging from billing.

Prompt for agents
Preserve the first complete usage report even when the companion video_job_usages write fails after ownership persistence. In contextual_orchestrator/video_jobs.py, register currently logs and returns, while server.py independently records the initial counts in the cost ledger. A later observe_provider_result call can therefore persist different counts as the job's first usage row. Add a recoverable durable representation or retry/outbox mechanism that keeps the initial counts associated with the gateway job without sacrificing the requirement to return its opaque ID after provider acceptance. Ensure later polling cannot replace that initial report and that registry-visible usage stays aligned with the deterministic ledger record.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +223 to +234
def _owner_from_legacy(self, owner: VideoJobOwner) -> VideoJobOwner:
"""Join a retained legacy owner to the shared first-write usage row."""
if owner.provider_usage is not None:
return owner
provider_usage = self._usage_document(owner.gateway_job_id)
if provider_usage is None:
return owner
return replace(
owner,
usage_measurement_status="measured",
provider_usage=provider_usage,
)

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.

📝 Info: Legacy counts remain authoritative

Legacy embedded usage takes precedence over normalized rows. Historical counts cannot be revised, while empty legacy records can adopt the first atomic observation.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Status check (2026-08-30, this cycle): opencode-review, noema-review, and strix are still failing on this exact head. Same confirmed org-wide review-sidecar infrastructure gap noted on #868/#857/#906/#911 this cycle — .github#1422's pin bump (b216451…5f2753a…) is live, but the sidecar still fails its own startup preflight (413 request_too_large → OpenRouter ZDR fallback → sidecar exited before healthz) regardless of which commit is vendored. Not a defect in this PR; a dedicated effort is fixing that sidecar directly. Full unit and contract suite remains green.

Re-checked CodeRabbit's three outstanding findings against this head — all already resolved, no action needed:

  • docs/architecture.md's VideoJobRegistry description already says legacy owner payloads are a "compatibility read-and-update path" (not read-only), matching what observe_provider_result actually does.
  • docs/doctoring/video-job-resource.md's references section already cites the valid openrouter.ai/docs/guides/overview/multimodal/video-generation link.
  • The gap-baseline heading date nitpick (2026-08-29 vs. the actual 2026-08-28 change date) is cosmetic only; left alone rather than touching the shared gap-baseline file for a date label.

Generated by Claude Code

# Conflicts:
#	docs/product-technical-gap-baseline.md

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 new potential issue.

Devin Review

Comment on lines +260 to +269
if usage is not None and owner.provider_usage is None:
self._store_usage_if_absent(
owner.gateway_job_id,
VideoJobUsage(
prompt_tokens=usage["prompt_tokens"],
completion_tokens=usage["completion_tokens"],
observed_at=int(time.time()),
),
)
return self._owner_from_record(record)

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.

🟡 Failed billing write never retries

If record_async_video_usage fails after usage persists, later polls skip it because usage already exists. The job remains absent from billing.

Prompt for agents
Decouple “usage has been observed” from “usage has been ledgered.” In contextual_orchestrator/video_jobs.py, observe_provider_result returns the persisted usage on every later poll, while server.py only invokes CostRoutingCoordinator.record_async_video_usage when the pre-poll owner had no usage. If that ledger call fails or drops after the usage row was committed, every later request sees previous_usage populated and never retries. Add durable ledger-completion state or safely invoke the idempotent deterministic ledger write whenever persisted usage is available, including subsequent polls. Preserve first-complete usage semantics and avoid changing the provider usage row.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@seonghobae
seonghobae merged commit c71dcae into main Aug 30, 2026
31 of 34 checks passed
@seonghobae
seonghobae deleted the feat/normalized-video-job-resource-20260829 branch August 30, 2026 09:41
seonghobae added a commit that referenced this pull request Aug 30, 2026
#921)

* docs(gap-baseline): record the sidecar preflight max_tokens root cause

Fulfills the reference added in .github#1436's code comment (Devin flagged
it as a missing baseline entry when it merely pointed at a not-yet-written
one). Records the exact-evidence trail (downloaded strix-reports artifact
from this repo's own PR #912 run) that this repo's PRs cannot fix directly
since the sidecar is central-.github-owned infrastructure.

* docs(gap-baseline): record the post-merge canary result for the sidecar fix

.github#1436 merged (admin bypass, structurally deadlocked check —
evidence on the PR). Re-queued opencode-review/noema-review/strix on
this PR plus #911/#920 as the live canary: the specific max_tokens
502 symptom is confirmed fixed, but noema-review still failed with a
distinct signature (bytez discovery 500 + preflight finding zero
passing routes). Records what's confirmed, what's still open, and
the working hypothesis (concurrent-run rate-limit contention) pending
a clean re-observation.

* docs(gap-baseline): correct the reasoning-starvation mechanism claim

Devin flagged (on #921) that ModelClient._response_content returns
successfully for any string content, including "", so the entry's
"reasoning consumes the budget, content comes back empty" narrative
doesn't match the code, and the generic error message quoted implies
a narrower condition (non-string/absent content, reasoning falsy)
than originally claimed. Verified against the code and the original
strix-reports artifact (no raw provider payload was ever captured —
sanitized by design), corrected the entry to state what's actually
evidenced (budget mismatch reproduces, matching it fixes it) versus
what was an unverified hypothesis, and noted the fix's own tests
target a different, stricter function (the launcher's own preflight
content check) that is unaffected by this correction.

* docs(gap-baseline): replace the rate-limit hypothesis with confirmed evidence

Downloaded and inspected the actual strix-reports artifact instead of
continuing to speculate: the real causes were (1) noema-review/
opencode-review having zero visibility into per-route preflight
rejection reasons, and (2) the gateway preflight's 30s curl timeout
cutting off a route the routing probe had just proven healthy in 18s.
Both fixed and RED/GREEN-tested in ContextualWisdomLab/.github#1440.

* docs(gap-baseline): restate the retracted hypothesis instead of a dangling "above"

Devin caught it on #921: the previous edit deleted the paragraph
stating the rate-limit hypothesis while the replacement text still
said "the hypothesis above," leaving nothing for that reference to
point to. Restated it inline.

* docs(gap-baseline): record the full incident timeline (checker tightened 8/27, gateway check broke 8/29)

Investigated whether .github's opencode-review verdict-checker itself
was defective, since it's been failing org-wide for days. It isn't:
git history shows it was a rubber stamp (always exit 0) until 8/27,
when it was correctly rewritten to require a real matching review.
Two days later, 8/29's "exercise exact gateway readiness" commit
introduced the end-to-end gateway check that shipped with the
max_tokens:16 bug (#1436 fixed today). The now-strict checker
collided with a newly-broken dispatch path, not a checker design
flaw. Confirmed via #1246: last real opencode-agent review was 8/23,
none since despite the head moving forward repeatedly.

* docs(gap-baseline): fix two stale/inaccurate claims CodeRabbit flagged

1. Softened "will resolve on the scheduler's next pass" to not assert
   an outcome that hasn't been verified.
2. Removed the false implication that the routing probe's 10s
   per-candidate timeout says anything about the separate gateway
   curl timeout completing in time -- especially now stale since
   that curl timeout was raised 30s->120s in .github#1440 after
   being found too tight for real reasoning-model latency.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants