diff --git a/CHANGELOG.md b/CHANGELOG.md index 43020db98e..1f719ae227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ this file. The format follows Keep a Changelog, and versioned releases follow Semantic Versioning where the repository publishes a release. ## [Unreleased] +- Dispatch exact-head terminal source-check failures directly to the bounded RCA + repair path, ahead of ordinary review feedback. The scheduler now completes + paginated check inventories, excludes review/control-plane failures, retains + workflow identity through REST fallback, and isolates one PR's metadata + failure without blocking other eligible repairs. The worker refetches the + live head and failed-check evidence before granting a sealed changed-path + scope, so stale or unsupported dispatches fail closed. - Fail closed when the first top-level Noema JSON candidate is malformed, preventing a later approval object from overriding malformed preface data; multiple-object output remains supported when its first object is valid. diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 76d85b949b..b5fa7310cc 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -91,6 +91,14 @@ flowchart LR | G-11 | UI가 있는 제품의 Figma/Storybook inventory와 token/interaction/i18n 테스트는 중앙 control plane에서 소유할 수 없다. Figma File ID는 이 저장소 ADR에서 N/A다 | 제품 간 UI가 달라지고 운영자 onboarding이 일관되지 않는다 | 각 UI repo가 실제 Figma File ID ADR, Storybook inventory, shared token package, keyboard/edge/i18n tests를 소유한다 | | G-12 | CSAP/SOC 2 통제 목표와 PII masking 대안은 doctoring에 흩어져 있으며 evidence-to-control mapping의 live completeness가 미확인이다 | PII를 마스킹하면 업무가 멈추고, 원문 접근을 허용하면 감사·유출 위험이 커진다 | consent/purpose/access lease, field-level encryption/tokenization, redaction-at-egress, audit/revocation와 CSAP/SOC 2 evidence map을 구현한다 | | G-13 | hourly scheduler는 존재하지만 no-op/credential unavailable/queued Checks의 customer next action을 모든 caller가 동일한 receipt로 내는지 미확인이다 | 자동화가 실패해도 운영자가 무엇을 고쳐야 하는지 알 수 없다 | `skipped_credential_unavailable` receipt와 다음 행동 문구를 exact-head Checks로 검증하고, bounded receipt schema, retry floor, single-flight, no secret fallback을 모든 caller contract test로 고정한다 | + +G-13의 failed-check 경로는 이제 현재 HEAD의 terminal source-check 실패를 +일반 review feedback보다 먼저 bounded RCA로 보낸다. 상태 context 전체 페이지를 +확인하지 못한 PR은 해당 PR만 fail closed로 대기하며, review/control-plane 실패는 +source repair 근거로 사용하지 않는다. 중앙 worker는 payload를 권한으로 신뢰하지 +않고 live head와 실패 근거를 다시 확인한 뒤 sealed changed-path scope만 제공한다. +따라서 이 개선은 receipt 일관성·credential-unavailable 운영 증거·모든 caller의 +single-flight 검증이 남아 있다는 Gap 자체를 닫지는 않는다. | G-14 | release/changelog/version 증거가 각 PR에 분산되고 현재 central repo 보호 main의 release candidate가 명확하지 않다 | 운영자는 어떤 기능이 supportable release인지 확인할 수 없다 | merge 후 release readiness ledger, CHANGELOG, semantic version/tag, rollback/operability evidence를 함께 갱신한다 | | G-15 | 첨부파일 처리 경계가 제품별로 다르고, 1MB 상한은 업무 데이터와 맞지 않으며 미지원 MIME/컨테이너가 parser registry에서 명시적으로 pending/quarantine 되는지 확인되지 않았다. 현재 20MB 초과 파일 가능성과 PDF/HWP/HWPX·이미지·압축파일의 parse/sidecar 흐름을 하나의 exact contract로 묶지 못했다 | 큰 업무 첨부를 거부하거나 파싱 실패를 조용히 잃으면 고객의 메일·문서 업무가 중단된다 | naruon/newsdom-api 소유 PR에서 streaming upload, configurable bounded limit above 20MB, MIME sniffing, parser capability registry, quarantine/retry, source-position provenance, and ADR를 추가하고 size/unsupported-type/zip-bomb tests를 required evidence로 만든다 | diff --git a/tests/test_pr_review_fix_scheduler_direct_rca_regressions.py b/tests/test_pr_review_fix_scheduler_direct_rca_regressions.py index c5a0c965b5..91e4865a16 100644 --- a/tests/test_pr_review_fix_scheduler_direct_rca_regressions.py +++ b/tests/test_pr_review_fix_scheduler_direct_rca_regressions.py @@ -72,6 +72,31 @@ def test_failed_check_rca_precedes_ordinary_review(monkeypatch: Any, is_draft: b assert captured["resolve_conflict"] is False +@pytest.mark.parametrize( + ("is_draft", "expected_reason"), + [ + (True, "draft PR"), + (False, "merge conflict is not authorized for repair"), + ], +) +def test_conflicted_pr_stays_fail_closed_without_repair_authority( + is_draft: bool, + expected_reason: str, +) -> None: + """A conflict cannot be widened into RCA without conflict-repair authority.""" + pr = make_pr(is_draft=is_draft) + pr["mergeStateStatus"] = "DIRTY" + pr["reviews"] = {"nodes": []} + args = fix.parse_args( + ["--repo", "owner/repo", "--base-branch", "main", "--dry-run"] + ) + + assert fix.inspect_pr("owner/repo", pr, args) == ( + "skip", + (expected_reason,), + ) + + def test_scan_queue_control_plane_failure_does_not_trigger_rca() -> None: """A failed queue scanner cannot consume a source-repair retry by itself.""" pr = make_pr()