Repository navigation
[Refactor] fan-in 등록 창을 원장 정책 신선도로 바꾸고 광주 보관 KRIC 시간표를 매일 재확인 - #930
Conversation
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
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
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. Comment |
AquilaXk
left a comment
There was a problem hiding this comment.
Actionable comments posted: 2
🎯 Linked issue & acceptance criteria audit
- Linked issue: #929 Reflect QA decisions on fan-in window, daily Gwangju check, scheduled roles
- Goal summary: Apply QA decisions D1 and D2 so candidate refresh is not blocked by a 1-day window.
- Acceptance criteria verified: 3/3
| 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 D2release/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.mjstools/ci/decide-retained-gwangju-timetable-refresh.test.mjstools/datapack/build-current-five-region-source-fan-in.mjstools/datapack/build-current-five-region-source-fan-in.test.mjstools/datapack/refresh-nationwide-candidate.mjs
📜 Review details
#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
S6959·S1940. 원장 freshnessExpiresAt이 시각으로 읽히지 않으면 지금처럼 실패하도록 Number.isFinite 검사를 따로 둔다. fan-in 테스트 14/14 통과. Refs #929
|
No description provided. |
|
No description provided. |
|



Related issue
Refs #929
Refs #870
Summary
admission freshness mismatch for busan-transportation-accessibility로 실패한다.freshUntil로 등록 확인 창을 본다. 이 값은 등록기가 수집 시각 +1일로 쓴다.kric-nationwide-timetable-file)은 만료 6시간 전에만 DUE가 된다. 현재 head 기준으로 갱신 기회는 2026-10-09T20:51Z 한 번뿐이다.Changes
tools/datapack/build-current-five-region-source-fan-in.mjsevidence.freshUntil ?? snapshot.freshnessExpiresAt에서snapshot.freshnessExpiresAt(원장 정책 신선도)로 바꿨다. 관측 시각 검사(미래 관측 실패)는 그대로다.assertFanInSourceFreshnessPolicy신규: 고른 원천마다 정책 클래스가 0개면fan-in freshness policy missing, 2개 이상이면ambiguous로 실패한다. 원장 headfreshnessExpiresAt이deriveFreshnessExpiresAt으로 구한 상한(등록 기준 시각 + 정책 기간)을 넘어도fan-in freshness exceeds policy로 실패한다(리뷰 F1).--freshness-policy가 필수이고, 검사를 통과해야 출력을 쓴다.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).build-current-five-region-source-fan-in.test.mjssnapshot freshness mismatch로 바꿨다.decide-retained-gwangju-timetable-refresh.test.mjs: CURRENT 기준 시각과 저장소 판정refreshDueAt을 관측 +P1D로 바꿨다. 짧은 유효기간 head에서는 경보 창이 우선함을 따로 단언한다.Scope
Included
Excluded
freshUntil값 자체. 기록은 그대로 두고, fan-in이 창으로 쓰지 않을 뿐이다.Ownership / dependencies
data-test-ownership.json변경 없음)Contract & Compatibility
--freshness-policy가 추가됐다. 호출자는refresh-nationwide-candidate.mjs하나뿐이고 같은 PR에서 고쳤다.Version impact
Product gate impact
Provenance impact
Version decision
Verification
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: 현재 headrefreshDueAt2026-10-04T02:51:29.225Z,DUEData contracts결과는 PR check에서 확인Not run
Risk
Rollout / Recovery
Review focus
Checklist