Skip to content

fix(ci): close HTTP error response bodies - #1879

Open
seonghobae wants to merge 5 commits into
mainfrom
codex/close-http-error-responses
Open

fix(ci): close HTTP error response bodies#1879
seonghobae wants to merge 5 commits into
mainfrom
codex/close-http-error-responses

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

현재 변경과 검증

Head: d4366f837afd9f988707618c6855c1661f81c3f8.
Parents: 기존 head 0723a0c7d9d4da82e64f884cff8babf1f0e0c81a + main 43024633eba9d96b0456970391360da5a171fbda.
두 부모를 보존한 일반 merge/push이며, 현재 main 대비 11파일 199줄 추가·7줄 삭제입니다. 기존 8파일의 응답 정리·회귀 변경을 유지했습니다.

네 요청 경계에서 응답 정리 오류가 원래 통신 실패를 가리지 않게 했습니다. 정리 한 문장에만 표준 라이브러리를 사용하고 KeyboardInterrupt·SystemExit는 전파합니다. 직접 redirect 예외를 만든 네 기존 테스트도 응답을 닫습니다. 새 클라이언트·의존성·재시도·경고 필터는 없습니다. URL·프록시·리다이렉트 차단, 모델 제한 시간, 권한·리뷰 의미 검증은 바꾸지 않았으며 #1641의 schema 기능을 복제하지 않았습니다.

재현과 정확한 커밋 검증

  • 수정 전 새 계약 검사: 12 failed / 12 passed. 네 실제 호출자에 OSError·ValueError·RuntimeError 정리 실패를 주입하면 원래 결과가 사라졌습니다.
  • 커밋 후 같은 계약: 24 passed, 0 failed/skipped, 0.04초. 정상 정리와 두 취소 신호도 포함합니다. 이 재현 범위의 실패는 12→0, 전체 목표 완료율은 아닙니다.
  • 커밋 후 일반 전체 검사: 2954 passed, 1 skipped, 21 subtests passed, 98.95초.
  • 같은 커밋의 GITHUB_ACTIONS=true 전체 검사: 2954 passed, 1 skipped, 21 subtests passed, 112.37초.
  • 두 전체 실행 모두 -W error, 문장 13197/13197, 분기 5332/5332, 100% coverage. interrogate scripts/ci도 **100%**이며 whitespace 검사는 통과했습니다.
  • 유일한 skip은 이 호스트에 검토된 LLVM 19 도구가 없는 경우의 선택적 admission 검사입니다. LLVM 검증 통과로 계산하지 않습니다.
  • 재사용한 프로젝트 격리 환경의 결과이며 fresh-lock 설치 증거는 아닙니다. Ruff 실행 파일이 없어 Ruff 통과를 주장하지 않습니다.
python -m pytest -q -W error tests/test_http_error_response_ownership.py
python -m pytest -q -W error --cov=scripts/ci --cov-branch --cov-fail-under=100
GITHUB_ACTIONS=true python -m pytest -q -W error --cov=scripts/ci --cov-branch --cov-fail-under=100
interrogate scripts/ci
git diff --check

JUnit SHA-256:

  • 계약: 81ea82402292471832c132c681eda201e54e3e2b8eb93c747fc4b7b55e0ac07a
  • 일반 전체: 932f2c11c0c858901f0db789fff5e776b6752d9aba10e8f271ca06cb53f594b8
  • CI 환경 전체: 1ff8ec5af9c21691f4df9f5672413590db7ebbc658f651423177ef1226c4725f

증거 한계와 인계

독립 읽기 전용 검토에서 요청 소유권·취소·기존 delta 보존·schema 비복제를 확인했습니다. 이는 GitHub 승인이 아닙니다. 정리 전에 구현 자체가 실패하는 경우 모든 운영체제 자원의 해제까지 보장하지 않습니다. 테스트의 닫힘 assertion은 정상/정리 후 실패 경우를 증명하며, 이 한계를 기존 doctoring 문서에 명시했습니다.

이전 head의 OpenCode 실패는 exact-head formal verdict 부재, CodeQL은 비동기 terminal verdict 대기, Strix는 provider/backend 사용 불가였습니다. metadata 전체 검사 실패 17개는 최신 main의 scheduler fixture 수리와 구분했습니다. 이전 성공·실패·승인은 새 head에 승계하지 않습니다. 현재 head의 실제 공급자 호출, required Checks, 독립 승인, 보호 병합은 별도이며 아직 병합하지 않았습니다. principal/규칙/토큰 범위 변경이나 수동 재실행은 하지 않았습니다.

Project #1의 별도 상태 갱신은 조회 권한이 없어 확인하지 못했습니다. 정확한 head와 본 PR의 검증 영수증으로 인계합니다.

이전 본문 보존 — 현재 head의 검증 아님

Summary

  • close HTTPError response bodies after bounded telemetry and policy reads
  • preserve existing sanitized error behavior
  • add focused regression coverage for all four paths

Verification

  • git diff --check
  • pytest -q tests/test_noema_review_gate.py tests/test_pingora_edge_policy.py tests/test_repository_metadata_live_verification.py tests/test_sandboxed_web_e2e.py — 261 passed

Replaces the only substantive runtime fixes from #1871 without its unrelated 928-line coverage/docstring expansion.

Signed-off-by: Seongho Bae <me@seonghobae.me>
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 13 minutes.

Check out review usage here.

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: Team

Run ID: 466e9cc9-1e0d-42c6-913c-a9280bfa5f11

📥 Commits

Reviewing files that changed from the base of the PR and between 0b0f104 and 23eb283.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • docs/doctoring/noema-repair-attempt-telemetry.md
  • scripts/ci/noema_review_gate.py
  • scripts/ci/pingora_edge_policy.py
  • scripts/ci/reconcile_repository_metadata.py
  • scripts/ci/sandboxed_web_e2e.py
  • tests/test_http_error_response_ownership.py
  • tests/test_noema_review_gate.py
  • tests/test_pingora_edge_policy.py
  • tests/test_repository_metadata_live_verification.py
  • tests/test_sandboxed_web_e2e.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.

@seonghobae
seonghobae enabled auto-merge (squash) September 5, 2026 02:27
@opencode-agent
opencode-agent Bot disabled auto-merge September 5, 2026 05:56
seonghobae added a commit that referenced this pull request Sep 5, 2026
#1883)

The admission-controller feature burst (#1859-#1869) shipped
review_admission_controller.py, pr_review_merge_scheduler_core.py's
SchedulerAdmissionGate, and (separately, pre-existing) a coverage gap
in audit_codeql_default_setup_rollout.py without full test coverage
or docstrings, breaking the required 100% coverage/docstring gate for
every PR in this repository regardless of that PR's own diff.

The original fix for this landed on .github#1871, which was later
closed in favor of narrower successors (#1877 for the stale schedule
oracles, #1879 for HTTP error response bodies) -- but the coverage and
docstring portion of #1871's delta was dropped in that narrowing and
never reached main. This PR recovers exactly that portion from
#1871's still-present branch (fix/hourly-review-repair-callers-cron-
format-drift) and completes it:

- review_admission_controller.py: 85% -> 100% coverage (new
  tests/test_review_admission_controller.py), 14 missing docstrings
  added across its WorkerBoundary/AdmissionRequest/RequestRecord/
  DispatchLease/ControllerState/DispatchPlan dataclasses and methods.
- audit_codeql_default_setup_rollout.py: 79% -> 100% coverage (new
  tests/test_codeql_default_setup_rollout.py), 2 missing docstrings
  added (parse_args, main).
- pr_review_merge_scheduler_core.py's SchedulerAdmissionGate: 3
  missing docstrings added (__init__ and its two nested closures,
  lease/reconcile_state).

Additionally closed pr_review_merge_scheduler_core.py's own separate,
longer-standing coverage gap (98% -> 100%, unrelated to the
admission-controller work) discovered while verifying this fix would
actually bring main to a green gate rather than a differently-shaped
99%: the durable admission gate's own bounded-budget/stale-head
branches across every dispatch call site (9 "admission_deferred"
checks across post_update_branch_followup/dispatch_draft_review_only/
inspect_pr, plus dispatch_strix_evidence's own two "admission_deferred"/
"stale_head" pairs), reconcile()'s live-head-moved and still-running
branches, rotating_pr_window's/dispatch_draft_review_only's/the
workflow-run classifier's/the empty-PR-close path's/main()'s own
--admission-state-path wiring's remaining gaps, and two untestable
package-import fallback lines marked `# pragma: no cover - package
import path` matching this file's established convention for that
exact pattern.

Full local triad: 2875 passed, 1 skipped; coverage 100%; interrogate
100%.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
seonghobae added a commit that referenced this pull request Sep 5, 2026
…pline (#1909)

Two rules from mistakes this session actually made and corrected, per the
per-session lane split agreed with the other concurrent sessions (peer 3
took verification discipline in #1907; peer 2 has gate/merge mechanics;
host 1 has close-time diff comparison and noema concurrency; host 2 has
CI failure diagnosis).

- Narrowing a PR does not carry its delta. #1871 was closed in favor of
  #1877 plus #1879; both successors were green, but neither carried the
  coverage/docstring delta, leaving main's required 100% gate broken
  until #1883 recovered it. "Each piece works" and "the pieces together
  cover the original's scope" are different questions.
- Compare content, not ancestry. main mixes squash and merge commits
  (last 200: 153 single-parent, 47 two-parent, counted directly), so
  `git merge-base --is-ancestor` gives false negatives for squashed
  deltas and false positives for reverted merge-commit deltas.
- Never endorse a timeout or retry constant on a model-invocation path
  without reading docs/product-goal-directive.md section 8, which accepts
  more than two hours per model and states speed is not a core
  consideration. #1889/#1890/#1892 each capped a model step at 900s on
  real multi-hour-hang evidence and were all reverted (#1891, #1895).

Every PR number, the section-8 quotes, the parent-count distribution, and
the 100% gate values were verified against the repository directly. An
earlier draft of the timeout bullet cited a section number that does not
exist and attributed a sentence to that file which appears only in
#1891's PR body; both were caught by grepping rather than trusting the
summary that introduced them, and that failure is recorded in the text.

Full suite: 2883 passed, 1 skipped.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

@cwl-noema-review cwl-noema-review 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.

Noema LLM review

The PR correctly and safely closes HTTP error response bodies across four scripts using HTTPError.close(), which is idempotent and prevents resource leaks (file descriptors, connections). Each code change is accompanied by a regression test asserting the response body is closed. The changes are narrowly scoped, preserve existing behavior for non-HTTP errors, and introduce no correctness, security, or maintainability regressions. The only minor gap is that test_repository_metadata_live_verification.py does not cover the plain URLError (non-HTTP) branch, but that branch is unchanged by this PR, so it is non-blocking.

Reviewed changed lines

  • scripts/ci/noema_review_gate.py:1639 (RIGHT): The finally block guarantees exc.close() is called even if _extract_http_error_telemetry raises. CPython's HTTPError.close() is idempotent and closes the underlying fp. The new test test_call_llm_reports_only_safe_model_from_bounded_http_error asserts response_body.closed, confirming the resource is released. The exception handling path is otherwise unchanged.
  • scripts/ci/pingora_edge_policy.py:307 (RIGHT): The isinstance(exc, HTTPError) check and exc.close() before re-raising PolicyError match CPython semantics. The test test_github_open_json_raises_policy_error_for_transport_errors asserts exc.fp.closed. Non-HTTP URLError and TimeoutError paths are unaffected.
  • scripts/ci/reconcile_repository_metadata.py:251 (RIGHT): HTTPError is a subclass of URLError, so the isinstance guard correctly catches redirected 302 cases from _NoPagesRedirects. The exc.close() call is idempotent; the new test test_pages_transport_error_closes_response_body verifies body.closed. Non-HTTP URLError/timeout/OSError branches are untouched.
  • scripts/ci/sandboxed_web_e2e.py:587 (RIGHT): The except (urllib.error.URLError, TimeoutError) as exc block now checks isinstance(exc, urllib.error.HTTPError) and calls exc.close() before sleeping. This matches CPython's HTTPError.close() semantics. The new test test_wait_for_url_closes_http_error_response asserts body.closed. Retry and sleep logic are unchanged for other error types.
  • tests/test_repository_metadata_live_verification.py:67 (RIGHT): The test constructs an HTTPError with a BytesIO body and verifies body.closed after _pages_publication_ready raises a RuntimeError. It exercises the HTTPError branch of the new exc.close() logic. The non-HTTP URLError branch is not covered here, but that branch was not modified by this PR.

Adversarial validation

  • scripts/ci/noema_review_gate.py:1639 (RIGHT) falsified: Calling exc.close() in a finally block could cause a double-close or an exception if _extract_http_error_telemetry already closed the body. — CPython source (urllib/error.py L94-96) and the added test test_call_llm_reports_only_safe_model_from_bounded_http_error which asserts response_body.closed after the call.
  • scripts/ci/sandboxed_web_e2e.py:587 (RIGHT) falsified: Closing the HTTP error body in the polling loop could break the retry logic or cause a sleep to be skipped. — The test asserts body.closed and that wait_for_url returns False after the timeout period, proving the sleep path is unaffected.
  • Residual risk: Low. The only untested scenario is a plain (non-HTTP) URLError in reconcile_repository_metadata.py, but that code path is unchanged and existing tests cover transport errors broadly. The risk of a resource leak is mitigated by the idempotent close() and the confirmed tests for all HTTPError paths.

Findings

  • No blocking findings.
  • Result: APPROVE
  • Head SHA: 0723a0c7d9d4da82e64f884cff8babf1f0e0c81a
  • Reviewer credential: noema-review-github-app-refresh
  • Actor: cwl-noema-review[bot]

@seonghobae

Copy link
Copy Markdown
Contributor Author

현재 head 0723a0c7d9d4da82e64f884cff8babf1f0e0c81a와 main43024633을 확인했습니다. 기존 수정은 세 요청 소유자의 응답 누수 진단을 통과합니다. 다만 경고 엄격 검사(-W error)에서는 직접 redirect 예외를 만든 테스트 네 곳의 정리 누락으로 4 failed / 260 passed(외부 진단3개 포함)를 재현했습니다. 이 작업(01a06aac-7183-7910-aa64-48e0ae87d955)은 별도 격리 worktree에서 기존8파일 delta를 보존한 최신 main 일반 통합과 close 실패/사용자 취소 회귀를 보완합니다. 신규 PR·강제 갱신·권한 변경은 하지 않습니다. 다른 활성 작성자가 있으면 원격 갱신 전에 알려 주세요. Noema #1641의 schema/리뷰 의미 변경은 이 PR로 복사하지 않습니다. 기존 승인과 Checks는 새 head에 승계하지 않습니다.

PR #1879의 응답 정리 변경을 최신 main과 정상 통합한다. 네 요청 경계의 정리 실패가 원래 실패를 가리지 않게 하고 직접 만든 redirect 테스트 응답도 닫는다. 새 계약 검사는 12 RED를 재현했으며 전체 후보 검사는 2954 passed, 1 optional skip, branch coverage 100%를 확인했다. Noema #1641의 별도 schema 변경은 포함하지 않는다.

Commit-Message-Assisted-by: Codex
Signed-off-by: Seongho Bae <me@seonghobae.me>
@seonghobae

Copy link
Copy Markdown
Contributor Author

후속 작성 완료·인계: d4366f8 일반 push, main43024633 base와 본문 원문 readback을 확인했습니다. 정확한 커밋에서 일반·GitHub Actions 환경 전체 각각2954 passed/LLVM19부재1skip/21subtests, 문장·분기100%, docstring100%입니다. 현재 source writer 작업은 완료·해제합니다. 새 head의 Checks와 독립 리뷰는 별도 대기 상태이며 이전 승인·실패를 승계하지 않습니다. Noema#1641의schema 기능은 포함하지 않았습니다. 기존 구현·이력은 보존했고 강제 push·수동 재실행·권한 변경·병합은 하지 않았습니다. 자세한 재현12RED→0 및 환경·정리 보장 한계는 갱신된 PR 본문에 있습니다.

Copy link
Copy Markdown
Contributor Author

Read-only exact-head audit of d4366f837afd9f988707618c6855c1661f81c3f8 (parents predecessor 0723a0c7d9d4da82e64f884cff8babf1f0e0c81a + protected main@43024633eba9d96b0456970391360da5a171fbda; tree 7f6d43c7ec3b0e8217b93abde3e200efe1cd09b0) confirms this existing PR owns the four HTTPError response boundaries: Noema, Pingora policy evidence, Pages publication verification, and sandbox readiness.

Independent exact-tree GREEN:

  • python -W error -m pytest -q tests/test_http_error_response_ownership.py tests/test_noema_review_gate.py tests/test_pingora_edge_policy.py tests/test_repository_metadata_live_verification.py tests/test_sandboxed_web_e2e.py: 287 passed;
  • full python -W error -m pytest -q: 2,954 passed · 1 skipped · 21 subtests.

The fixtures exercise ordinary cleanup, OSError/ValueError/RuntimeError cleanup failure, and KeyboardInterrupt/SystemExit; ordinary cleanup failures preserve each caller's original typed failure/result, while process cancellation remains unsuppressed. Direct redirect tests close the HTTPError they construct. No warning filter or forced-GC workaround is present.

Stack finding: #1879 and Draft #1641 (ad48dd65c7d0d8b6e0d37f0315302b9c6e138899) are parallel children of the same main@43024633 and overlap on the Noema cleanup. #1641 additionally owns its schema/provenance contract; #1879 additionally owns the other three callers. Preserve the explicit dependency order: merge/settle this canonical four-caller cleanup owner first, then ordinary-restack #1641 and its #1898 descendant, or, if #1641 advances first, ordinary-restack this branch and resolve the overlap without dropping either semantic delta. Do not transfer predecessor approval/checks.

The active writer claim in comment 5558088495 remains authoritative; this audit made no source/ref mutation, rerun, approval, principal change, or bypass.

@seonghobae

Copy link
Copy Markdown
Contributor Author

최신 main 통합 검증

현재 head는 23eb2833794f829985e3cef81e8da77fe10b91e1입니다. 기존 head d4366f837afd9f988707618c6855c1661f81c3f8에 main 0b0f10476469d52adc40f98495d50855486cd32f를 일반 merge로 합치고 기존 브랜치에 정상 push했습니다. Force push는 하지 않았습니다.

충돌은 CHANGELOG 첫머리뿐이었으며 양쪽 기록을 모두 보존했습니다. 최신 main 대비 변경은 11개 파일, 199줄 추가·7줄 삭제입니다. CHANGELOG를 제외한 기존·통합 후 변경의 stable patch-id는 모두 75b6631a3c6ebf7b92c73730146aa3d9a3d1c517로 같습니다. HTTPError 수정의 유효 증분을 그대로 유지했습니다.

  • 전체 suite: 2,972 passed, 1 skipped, 21 subtests passed, 271.48초, 종료 코드 0.
  • Branch coverage: 100%, 누락 statement·branch 0.
  • 건너뛴 1개는 이 호스트에 검토된 LLVM 19 도구가 없어 실행하지 못한 검사입니다.
  • 주 작업 세션의 별도 재검증: python -m pytest tests/test_http_error_response_ownership.py -q -o addopts= -W error, 24 passed, 종료 코드 0. 프로젝트 격리 환경과 비어 있는 상속 환경에서 실행했습니다.
  • 전체 JUnit SHA-256: 08dc446b5ca7350fd95e06621ca0d1703e9fa87b5657a937448a92ba0e119d77.

이는 로컬 시험 증거이며, 새 head의 hosted Checks·독립 승인·보호 병합을 대신하지 않습니다. 이전 head의 CodeQL dispatch 대기, OpenCode 판정 부재, Strix 사전 점검 실패를 새 head의 성공으로 간주하지 않습니다. Project API 권한 부족과 Mac 잠금으로 이번 변경의 새 Project·브라우저 확인은 수행하지 못했습니다.

@seonghobae

Copy link
Copy Markdown
Contributor Author

현재 head 23eb2833794f829985e3cef81e8da77fe10b91e1의 Noema 실행이 실패로 종료됐습니다. 실제 job 로그의 2026-09-06T13:18:37Z 진단은 HTTP 502, caller attempts=1, duration=637.0s, phase=response_error, served_model=google/gemma-4-31b-it 입니다. 이는 900초 제한이나 credential 실패의 증거가 아닙니다. CO 담당자에게 원시 공급자/sidecar 원인 확인을 요청했으며 재실행·권한 변경은 하지 않았습니다. Strix job 101486795855는 현재 Run Strix (quick) 단계에서 실행 중입니다. 로컬 검사 성공과 hosted gate 실패를 구분하며, 보호 병합은 아직 완료되지 않았습니다.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Noema 637초 실패의 내부 경로를 추가 확인했습니다. artifact 9990082549의 원본 stderr(SHA-256 e0c879d13daa0f73f145d6e58fb3782dc0f1aca3c9b51fb6558edfca3196fe78)에는 TimeoutError 5건과 마지막 circuit_opened → provider_connection_error 502가 있습니다. 마지막 provider_attempt 13:17:07.262에서 실패 13:18:37.342까지는 90.080초입니다. 실제 설치 CO 414f22973658c4ddc3d4320fcf7acd9b4e8ba991의 contextual_orchestrator/orchestrator.py:1696은 기본 timeout=90, :2256은 이를 connection_timeout으로 사용합니다. trusted source 0b0f10476469d52adc40f98495d50855486cd32f의 launcher:1240–1243은 ModelClient에 timeout을 전달하지 않습니다. 담당자의 산출물 분석 뒤 제가 원본 로그·해시와 두 exact-revision 소스를 별도로 확인했습니다. 따라서 묵시적 90초 제한 경로가 이번 실패를 뒷받침합니다. 전체 후보 소진의 완전한 재구성이나 제한 제거 후 정상 verdict까지 증명한 것은 아닙니다. CO canonical owner의 기존 수정에서 이어가며 caller 재실행·유료 fallback·권한 변경은 하지 않았습니다.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Strix 실제 종료 증거 갱신: run34031339200/job101486795855는 2026-09-06 14:54:04 UTC에 failure로 종료됐습니다. 대상 head23eb2833794f829985e3cef81e8da77fe10b91e1. Run Strix quick 단계13:24:19→14:53:58 UTC, 로그는 orchestrator/free after5377s exit1을 기록합니다. OpenAI SDK 경로에서 HTTP500 internal_error가 발생했고 request_id는 5055fdba76e54ecabdb5e741c3c311f5 및 80843d4b38c04131b05d588ecc19b3d1입니다. 이 실행을 900초 wrapper 제한이나 인증 실패로 분류할 근거는 없습니다. wrapper의 provider-unavailable 문구만으로 upstream 원인을 확정하지 않으며 CO 측 trace 연결이 필요합니다. artifact9991542931(strix-reports) 보존 확인. 원본 job log SHA256 783ab7b1ad3ffaba368a3976602d43c67c0831b8e4610671a4a640bcd6b74128. 재실행이나 timeout 변경은 하지 않았습니다.

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.

1 participant