Skip to content

[Refactor] fan-in 등록 창을 원장 정책 신선도로 바꾸고 광주 보관 KRIC 시간표를 매일 재확인 - #930

Merged
AquilaXk merged 6 commits into
mainfrom
refactor/freshness-window-daily-reverify-929
Oct 4, 2026
Merged

AquilaXk merged 6 commits into
mainfrom
refactor/freshness-window-daily-reverify-929

Conversation

@AquilaXk

@AquilaXk AquilaXk commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Related issue

Refs #929
Refs #870

Summary

  • Problem:
    • 2026-10-04T04:31:38.280Z에 전국 후보를 갱신하면 fan-in이 admission freshness mismatch for busan-transportation-accessibility로 실패한다.
      • fan-in은 inventory 등록 evidence의 freshUntil로 등록 확인 창을 본다. 이 값은 등록기가 수집 시각 +1일로 쓴다.
      • 그래서 원장 정책 신선도가 P90D(접근성)·P30D(지역 시간표)인 원천 11개도 하루가 지나면 후보를 막는다.
    • 광주 보관 KRIC 파일(kric-nationwide-timetable-file)은 만료 6시간 전에만 DUE가 된다. 현재 head 기준으로 갱신 기회는 2026-10-09T20:51Z 한 번뿐이다.
  • Outcome (QA 결정 2026-10-04):
    • D1: fan-in 등록 확인 창의 끝을 원장 정책 신선도로 본다. 고른 원천은 신선도 정책 클래스가 정확히 하나여야 한다.
    • D2(a): 광주 보관 KRIC 파일은 관측 후 P1D가 지나면 DUE다. 만료 계산은 그대로다.

Changes

  • tools/datapack/build-current-five-region-source-fan-in.mjs
    • 등록 확인 창의 끝을 evidence.freshUntil ?? snapshot.freshnessExpiresAt에서 snapshot.freshnessExpiresAt(원장 정책 신선도)로 바꿨다. 관측 시각 검사(미래 관측 실패)는 그대로다.
    • assertFanInSourceFreshnessPolicy 신규: 고른 원천마다 정책 클래스가 0개면 fan-in freshness policy missing, 2개 이상이면 ambiguous로 실패한다. 원장 head freshnessExpiresAt이 deriveFreshnessExpiresAt으로 구한 상한(등록 기준 시각 + 정책 기간)을 넘어도 fan-in freshness exceeds policy로 실패한다(리뷰 F1).
    • CLI는 --freshness-policy가 필수이고, 검사를 통과해야 출력을 쓴다.
    • fan-in 산출물 형식과 재구성 경로(ownership ledger, RC의 candidate source set 검증)는 바뀌지 않는다.
      • RC FINAL은 이미 validateSourceSnapshotFreshness로 인용 원천의 정책 클래스 유일성과 유도값을 검사한다(SOURCE_FRESHNESS_POLICY_MISSING).
  • tools/datapack/refresh-nationwide-candidate.mjs: fan-in에 release/product-gates/datapack-freshness-sla.json을 넘긴다.
  • tools/ci/decide-retained-gwangju-timetable-refresh.mjs: refreshDueAt = min(관측 + RETAINED_GWANGJU_DAILY_REVERIFICATION_PERIOD(P1D), 만료 - alertBeforePackExpiry). P1D가 정책 cadence(P7D)보다 짧은 근거(QA D2(a))를 주석과 계약 테스트로 고정했다(리뷰 F2).
  • 테스트 pin 수정. P1D 결정에 묶여 있던 단언과 근거는 아래와 같다.
    • build-current-five-region-source-fan-in.test.mjs
    • decide-retained-gwangju-timetable-refresh.test.mjs: CURRENT 기준 시각과 저장소 판정 refreshDueAt을 관측 +P1D로 바꿨다. 짧은 유효기간 head에서는 경보 창이 우선함을 따로 단언한다.

Scope

Included

  • fan-in 등록 창, 정책 클래스 검사, 광주 보관 decide 창, 테스트

Excluded

Ownership / dependencies

Contract & Compatibility

  • Source / API / schema contract
    • fan-in CLI에 필수 인자 --freshness-policy가 추가됐다. 호출자는 refresh-nationwide-candidate.mjs 하나뿐이고 같은 PR에서 고쳤다.
  • Artifact / provenance identity: fan-in·후보 산출물 형식은 바뀌지 않는다.
  • Backward compatibility
    • 이전에 막히던 후보가 이제 만들어진다. 원장 신선도가 지난 원천은 예전처럼 실패한다.
  • Migration or cutover: 없음

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: fan-in CLI 입력 추가(내부 도구)
  • route-map artifact / product gate: 변경 없음
  • promotion request id: 해당 없음

Verification

Check Result / Evidence
Focused RED → GREEN D1: 정책 검사 stub 상태에서 새 테스트 3건과 CLI 테스트 1건 실패 → 26/26 통과(fan-in·refresh). D2: 새 D2 테스트와 고친 기준 테스트 3건 실패 → retained decide·register·controller 20/20 통과
Affected integration fan-in 소비자 테스트(ownership ledger, candidate source set, release pin drift, nationwide candidate, capital facility operation, same-raw reverification) 99/99 통과
실측 저장소 사본에서 refresh-nationwide-candidate --evaluated-at 2026-10-04T07:58:14.488Z --release-sequence 127 실행: 변경 전에는 fan-in에서 실패했고, 변경 후에는 끝까지 성공했다(8개 출력, 가장 이른 원천 만료 korail-metropolitan-timetable-file 2026-10-10T00:05:31.571Z). 커밋하지 않았다. decide-retained-gwangju-timetable-refresh: 현재 head refreshDueAt 2026-10-04T02:51:29.225Z, DUE
Required CI Data contracts 결과는 PR check에서 확인
Live provider / release Not required — reason: 외부 호출이 없다
Security / data integrity 실패 조건을 약화하지 않는다. 원장 신선도가 지난 원천은 그대로 실패한다. 정책 클래스가 없거나 둘 이상이면 새로 실패한다.

Not run

Risk

  • Level: High
  • Main risk
    • 등록 창이 원천별 정책 기간만큼 길어진다(QA가 승인한 신선도 기준 변경).
    • 광주 보관 원문 관측 객체(약 231MB 원본에서 만든 관측)를 매일 새로 게시한다(OCI 사용량 증가).
  • Failure behavior: 원장 정책 신선도가 지난 원천, 정책 클래스가 없거나 둘 이상인 원천은 fan-in에서 실패한다.
  • Candidate / admission / publication state on failure: 변경 없음
  • Fallback or degraded-success path introduced: No

Rollout / Recovery

  • Rollout or promotion: 병합 뒤 다음 후보 갱신부터 적용된다.
  • Monitoring / success signal: 후보 갱신 run이 fan-in 단계를 통과하고, 광주 보관 workflow가 매일 DUE로 실행된다.
  • Rollback or recovery: 이 커밋들을 되돌린다.
  • Existing artifact / schema compatibility after rollback: 영향 없음

Review focus

  • fan-in 등록 창이 원장 정책 신선도와 정확히 같은 시각에 닫히는지
  • 정책 클래스 검사가 fan-in을 새로 쓰는 경로(CLI)에서 빠짐없이 도는지

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 상태를 확인했습니다.

QA 결정 D1(2026-10-04): 등록 evidence의 freshUntil(등록기가 수집 +1일로 쓰던 값)이 접근성(P90D)·지역 시간표(P30D)
원천까지 하루마다 후보 생성을 막았다(2026-10-04T04:31Z 실측: admission freshness mismatch for busan-transportation-accessibility).
- fan-in 등록 확인 창의 끝을 원장 head freshnessExpiresAt(정책 클래스로 유도·검증된 값)으로 본다. 관측 시각 검사는 그대로다.
- assertFanInSourceFreshnessPolicy: fan-in이 고른 원천마다 신선도 정책 클래스가 정확히 하나여야 한다(없음·중복 실패).
  fan-in CLI는 --freshness-policy가 필수이고, 검사를 통과해야 출력을 쓴다. 전국 후보 갱신은 release/product-gates/datapack-freshness-sla.json을 넘긴다.
- P1D 결정에 묶인 테스트 3곳을 고쳤다(#862 "만료된 등록 창은 실패", native schedule·generic admission의 stale evidence 단언).
  같은 입력이 이제 통과하고, 원장 신선도가 지난 경우만 snapshot freshness mismatch로 실패함을 단언한다.
RED: 정책 검사 stub 상태에서 새 테스트 3건과 CLI 테스트 1건 실패.
실측: 이 변경으로 2026-10-04T07:58:14.488Z 후보 갱신(seq 127)이 끝까지 성공했다(가장 이른 원천 만료 korail-metropolitan-timetable-file 2026-10-10T00:05:31.571Z).

Refs #929
Refs #870
QA 결정 D2(a)(2026-10-04): 만료 6시간 전에만 DUE여서 현재 head(관측 2026-10-03T02:51Z) 기준 갱신 기회가
2026-10-09T20:51Z 한 번뿐이었다.
- refreshDueAt = min(관측 + P1D, 만료 - alertBeforePackExpiry). 만료 시각 계산은 그대로다.
- 만료 6시간 전 기준만 고정하던 테스트 2곳을 고쳤다(CURRENT 기준 시각, 저장소 판정 refreshDueAt). 경보 창이 하루보다
  먼저 오는 짧은 유효기간 head에서는 경보 창이 우선함을 따로 단언한다.
RED: 새 D2 테스트와 고친 기준 테스트 3건이 변경 전 코드에서 실패.
실측: 현재 저장소 head는 refreshDueAt 2026-10-04T02:51:29.225Z, state DUE로 판정된다.

Refs #929
Refs #870
@coderabbitai

coderabbitai Bot commented Oct 4, 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: 11ab231c-4f38-41c5-a0fa-0bd99ee17160
  • 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.

@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 fan-in accepts only one policy class per source and ends the window at ledger freshness PASS tools/datapack/build-current-five-region-source-fan-in.mjs:207
AC-2 Gwangju retained decide is DUE one day after observation and expiry math is unchanged PASS tools/ci/decide-retained-gwangju-timetable-refresh.mjs:54
AC-3 Test pins tied to the P1D decision are changed with a recorded rationale PASS tools/datapack/build-current-five-region-source-fan-in.test.mjs:559
🛡️ Adversarial audit evidence
  • Falsifiability verified: Yes (Mutants on the min, plus, minus and window checks were all killed.)
  • Hollow assertions detected: 0
  • Production backdoors detected: 0
  • Ground-truth sources verified:
    • Linked issue 929 QA decisions D1 and D2
    • release/product-gates/datapack-freshness-sla.json source classes
🤖 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-retained-gwangju-timetable-refresh.mjs:9-9`
  Would it help to name the QA decision in the constant comment or to derive the
  period from a policy field, so a future cadence change is not missed here?
- `@tools/datapack/build-current-five-region-source-fan-in.mjs:493-503`
  Could this function also call deriveFreshnessExpiresAt from freshness-policy.mjs for
  each selected head and reject a mismatch, with a test that inflates
  freshnessExpiresAt beyond the class cadence? That would make the bounded-window
  claim hold at fan-in itself instead of relying on a later stage.

---

Outside diff comments:
None.

ℹ️ Review info
⚙️ Run configuration

Configuration used: CLAUDE.md, AGENTS.md and PR template of easysubway-data worktree

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 930

Base:
main@abd73720cc28bac34041192789c8a5fe5712b0c8

Head:
refactor/fan-in-window-policy-929@1f2a11f166c23060b0b36253af3e0f8c7c019098

Command:
node aquila-review/scripts/post-review.mjs input.json payload-1.json --repo AquilaXk/easysubway-data --pr 930

📥 Commits

Reviewing files changed in this pull request between:

  • Base: abd73720cc28bac34041192789c8a5fe5712b0c8
  • Head: 1f2a11f166c23060b0b36253af3e0f8c7c019098
📒 Files selected for processing (5)
  • tools/ci/decide-retained-gwangju-timetable-refresh.mjs
  • tools/ci/decide-retained-gwangju-timetable-refresh.test.mjs
  • tools/datapack/build-current-five-region-source-fan-in.mjs
  • tools/datapack/build-current-five-region-source-fan-in.test.mjs
  • tools/datapack/refresh-nationwide-candidate.mjs
📜 Review details

Comment thread tools/ci/decide-retained-gwangju-timetable-refresh.mjs Outdated
Comment thread tools/datapack/build-current-five-region-source-fan-in.mjs Outdated
#930 리뷰 F1: 정책 검사가 클래스 개수만 봐서, 원장 freshnessExpiresAt이 부풀려진 head는 fan-in 시점에 창을 넓혔다
(발행 FINAL에서야 다시 잡혔다).
- assertFanInSourceFreshnessPolicy가 원장 ledger를 받아, 각 head의 freshnessExpiresAt이 deriveFreshnessExpiresAt
  (클래스 basisField, 재확인 클래스는 더 늦은 재확인 시각, 정책 cadence·컷오버 규칙)으로 구한 상한 이하인지 본다.
  넘으면 "fan-in freshness exceeds policy for <원천>: <원장> > <상한>"으로 실패한다. ledger가 없으면 실패한다.
- fan-in CLI가 입력 ledger를 넘긴다.
RED: 상한보다 1ms 늦은 원장 head가 변경 전 코드에서 통과해 새 테스트 실패.
실측: 현재 저장소 원장 head 28개 모두 상한과 정확히 같고, 실제 입력으로 fan-in CLI(현재 시각)가 성공한다.

Refs #929
…로 고정

#930 리뷰 F2: 일일 재확인 주기가 밀리초 리터럴이라 정책 클래스 cadence(P7D)와 왜 다른지 기록이 없었다.
- RETAINED_GWANGJU_DAILY_REVERIFICATION_PERIOD = "P1D"로 내보내고 addCadence로 계산한다. 주석에 QA 결정 D2(a)(2026-10-04)와
  "만료는 cadence, 재확인 시작은 P1D"라는 근거를 적었다.
- 계약 테스트: 상수가 P1D이고 저장소 confirmationPolicy cadence보다 짧으며, cadence가 바뀌어도 재확인 시각은 관측 + P1D다.
RED: 상수 export 부재로 테스트 실패.

Refs #929
@AquilaXk
AquilaXk marked this pull request as ready for review October 4, 2026 09:10
S6959·S1940. 원장 freshnessExpiresAt이 시각으로 읽히지 않으면 지금처럼 실패하도록 Number.isFinite 검사를 따로 둔다.
fan-in 테스트 14/14 통과.

Refs #929
@AquilaXk AquilaXk added the automerge FIFO 병합 큐 대상 — 코디네이터가 순서대로 update-branch 후 auto-merge label Oct 4, 2026
@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

No description provided.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@AquilaXk
AquilaXk merged commit bd41ee1 into main Oct 4, 2026
11 checks passed
@AquilaXk
AquilaXk deleted the refactor/freshness-window-daily-reverify-929 branch October 4, 2026 13:39
@sonarqubecloud

sonarqubecloud Bot commented Oct 4, 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

Development

Successfully merging this pull request may close these issues.

1 participant