Repository navigation
[Feat] 동일 원본 재수집을 원본 재게시 없이 재확인 원장 행으로 등록 - #911
Conversation
- lib/same-raw-reverification: 원장 head와 원본 sha가 같으면 REVERIFY_EXISTING(기존 객체 재사용), 다르면 PUBLISH_NEW. head 크기·URI 불일치, 기존 객체 미존재·크기·sha 불일치는 실패. 원장 append-only 헬퍼. - 코레일 원본 게시기: 같은 원본이면 PUT 없이 head 객체를 읽어 확인하고 재확인 영수증을 남긴다 (같은 UTC 날짜 키 EXISTS로 막히던 대경선 재확인 해소). - 대경선 topology 등록기: 재확인 영수증이 원장 head 객체를 정확히 가리킬 때만 받고, 재확인 행에 rawObjectReusedFromSnapshotId를 남긴다. 원장은 append-only 헬퍼로 잇는다. - 계획 시간표 등록기: 부모 topology가 재확인 행이면 같은 기존 객체를 가리키는 재확인 영수증을 받는다. Refs #870 Refs #862 Refs #903
QA 결정(2026-10-03, #870 B안): 같은 원본 재수집마다 13.8MB snapshot을 쌓지 않는다. - snapshot(schemaVersion 2)은 원본 내용만 담고 관측 시각·수집 파일명과 무관하다. - inventory capitalScheduleAdmissionEvidence.reverifications: [{observedAt, rawSha256, collectionReceiptSha256}] append-only 이력. evidence.observedAt은 마지막 항목과 같고, prepare 신선도 기준이다. - 등록기: 같은 원본이면 기존 snapshot 바이트·records sha가 같을 때만 재사용(파일 미작성), 다르면 실패. 다른 원본이면 새 snapshot. 새 관측은 직전 관측보다 뒤여야 하고 이력 앞부분은 바뀌면 안 된다. Refs #870 Refs #899
기존 수집본(2026-10-02T15:57:05.773Z, raw sha256 218f76dd…)으로 register-kric-capital-timetable.mjs를 다시 실행했다. snapshot(schemaVersion 2)에서 observedAt·rawFile을 빼고(id·records 동일), inventory evidence에 reverifications 첫 항목을 둔다. 관측 시각은 그대로다. Refs #870
…6-10-02T22:25:42.708Z) 공식 refresh-nationwide-candidate.mjs(--evaluated-at 2026-10-02T22:25:42.708Z --release-sequence 125 --requested-by data-operator-lead --approved-by data-release-authority). 팩·외부 시간표 파일 바이트는 같다. 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: 4
🎯 Linked issue & acceptance criteria audit
- Linked issue: #870 [Refactor] Data pipeline operations: auto refresh, gate split, fewer artifacts
- Goal summary: Same-source reverification without republishing raw, keeping failures explicit.
- Acceptance criteria verified: 3/3
| ID | Acceptance criterion | Status | Evidence |
|---|---|---|---|
| AC-1 | New publication only when content changed; same raw is reverified | PASS | tools/datapack/lib/same-raw-reverification.mjs |
| AC-2 | Failed reverification stays explicit with no fallback success | PASS | tools/datapack/publish-korail-metropolitan-timetable-raw.mjs |
| AC-3 | Same-date snapshot re-registration no longer blocks | PASS | tools/datapack/register-kric-capital-timetable.mjs |
🛡️ Adversarial audit evidence
- Falsifiability verified: Yes (Manual mutants of purge ordering, capital order checks and OCI authority were killed.)
- Hollow assertions detected: 0
- Production backdoors detected: 0
- Ground-truth sources verified:
Committed capital snapshot records sha unchangedIssue 870 same-raw and Fallback rulespreauthenticatedObjectStorageClient GET plus sha verification
- Mutation testing evidence:
Survivors: receipt byteSize and register-korail-timetable receipt field comparisons.
🤖 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/datapack/lib/same-raw-reverification.mjs:50-50`
Could the helper call readObject and hash the body itself, or assert the
client kind, so the actual-bytes guarantee does not depend on the caller?
- `@tools/datapack/lib/same-raw-reverification.mjs:58-63`
Could a table-driven rejection test tamper each bound receipt field one at a
time and expect RECEIPT_HEAD or PUBLICATION_RECEIPT? That would make each
binding falsifiable.
- `@tools/datapack/register-kric-capital-timetable.mjs:75-75`
Could registration require capturedAt to be at or before the injected clock and
within a short window of it, and cross-check the date in rawFile? Adding a test
with a forged receipt that keeps the sha but advances capturedAt would prove it.
- `@tools/datapack/register-kric-capital-timetable.mjs:92-92`
Should the plan compare against an existing snapshot file by bytes and reuse it,
as the REVERIFY branch does? A test for A to B to A would pin the behavior.
---
Outside diff comments:
None.
ℹ️ Review info
⚙️ Run configuration
Configuration used: aquila-review skill run by Claude Code with hub CLAUDE.md invariants
Review profile: ASSERTIVE
Plan: Aquila fallback
Run ID: N/A (Aquila fallback)
Review source: Aquila CLI
Repository visibility: public GitHub repository
Trigger:
Discovery review of the current change set for the automerge review gate
Base:
main@c8f83ea1c54ad7110ff51dcff85d4fb3e4ae06cb
Head:
lib/same-raw-reverification@e7cc6ba82df6a698f798d9c84946ab9cda38df58
Command:
node scripts/compile-review-payload.mjs input.json --output payload-1.json
📥 Commits
Reviewing files changed in this pull request between:
- Base:
c8f83ea1c54ad7110ff51dcff85d4fb3e4ae06cb - Head:
e7cc6ba82df6a698f798d9c84946ab9cda38df58
📒 Files selected for processing (25)
contracts/documentation/documentation-fragment.jsontools/ci/data-test-ownership.jsontools/datapack/lib/capital-official-timetable.mjstools/datapack/lib/capital-official-timetable.test.mjstools/datapack/lib/kric-capital-timetable-records.mjstools/datapack/lib/kric-capital-timetable-records.test.mjstools/datapack/lib/same-raw-reverification.mjstools/datapack/lib/same-raw-reverification.test.mjstools/datapack/publish-korail-metropolitan-timetable-raw.mjstools/datapack/publish-korail-metropolitan-timetable-raw.test.mjstools/datapack/purge-expired-source-raw.mjstools/datapack/purge-expired-source-raw.test.mjstools/datapack/register-korail-route-topology.mjstools/datapack/register-korail-timetable.mjstools/datapack/register-korail-timetable.test.mjstools/datapack/register-kric-capital-timetable.mjstools/datapack/register-kric-capital-timetable.test.mjstools/datapack/release/candidate-build-spec.jsontools/datapack/release/current-five-region-source-fan-in.jsontools/datapack/release/hash-evidence.jsontools/datapack/release/nationwide-candidate-preparation.jsontools/datapack/release/release-request.jsontools/datapack/reports/nationwide-requirement-ownership-ledger.jsontools/datapack/source-inventory.jsontools/datapack/sources/kric-nationwide-timetable-file-capital-dec3ef2fdb5318efd9cff47c6b012e88c80c34f7b4866106eabbed6e1e7bdd00.json
📜 Review details
- 등록기가 원본을 직접 받아(실제 GET·sha 계산) 같은 실행의 수집 영수증만 쓴다. --workbook/--receipt 외부 영수증 인자를 없앴다(--operation-directory만). 손으로 고친 capturedAt이 끼어들 경로가 없다. - 등록 계획은 실행 시계를 받아 관측 시각이 미래이거나 1시간보다 오래되면 실패한다(18MB 다운로드·파싱 수 분 + 느린 망·시계 오차 여유). Refs #870
- lib: rawObjectUri·reusedFromSnapshotId·rawObjectSha256·byteSize를 각각 바꾸면 RECEIPT_HEAD로 실패 - 계획 시간표 등록기 재확인 분기: rawObjectUri·reusedFromSnapshotId·byteSize·collectionReceiptSha256을 각각 바꾸고 부모 원장 영수증 hash는 다시 맞춘 경우 PUBLICATION_RECEIPT로 실패 - topology 등록기 재확인 영수증: byteSize·collectionReceiptSha256 변조 사례 추가 각 비교를 지운 mutant 6개가 모두 죽는다. Refs #870
원본이 A → B → A로 돌아오면 A snapshot 파일이 이미 있어 PUBLISH_NEW의 wx 쓰기가 EEXIST로 실패했다. 기존 파일 바이트가 같으면 다시 쓰지 않고 재사용하고, 다르면 SNAPSHOT_MISMATCH로 실패한다. Refs #870
서명(S3 호환) 클라이언트의 verifyObject는 HEAD 메타데이터만 비교한다. 재확인은 verifiesObjectBytes: true (실제 GET 본문으로 크기·sha256 계산)를 표시한 클라이언트만 받고, 없으면 SAME_RAW_REVERIFICATION_STORAGE로 실패한다. PAR 클라이언트에 이 능력을 표시하고, HEAD 전용 fake가 거부되는 테스트를 더했다. Refs #870
|
No description provided. |
|
* [Data] 대경선 코레일 광역전철 시각표 topology 동일 원본 재확인 등록(P7D) - 공식 첨부(sha c28dcd…)를 2026-10-03T00:05Z에 재수집해 원본 재게시 없이 재확인 원장 행으로 등록한다(#911 경로). - topology head를 5d68004f…에서 a2fee5ce…로 잇고 freshUntil은 2026-10-10T00:05Z다. Refs #903 #862 * [Data] 대경선 계획 시각표를 재확인한 topology head에 다시 결속해 등록 - planned timetable을 topology a2fee5ce…에 결속해 재등록한다(동일 원본, 운행 194 trip). - 달력 창은 2026-10-03~2026-11-02(KASI 10·11월), freshUntil은 2026-11-02T00:05Z다. - materializeKorailTimetable 부모 불일치(PARENT)가 해소된다(10-03·10-08 시각 건식 실행 194 trip). Refs #903 #862 * [Feat] 코레일 6개 노선 projection·station-lines 스냅샷을 공용 시간표 적재기 입력으로 연결하는 lib - 수도권 등록기가 같은 관측에서 코레일 projection snapshot을 만들고 korailScheduleAdmissionEvidence에 결속한다 (재확인 이력 append-only, 수도권과 같은 관측 시각). - 코레일 provider: 휴일→WEEKEND_HOLIDAY, alias 근거 동반, 급행은 trip 단위 고정 집합(경춘 5·수인분당 22·경의중앙 28). - station-lines provider: 등록 파생 스냅샷의 내용 해시·inventory 결속을 다시 확인하고 trip을 그대로 넘긴다. Refs #903 #862 * [Data] KRIC 파일 900 재수집: 수도권 동일 원본 재확인과 코레일 6개 노선 projection 첫 등록 - 2026-10-03T00:11Z 재수집(원본 sha 218f76dd…). 수도권 snapshot은 재사용하고 재확인 이력만 append한다. - 같은 관측에서 코레일 projection(59,346행, snapshot c186585e…)을 korailScheduleAdmissionEvidence로 등록한다. Refs #903 #862 * [Chore] Hub data 계약 pin을 905ae116(#3025, planned_timetable station-lines 포함)으로 상향 Refs #903 * [Data] KRIC 역별 시간표 5개 노선(kric-subway-timetable-station-lines) 첫 등록과 필수 원천 지정 - 2026-10-03T00:12Z 실수집(응답 210, 3,956 trip, 기대값 일치), OCI 원본 게시(sha 3a8c462a…), 명시 검토 admission (data-release-authority, QA 승인 2026-10-03 채팅: 출시 범위 전 노선 공식 시간표 적재)으로 등록한다. - governance(kric-subway-timetable과 같은 약관)·freshness(planned_timetable)·원장·inventory·파생 스냅샷을 한 트랜잭션으로 쓴다. - production scope requiredSourceIds에 추가한다. Refs #903 #862 * [Feat] 전국 후보 prepare에 코레일 6개 노선·KRIC 역별 5개 노선·대경선 시간표 적재 연결 - 코레일 projection(korailScheduleAdmissionEvidence)은 수도권과 같은 inventory evidence·정책 신선도로 고르고, 대경선 계획 시각표·station-lines 파생 스냅샷은 원장 head·fan-in으로 고른 뒤 내용 해시를 원장·inventory와 대조한다. - 두 원천은 공용 적재기로 싣고 station-lines는 외부 시간표 파일(sidecar) 원천에 추가한다. 코레일은 수도권과 같은 원천의 두 번째 섹션이 된다. 노선별 요약·격리 행은 nationwide-official-line-timetable-report.json에 남긴다. - 대경선은 materializeKorailTimetable로 싣는다. e2e에 '팩의 전 노선 trip 1건 이상' 단언을 추가한다(RED). Refs #903 #862 * [Test] e2e 후보 건수를 노선 추가분만큼 갱신하고 입력 선택 17개로 고정 - trip 19,973→26,240, 정차 561,983→671,914, route 26→39, 달력 24→30, 달력 예외 132→200. 증가분은 코레일 6개 노선·KRIC 역별 5개 노선·대경선뿐이고 기존 노선 건수는 같다. Refs #903 * [Data] 전국 후보 재생성(seq125, 후보 시계 2026-10-03T00:17:16.988Z): 전 노선 시간표 적재 - 노선 36개 전부 trip 1건 이상(이전 12개 노선 0건). trip 26,240·정차 671,914. - 팩 JSON 59,147,366B, 외부 시간표 파일 gzip 5,641,204B(펼침 58,866,238B). Refs #903 #862 * [Test] 새 등록 데이터에 맞춰 저장소 상태 의존 테스트를 갱신 - 수도권 외부 시간표 단언은 같은 원천의 코레일 섹션과 구분하도록 snapshot으로 고른다. - 대경선 재확인 head의 신선도 값과 이전 head lineage를 반영한다. - car-door 등록 테스트의 'inventory·governance 끝' 위치 가정을 '하나만, 기존이면 제자리'로 바꾼다. - station-lines 등록·게시 테스트는 저장소 사본을 등록 전 상태로 되돌려 첫 등록부터 시험한다. Refs #903 * [Data] station-lines·코레일 evidence 등록에 따른 전국 coverage tally 재생성 Refs #903 * [Data] coverage tally 재생성 뒤 전국 후보 재결속(seq125, 후보 시계 2026-10-03T00:25:30.412Z) - 팩·외부 시간표 파일 바이트는 같고 fan-in·소유권 원장·spec·request·hash 결속만 갱신된다. Refs #903 #862 * [Data] 수도권·인천 현행 topology 입력 재수집(2026-10-03) current-capital-topology-refresh 절차와 같은 공식 수집기 출력 4개다(ITX admission은 10-11까지 유효해 재수집하지 않는다). Refs #903 #862 * [Data] 수도권 현행 topology admission 활성화(2026-10-03 수집 입력) activate-current-source-set --topology-source-admission 결과(inventory·canonical pack·재검증 증거)다. Refs #903 #862 * [Data] 수도권 topology 원본 OCI 게시·원장 등록(capital-route-topology-20261003) run-current-capital-route-topology-registration 결과다. Refs #903 #862 * [Data] 광주 topology 재수집·OCI 게시·원장 등록(2026-10-03, 원본 sha 동일) register-gwangju-route-topology publish-register 결과다. 같은 도구가 topology에 묶인 접근성·노선도 위치 snapshot 결속을 새 topology로 다시 맞췄다. Refs #903 #862 * [Data] 부산 topology 재수집·OCI 게시·원장 등록(2026-10-03, 원본 sha 동일) register-busan-route-topology publish-register 결과다. Refs #903 #862 * [Data] 대전 역간 거리·운임 topology 재수집·OCI 게시·원장 등록(2026-10-03, 원본 sha 동일) register-daejeon-route-topology publish-register 결과다. 같은 도구가 topology에 묶인 접근성·노선도 위치 snapshot 결속을 다시 맞췄다. Refs #903 #862 * [Data] 대구 1·2·3호선 topology·시간표 재수집·OCI 게시·원장 등록(2026-10-03, 원본 sha 9개 모두 동일) register-daegu-datapack-sources publish-register 결과다. data.go.kr 파일은 #890 미해결로 포털 다운로드 버튼과 같은 경로(임시 방식, 키 없음)로 받았다. Refs #903 #862 #890 * [Data] 대전·광주 접근성 재수집·OCI 게시·원장 등록(2026-10-03, 원본 sha 동일) topology 재등록이 접근성 결속을 새 topology 계보로 바꿔서, #892와 같이 접근성을 다시 수집해 원장 행을 맞췄다. data.go.kr 파일은 #890 미해결로 임시 포털 경로로 받았다. Refs #903 #862 #890 * [Data] 재수집으로 대체된 접근성 중간 snapshot 제거 topology 재등록이 만든 접근성 중간 snapshot 2개는 같은 브랜치의 재수집 등록으로 대체돼 inventory·원장 어디에서도 참조되지 않는다. Refs #903 * [Data] 부산 topology 재등록에 맞춰 부산 환승 지표 재결속 build-busan-transfer-metrics 결과다. 지표 12개와 제외 행 6개는 같고 원천 식별만 새 topology head로 바뀐다. Refs #903 #862 * [Data] 수도권 정본 팩 재결속에 맞춰 서울 실측 환승 지표 재생성 build-seoul-measured-transfer-metrics 결과다. 지표 263개는 같고 정본 팩 sha만 8102f1f6…에서 83e11161…로 바뀐다. Refs #903 #862 * [Data] 서울 환승 거리·시간 admission을 새 수도권 정본 팩에 재결속 rebind-current-seoul-transfer-source-admission(#893) 결과다. 잠긴 원본은 OCI에서 GET만 했다(PUT 없음). 원천 값은 같고 정본 팩 결속만 바뀐다. Refs #903 #862 * [Data] topology 7개 재등록 뒤 전국 후보 재결속(seq125, 후보 시계 2026-10-03T00:40:43.059Z) 시간표 내용은 같고 인천 1·2호선 snapshot 식별(20261003)과 fan-in·spec·request·hash 결속만 바뀐다. Refs #903 #862 * [Fix] 광주 시간표 materializer의 topology evidence 창을 #904 컷오버 규칙으로 검사 컷오버 이후 수집한 광주 topology(P7D)를 24시간 고정값으로 검사해 보관본 등록 경로가 실패했다(register-retained-kric-timetable 테스트가 실데이터로 RED). topologySnapshotFreshnessMillis로 컷오버 전 P1D, 후 P7D를 정확값으로 검사한다. Refs #903 #904 * [Test] 컷오버 이후 첫 topology 데이터에 맞춰 저장소 상태 의존 테스트 갱신 - 공용 materialize fixture는 대전 topology 창을 커밋된 evidence 창이 아니라 수집 시각 기준 컷오버 규칙으로 유도한다. - 인천 station-info 수집기·후보 입력 만료 테스트의 24시간 고정값을 컷오버 규칙으로 바꾼다. - 인천 시간표 만료 테스트는 후보 시계를 옮기지 않고 수집 시각을 옮겨 다른 입력의 만료와 섞이지 않게 한다. - 후보 입력 pin을 10-03 원장 head로 갱신한다. - 코레일 projection 결정성 단언을 독립 해시 계산으로 바꾼다(anti-cheat ANTI-CIRCULAR-ORACLE). Refs #903 * [Docs] source-inventory·governance blob 변경에 맞춰 문서 파편 갱신 Refs #903 * [Test] 코레일 급행 고정 집합이 기본 경로 binding과 노선 보고서에 붙는지 단언 리뷰 F1: 기본 allowance 경로를 타는 테스트가 없어 고정 집합 부착 분기를 지우는 변이가 살아남았다. 기본 경로에서 경춘 5·수인분당 22·경의중앙 28 trip 고정 집합이 붙고 나머지 3개 노선에는 붙지 않음을 단언하고, e2e에서 노선 보고서 pinnedQuarantine을 노선별로 단언한다. 부착 분기를 false로 바꾸면 두 테스트가 모두 실패한다. Refs #903 * [Fix] station-lines·코레일 격리 행의 운행일을 명시 값만 받도록 엄격화 리뷰 F2: 격리 행은 평일이 아닌 값을 모두 주말·휴일로 기록했다. station-lines는 dayCd 8=평일·9=주말·휴일, 코레일은 요일구분 평일·휴일만 받고 그 밖의 값은 SERVICE_DAY로 실패한다. 현재 데이터의 격리 행·보고서·팩 바이트는 바뀌지 않는다. Refs #903



Related issue
Refs #870
Refs #862
Refs #903
Summary
EXISTS로 실패했습니다. 대경선 재확인이 여기서 막혔습니다.wx로 쓰다가 충돌했습니다.Changes
tools/datapack/lib/same-raw-reverification.mjs(공용, [Refactor] 데이터 파이프라인 운영 방식 전환: 자동 갱신·개발/발행 게이트 분리·생성 산출물 축소 #870 일일 자동 재확인에 재사용)planRawObjectPublication: 원장 head와 원본 sha가 같으면REVERIFY_EXISTING, 다르면PUBLISH_NEW를 돌려줍니다. head의 크기나 URI가 맞지 않으면 실패합니다.verifyReusedRawObject: 기존 OCI 객체를 GET으로 읽어 크기와 sha를 확인합니다. 객체가 없거나 다르면 실패합니다.appendLedgerRow: append-only로 붙입니다. snapshot이 충돌하거나 head 계보가 맞지 않으면 실패합니다.publish-korail-metropolitan-timetable-raw.mjs)korail-metropolitan-timetable-raw-reverification-receipt를 남깁니다. 기록 항목:verifiedAt,reusedFromSnapshotIdregister-korail-route-topology.mjs)rawObjectReusedFromSnapshotId를 남기고,appendLedgerRow로 잇습니다.register-korail-timetable.mjs): 부모 topology가 재확인 행이면, 같은 기존 객체를 가리키는 재확인 영수증을 받습니다.kric-capital-timetable-records,capital-official-timetable,register-kric-capital-timetable). QA 결정(2026-10-03, B안)을 따랐습니다.capitalScheduleAdmissionEvidence.reverifications[{observedAt, rawSha256, collectionReceiptSha256}], append-onlyobservedAt은 마지막 항목과 같고, prepare가 신선도 기준으로 씁니다.--operation-directory만 받습니다.verifiesObjectBytes: true, PAR 클라이언트)만 받습니다.purge-expired-source-raw.mjsconflicting object key로 거부합니다.218f76dd…)으로 수도권 snapshot을 새 형식으로 다시 등록했습니다. 관측 시각은 그대로입니다.Scope
Included
Excluded
Ownership / dependencies
Contract & Compatibility
kric-nationwide-timetable-file,korail-metropolitan-timetable-file의 등록 계약만 확장했습니다.rawObjectReusedFromSnapshotId로 출처 행을 남깁니다.korail-metropolitan-timetable-raw-receipt)과 기존 원장 행은 그대로 받습니다.Version impact
Product gate impact
Provenance impact
Version decision
nationwide-candidate-20261002-seq125). 발행은 이 PR 밖에서 합니다.reverificationsrelease-request-nationwide-candidate-20261002-seq125Verification
RED와 GREEN
RAW_RECEIPT,PUBLICATION_RECEIPT로 실패했습니다.register-korail-timetable10건, 게시기·lib 테스트 통과purge-expired-source-raw40건 통과assertReverificationReceiptMatchesHead호출)를 제거하면 테스트가 실패합니다.통합 테스트
--check: IN SYNC실제 원본 재확인
218f76dd…로 이전과 같음).registerKricCapitalTimetable을 실행한 결과는REVERIFY_EXISTING이었습니다.cmp일치).reverifications가 2건으로 append됐고,observedAt이 갱신됐습니다.Not run
register-korail-route-topology.mjs publish-register로 실행합니다.collect-korail-itx-cheongchun-timetable.test.mjsRisk
Rollout / Recovery
REVERIFY_EXISTING이고, 원장과 이력이 1건 append됩니다.Review focus
Checklist