Skip to content

[Fix] 전국 후보 갱신이 멈춘 원장 쓰기 자동화를 PT2H까지만 기다리고 스케줄러 App dispatch로 깨울 수 있게 수정 - #1035

Merged
AquilaXk merged 4 commits into
mainfrom
fix/candidate-refresh-stuck-writer-dispatch-1032
Oct 8, 2026
Merged

AquilaXk merged 4 commits into
mainfrom
fix/candidate-refresh-stuck-writer-dispatch-1032

Conversation

@AquilaXk

@AquilaXk AquilaXk commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Related issue

Closes #1032
Refs #870

Summary

  • Problem: 원장을 바꾸는 자동 갱신(KRIC Refresh KRIC facility snapshot #1016, topology Refresh current capital topology inputs #1021 등)이 병합된 뒤 전국 후보가 다시 만들어지지 않아 Data Pack Release가 2026-10-07T19:15Z부터 release-pin-drift((a)(b)(d)(e))로 매번 실패했다. 후보 갱신 판정은 입력 변경을 정확히 감지했지만(run 37678888180: BLOCKED_BY_PENDING_PR, stalePaths source-snapshots.json·source-inventory.json) 원장 쓰기 자동화가 열려 있는 동안 기다리고 그 기다림에 상한이 없었다. 기다리게 한 대상이 스스로 멈춰 있었다: 서울 claim automation/639-…-37673242104(게시 뒤 Finalize 게이트 오류로 PR 없이 남아 CLAIM_ORPHAN_PUBLISHED_UNREGISTERED 사람 확인 대기), 광주 PR Refresh retained Gwangju timetable #1019(head가 [Fix] 광주 정기 갱신 head 앞섬에서 깨지던 fixture 롤포워드를 native admission 결속으로 수정 #1023 수정 전이라 CI 빨강, 재생성이 24시간 3회 상한 AUTOMATION_PR_RECREATE_LOOP). 또 후보 갱신은 GitHub schedule로만 돌아 6~7시간 간격이었다.
  • Outcome: (1) 후보 갱신이 원장 쓰기 PR·claim을 PT2H까지만 기다린다. (2) 수도권 topology 등록 전에는 후보를 만들지 않는다. (3) 외부 스케줄러 App이 입력 없이 workflow_dispatch로 깨울 수 있다.

Changes

  • automation-pr-state.mjs: LEDGER_WRITER_WAIT_LIMIT = "PT2H". pendingLedgerWriters가 maxAgeMs·now·branchTimes를 받으면 브랜치 마지막 커밋이 상한보다 오래된 PR·claim을 expired로 알리고 기다리지 않는다(상한과 같은 나이는 기다림, 나이 미상·미래 시각은 기다림, 잘못된 시각은 실패, 상한을 안 주면 기존 동작과 출력 동일). branchCommitTimes는 로컬 origin/* ref의 committer date를 UTC로 읽는다.
  • decide-nationwide-candidate-refresh.mjs: 위 상한을 적용하고 무시한 대상을 expiredBlockers·expired_blockers 출력으로 남긴다. --actor를 받아 스케줄러 App의 dispatch는 schedule과 같이 CURRENT에서 아무것도 하지 않는다(FORCED 아님).
  • decide-nationwide-candidate-refresh.mjs·decide-capital-topology-registration.mjs: STALE이고 기다릴 원장 쓰기 자동화가 없어도 보호 admission(inventory)의 수도권 topology snapshot이 원장에 등록되지 않았으면 BLOCKED_BY_PENDING_REGISTRATION으로 후보를 만들지 않는다(capitalTopologyRegistrationState). 등록 전 후보는 후보 spec의 capitalTopologyAdmission(옛 reverification, 20261005)이 inventory admission(20261007)과 어긋나 PR CI의 candidate build spec release identity 테스트가 실패하고, 그 후보 PR이 열려 있는 동안 후보 갱신이 막힌다(재결속 PR [Data] 파생 산출물을 현재 원천 head에 다시 결속 #1037 head에서 후보를 모의 재생성해 확인).
  • ledger-writers-idle.mjs: --max-age로 push 직전 재확인에도 같은 규칙을 쓴다(후보 갱신 workflow만 사용, 다른 workflow 호출은 변경 없음).
  • nationwide-candidate-refresh.yml: dispatch 입력을 선택으로, job 게이트를 (사람 dispatch) || DATAPACK_SCHEDULED_CANDIDATE_REFRESH == 'true'로, 판정·plan에 ACTOR: github.triggering_actor 전달, 멈춘 대상 경고 step 추가.
  • scheduled-release-authority.mjs·plan-datapack-release-chain.mjs·release-request.schema.json: 정기 역할은 schedule 또는 스케줄러 App(easysubway-release-chain[bot])이 시작한 workflow_dispatch에서만 인정. dispatch run의 gateRun에 actor를 결속하고 GitHub run 기록의 actor·triggering_actor가 둘 다 그 App이어야 통과(사람이 재실행하면 거부). App은 사람 역할(2인 승인)을 입력으로 요청할 수 없고, 사람은 정기 역할을 입력으로 넣을 수 없다.
  • 주석·테스트: automation-pr-recreate 주석 정정, 입력 선택화에 맞춘 workflow 계약 테스트 갱신.

Scope

Included

  • 후보 갱신 판정·재확인의 대기 상한, 스케줄러 App dispatch 허용과 행위자 결속

Excluded

Ownership / dependencies

Contract & Compatibility

  • Source / API / schema contract: release-request.schema.json의 gateRun에 선택 속성 actor 추가(workflow_dispatch run에서만 필수, 코드가 강제). 외부 API 사용 없음(카탈로그 대조 대상 아님).
  • Artifact / provenance identity: 변경 없음. 커밋된 seq128 release request의 gateRun(event schedule)은 그대로 유효.
  • Backward compatibility: 상한·--actor·--max-age를 주지 않으면 이전 동작과 출력이 같다. 사람 dispatch는 항상 돌고 2인 역할 입력 검증은 plan이 계속 한다(빈 입력은 실패).
  • Migration or cutover: 없음. 스케줄러 등록 전에는 GitHub schedule과 사람 dispatch로만 돈다.

Version impact

  • no version change
  • datapack release only
  • route-map artifact change
  • data contract change
  • product gate JSON change
  • CI workflow·계약 테스트 change

Product gate impact

  • release/product-gates/** 영향 없음
  • 변경한 gate의 근거를 갱신했습니다.
  • 검증되지 않은 지원 범위 claim을 추가하거나 확대하지 않습니다.

Provenance impact

  • source inventory·geometry provenance manifest 영향 없음
  • 제공처·라이선스·갱신 시점·적용 범위를 갱신했습니다.
  • 공식 source로 확인되지 않은 값을 배포 artifact에 추가하지 않습니다.

Version decision

  • datapack version: 변경 없음
  • data contract: 변경 없음
  • route-map artifact / product gate: 변경 없음
  • promotion request id: 없음

Verification

Check Result / Evidence
Focused RED → GREEN RED(구현 전): node --test tools/ci/ledger-writers-idle.test.mjs → SyntaxError: ... does not provide an export named 'LEDGER_WRITER_WAIT_LIMIT', tools/datapack/lib/scheduled-release-authority.test.mjs → ... 'SCHEDULER_APP_LOGIN', decide·plan·workflow 계약 테스트 다수 실패. GREEN(구현 후): ledger-writers-idle 8/8, decide-nationwide-candidate-refresh 14/14, scheduled-release-authority 11/11, plan-datapack-release-chain 24/24, nationwide-candidate-refresh-workflow 15/15
Affected integration node --test --test-concurrency=1로 영향 파일 + workflow 열거 테스트 19개 파일 303/303 통과(automation-pr-policy·recreate·list-bounded, build/refresh-nationwide-candidate, verify-release-request-binding, scheduler heartbeat·gate 등). actionlint 오류 없음
Required CI PR 체크 참조
Live provider / release Not required — reason: 판정 도구는 저장소 파일과 GitHub PR 목록만 읽는다. 현재 main + 열린 PR 기준 실행 결과: {"state":"BLOCKED_BY_PENDING_PR",…,"blockedBy":[1030,1031]}(둘 다 생성 15분 이내라 상한 안에서 기다림, 상한 규칙의 비회귀 확인). 후보 갱신 도구를 파생 재결속 PR head(#1033 d1b0d2c)에서 로컬로 돌리면(OCI 발행만 건너뜀) release-pin-drift (a)(b)(d)(e)가 모두 통과, main 그대로에서는 4건 모두 실패. 같은 모의에서 등록 전 후보는 candidate build spec release identity가 capital current topology admission identity mismatch로 실패해 등록 대기를 추가했다. 등록 대기 판정은 현재 main에서 {"state":"BLOCKED_BY_PENDING_REGISTRATION",…,"registrationSnapshotId":"capital-route-topology-20261007"}를 출력한다
Security / data integrity 정기 역할의 행위자 결속: 사람의 workflow_dispatch에 정기 역할 입력 거부, App의 사람 역할 요청 거부, 재실행(triggering_actor≠App) 거부, actor 없는 dispatch gateRun 거부를 각각 테스트로 고정. GITHUB_TRIGGERING_ACTOR는 Actions 컨텍스트 값이며 입력이 아니다

Not run

  • Check: 로컬 deterministic-release 클래스 전체와 refresh-nationwide-candidate 실제 OCI 발행
  • Reason: apps/mobile 외부 fixture와 OCI PAR 시크릿이 로컬에 없다(EXTERNAL_FIXTURE_MISSING). full regression은 CI에 위임
  • Rerun owner / condition: required CI, 병합 후 후보 갱신 run

Risk

  • Level: High
  • Main risk: 등록이 멈추면 후보가 만들어지지 않는다(등록 workflow의 실패 보고와 admission 만료 경보가 알린다). 상한을 넘긴 PR이 나중에 병합되면 후보가 다시 낡는다. 그러면 다음 판정이 다시 STALE이므로 후보를 새로 만든다(수렴하지만 후보 PR이 한 번 더 생길 수 있다). 정기 역할의 새 경로(App dispatch)가 잘못 열리면 사람 승인 없는 후보가 만들어진다.
  • Failure behavior: 나이를 모르는 대상·잘못된 시각·입력 오류는 무시하지 않고 기다리거나 실패한다. 무시한 대상은 workflow 경고와 출력에 남고 소유 workflow의 실패 보고([Fix] 원천 갱신 workflow 실패를 이슈로 드러내고 광주 보관 시간표 갱신에 수동 실행 경로 추가 #926)가 계속 사람에게 알린다.
  • Candidate / admission / publication state on failure: 후보 생성은 기존처럼 push 직전 재확인에서 새 원장 쓰기가 감지되면 올리지 않고 다음 실행에 맡긴다. 발행·승격 경로는 건드리지 않는다.
  • Fallback or degraded-success path introduced: No (멈춘 대상을 성공으로 덮지 않고 기다림만 끝낸다. 실패 보고는 유지)

Rollout / Recovery

  • Rollout or promotion: 병합 즉시 GitHub schedule·사람 dispatch에 적용. 스케줄러 App dispatch는 platform 계약에 이 workflow를 등록·배포한 뒤부터(별도 PR).
  • Monitoring / success signal: 후보 갱신 run의 expired_blockers 출력·경고, 후보 PR 생성, RC Validate ITX-청춘 coverage contract green
  • Rollback or recovery: 이 PR revert. 커밋된 후보·release request 형식은 바뀌지 않는다(actor는 dispatch run에서만 생긴다).
  • Existing artifact / schema compatibility after rollback: dispatch run으로 만든 후보(gateRun.actor 포함)가 main에 들어온 뒤 revert하면 그 후보의 gateRun 형식을 거부하므로 그 후보는 schedule run으로 다시 만들어야 한다.

Review focus

  • releaseRoleEventViolations·gateRunViolations·gateRunRecordViolations의 행위자 결속이 사람 승인 우회를 막는지
  • pendingLedgerWriters의 상한 경계(같은 나이=대기, 나이 미상=대기)와 branchCommitTimes가 실제 checkout의 refs/remotes/origin/*를 읽는지
  • App dispatch가 FORCED로 처리되지 않는지(2시간마다 불필요한 후보 PR 방지)

Checklist

  • 이슈 범위와 실제 diff가 일치합니다.
  • 관련 없는 변경이나 다른 owner의 surface를 포함하지 않았습니다.
  • 위험에 필요한 검증과 미실행 사유를 기록했습니다.
  • 실패·호환성·promotion·recovery 동작이 명확합니다.
  • current failure를 이전·stale·alternate 결과의 성공으로 바꾸지 않습니다.
  • GitHub PR Review 객체가 있는지 확인했습니다. CodeRabbit status check만으로는 리뷰 완료로 보지 않습니다.
  • CodeRabbit Review 객체가 없으면 지원되는 Codex CLI 폴백 Review를 단일 GitHub PR Review로 게시했습니다.
  • datapack 배포 영향이 있는 경우 release workflow 상태를 확인했습니다.

… 수정 (#1032)

원장을 쓰는 자동화(PR·claim)가 스스로 풀 수 없는 상태로 멈추면 후보 갱신이 영구히 기다려
입력이 바뀐 뒤에도 후보가 다시 만들어지지 않았다. 대기에 상한(PT2H)을 두고, 정기 역할을
스케줄러 App의 workflow_dispatch에서도 쓸 수 있게 하되 행위자를 gateRun에 결속한다.

Refs #870
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c6afd375-bed2-4be4-8d24-6446fb721dd9
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

보호 admission의 topology snapshot이 원장에 등록되기 전에 후보를 만들면 후보의
capitalTopologyAdmission(옛 reverification)이 inventory admission과 어긋나 PR CI의
후보 build 테스트가 실패하고, 그 후보 PR이 열려 있는 동안 후보 갱신이 막힌다.

Refs #870

@AquilaXk AquilaXk left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🎯 Linked issue & acceptance criteria audit
ID Acceptance criterion Status Evidence
AC-1 Writers open longer than PT2H are not waited for and are reported PASS tools/ci/automation-pr-state.mjs:121
AC-2 Unregistered topology admission blocks the candidate as pending registration PASS tools/ci/decide-nationwide-candidate-refresh.mjs:90
AC-3 The scheduler App may wake the refresh by workflow_dispatch without inputs PASS .github/workflows/nationwide-candidate-refresh.yml:41
AC-4 Scheduled role only for schedule or an App dispatch with actor binding PASS tools/datapack/lib/scheduled-release-authority.mjs:129
🛡️ Adversarial audit evidence
  • Falsifiability verified: Yes (Actor, role, and age-cap mutants, including the equal-age boundary, were killed.)
  • Hollow assertions detected: 0
  • Production backdoors detected: 0
  • Ground-truth sources verified:
    • Issue 1032 criteria and the external scheduler heartbeat dispatcher login
    • Run record fields actor and triggering_actor checked by the release candidate gate
🤖 Prompt for all review comments with AI agents
Could we verify each finding against the current code and keep only those that
still apply? Could we make the smallest validated fix and briefly note why any
finding no longer applies?

Inline comments:
- `@tools/ci/decide-nationwide-candidate-refresh.mjs:90-93`
  Could the notice or the owning workflow report state how long a registration
  may stay pending before a person is alerted, so the wait stays visible?
- `@tools/ci/plan-datapack-release-chain.mjs:51-51`
  Could the workflow also pass github.actor so the plan and decision require both
  values to equal the App, matching the later run record check?

---

Outside diff comments:
None.

ℹ️ Review info
⚙️ Run configuration

Configuration used: aquila-review SKILL.md, repository CLAUDE.md, and PR 1035 and issue 1032 text

Review profile: ASSERTIVE

Plan: Aquila fallback

Run ID: N/A (Aquila fallback)

Review source: Aquila CLI

Repository visibility: public GitHub repository

Trigger:
Discovery review requested for the current change set of PR 1035

Base:
main@7aace42175f995bab8acff4e30a9fa7376add9d8

Head:
fix/candidate-refresh-stuck-writer-dispatch-1032@01cc22ffe90b846a789eb225460c75ad07b8b968

Command:
node aquila-review/scripts/compile-review-payload.mjs input.json --output payload-1.json

📥 Commits

Reviewing files changed in this pull request between:

  • Base: 7aace42175f995bab8acff4e30a9fa7376add9d8
  • Head: 01cc22ffe90b846a789eb225460c75ad07b8b968
📒 Files selected for processing (7)
  • .github/workflows/nationwide-candidate-refresh.yml
  • tools/ci/automation-pr-state.mjs
  • tools/ci/decide-nationwide-candidate-refresh.mjs
  • tools/ci/ledger-writers-idle.mjs
  • tools/ci/plan-datapack-release-chain.mjs
  • tools/datapack/lib/scheduled-release-authority.mjs
  • tools/datapack/schema/release-request.schema.json
📜 Review details

Comment thread tools/ci/decide-nationwide-candidate-refresh.mjs
Comment thread tools/ci/plan-datapack-release-chain.mjs
@AquilaXk
AquilaXk marked this pull request as ready for review October 8, 2026 00:33
@AquilaXk AquilaXk added the automerge FIFO 병합 큐 대상 — 코디네이터가 순서대로 update-branch 후 auto-merge label Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

No description provided.

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

No description provided.

@AquilaXk
AquilaXk merged commit ed38c69 into main Oct 8, 2026
12 checks passed
@AquilaXk
AquilaXk deleted the fix/candidate-refresh-stuck-writer-dispatch-1032 branch October 8, 2026 01:30
@sonarqubecloud

sonarqubecloud Bot commented Oct 8, 2026

Copy link
Copy Markdown

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

Labels

automerge FIFO 병합 큐 대상 — 코디네이터가 순서대로 update-branch 후 auto-merge

Projects

None yet

1 participant