Skip to content

[Bug]: Windows history manifest integrity check treats C:\... and \\?\C:\... as different paths and fails after sessions are archived #4442

Closed

Description

@SeanChengN

Summary

OpenCodex 2.52.0 on Windows reports:

backup manifest or restore target failed integrity checks — manual review required

for an existing v2 Codex history manifest. A read-only inspection shows that the history appears intact:

  • 66 manifest entries; all 66 corresponding thread IDs still exist in the database.
  • 46 rollout paths match exactly.
  • 20 rollout paths are reported as mismatches:
    • 18 differ only by the Windows extended-length prefix (\\?\\).
    • 2 have moved from sessions to archived_sessions.
  • There is no evidence that the 66 threads or rollout files are corrupted.

The likely false positive is that Windows paths such as C:\\... and \\\\?\\C:\\... refer to the same file, but the integrity check compares them as different path identities.

Environment

  • OpenCodex 2.52.0
  • Codex CLI 0.154.0
  • Windows

Reproduction

  1. Use an existing v2 Codex history manifest on Windows.
  2. Run the OpenCodex history integrity/doctor check.
  3. Observe the integrity warning for the manifest even though the database rows and rollout files are present.

Workaround

Setting syncResumeHistory=false bypasses the failing history-resume check and allows routing to recover as opencodex-local.

Expected behavior

Windows path identity normalization should treat C:\\... and \\\\?\\C:\\... as the same file, while legitimately archived sessions should be handled or reported separately from integrity failures.

Activity

  1. changed the title [-]Windows history manifest integrity check treats C:\... and \\?\C:\... as different paths and fails after sessions are archived[/-] [+][Bug]: Windows history manifest integrity check treats C:\... and \\?\C:\... as different paths and fails after sessions are archived[/+] on Sep 13, 2026
  2. added
    bugSomething isn't working
    platformOS/service/tray/ACL (Windows-heavy, not Windows-only)
    on Sep 13, 2026
  3. lidge-jun commented on Sep 13, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 67 / 80

    이 이슈는 Windows에서 Codex v2 히스토리 백업 매니페스트 무결성 검사가, 같은 파일을 가리키는 경로 표기만 다른데 서로 다른 파일로 보고 doctor/restore를 막는 버그다. 증상 문구는 backup manifest or restore target failed integrity checks — manual review required(src/cli/doctor.ts, src/codex/history-job.ts의 integrity 실패 설명과 동일). 보고 환경은 OpenCodex 2.52.0 + Codex CLI 0.154.0 Windows. 워크어라운드로 syncResumeHistory=false를 켜면 검사를 건너뛰고 opencodex-local로 라우팅이 살아난다고 한다. 즉 세션 데이터가 깨진 게 아니라, 경로 동일성 판정이 resume 경로 전체를 막고 있다.

    보고자의 읽기 전용 점검이 설득력 있다. 매니페스트 66개 entry ↔ DB thread 66개 모두 존재, rollout 경로 중 46개는 완전 일치, 불일치 20개 중 18개는 Windows extended-length prefix(\\?\\)만 다름, 2개는 sessions → archived_sessions로 옮겨진 정상 아카이브. 데이터 손상 증거는 없고, 거짓 양성 무결성 실패로 읽힌다.

    현재 dev(HEAD 7ca00ffe7)의 경로 동일성 잎은 src/codex/history-manifest.ts다. codexHistoryPathIdentity는 node:path.resolve 후 win32에서만 toLowerCase한다. sameCodexHistoryPath가 그 결과를 문자열 비교한다. \\?\\ 접두를 벗기거나 Win32 경로를 정규화하는 단계는 없다. ADR-0018 / structure/config.md History backup manifest contract도 “platform-aware database path identity”를 이 파일에 두었고, 실제 rollout 비교는 history-provider.ts가 sameCodexHistoryPath(row.rollout_path, entry.rolloutPath)로 이어 받는다(대략 878·896 근처). Node의 resolve만으로는 C:\\...와 \\?\\C:\\...가 같은 identity로 수렴하지 않는 경우가 있어, 보고와 HEAD 코드가 맞물린다. 아카이브로 옮긴 2건은 prefix 문제가 아니라 의도된 경로 변경이라, 같은 fix에 섞으면 안 되고 “아카이브된 rollout을 어떻게 재매핑/허용할지”를 따로 정해야 한다.

    이 버그가 중요한 이유: mid-flight history / native-restore 라인(#4380 등)과 doctor·inject 경로가 integrity에 fail-closed로 기대는데, Windows에서만 표기 차이로 fail-closed가 과하게 발동하면 resume/routing이 통째로 꺼진다. platform 라벨이 붙은 것도 그 맥락이다. #4342 계열 history containment과는 주제가 겹치지만, 이번 보고는 경로 identity 정규화가 핵심이라 별도 작은 PR이 맞다.

    경로 src/codex/history-manifest.ts 57-81 - codexHistoryPathIdentity / sameCodexHistoryPath에 win32용 extended-length(\\?\\, UNC \\?\\UNC\\) 스트립(및 필요 시 슬래시 정규화)을 넣고, DB identity와 rollout identity가 같은 함수를 쓰게 유지하세요. 잎 모듈이라 파일 I/O 없이 순수 문자열 계약으로 고칠 수 있다.

    경로 src/codex/history-provider.ts - restore 전 sameCodexHistoryPath 실패가 integrity로 올라가는 지점에, 정규화 전후 쌍( C:\\ vs \\?\\C:\\ ) fixture 회귀를 붙이세요. doctor 문구만 바꾸면 안 됩니다.

    경로 sessions vs archived_sessions - 아카이브로 옮긴 rollout은 prefix fix로 해결되지 않습니다. “파일이 archived_sessions에 있으면 매니페스트 경로를 갱신/허용할지, 별도 integrity code로 구분할지”를 제품 결정으로 남겨 두세요. 지금은 둘 다 같은 실패 문구로 뭉개집니다.

    메인테이너의 판단이 필요한 지점

    • win32 identity에 \\?\\ 스트립만 할지, GetFinalPathNameByHandle 수준까지 갈지(보통 스트립+resolve+casefold면 충분)
    • archived_sessions로 옮긴 entry를 resume 시 자동 rewrite할지, 경고만 하고 통과시킬지, 여전히 막을지
    • 2.52.0 사용자용 핫픽스 우선순위 vs dev(2.53.0 라인)만 고칠지

    너의 추천
    help wanted로 두되, 첫 PR 범위는 history-manifest.ts의 win32 path identity 정규화 + sameCodexHistoryPath 단위 테스트 + provider restore 회귀만으로 좁히세요. 아카이브 재매핑은 후속 이슈/PR로 분리하세요. 워크어라운드 syncResumeHistory=false는 문서/doctor에 “표기 차이 false positive 가능, 데이터 삭제가 아님” 한 줄을 남기면 운영자가 덜 당황합니다. 중복 close 대상은 아니고, types/config 분할과도 무관합니다.

    이 댓글은 grok-bot이 작성했습니다

  4. Ingwannu commented on Sep 13, 2026

    @Ingwannu
    Owner

    One additional compatibility boundary for the proposed path fix, confirmed on dev 4a49d7f: codexHistoryPathIdentity is not only an equality helper. It also feeds codexHistoryStateDbIdentity -> codexHistoryBackupId -> historyBackupPathFor, which hashes the database identity into the manifest filename.

    Changing normalization can therefore change where an existing manifest is looked up, even if the rollout comparison is corrected. Please include a compatibility fixture that begins with a manifest named using the OLD extended-path identity and verifies that its provenance is still discovered/retained after normalization; otherwise the fix can make an existing backup appear absent. Handle conflicting old/new manifests conservatively rather than silently replacing one.

    Keep the archive relocation cases separate: a sessions-to-archived_sessions move is not merely a path spelling alias. And a lexical equivalence fix must not waive the existing file identity, manifest ownership, or native-writer checks. I have not modified Windows histories, normalized any live files, or marked this resolved.

  5. added a commit that references this issue on Sep 13, 2026
  6. lidge-jun commented on Sep 13, 2026

    @lidge-jun
    Owner

    Fixed on dev via #4486 (merge commit a3ca64f, verified as an ancestor of origin/dev).

    Prefix normalization now lives in the shared identity leaf, so C:... and \?\C:... resolve to the same manifest identity. There is a compatibility detail worth knowing, because a prefixed state-db path hashed to a different manifest filename before this fix: readers fall back to the legacy manifest name when no canonical file exists, and when both exist the canonical one wins while the legacy file is left in place rather than silently replaced.

    The sessions to archived_sessions moves you reported stay out of scope here. That is a real path change and a separate product decision, not a normalization defect.

    Windows evidence: workflow_dispatch run 34744707214 of ci.yml with lane=all on the exact merged head, all six windows shards passing.

    Credit note: draft #4465 by @maoxin1234 proposed the same normalization before this landing, and the merged commit carries a Co-authored-by trailer naming them.

  7. added a commit that references this issue on Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingplatformOS/service/tray/ACL (Windows-heavy, not Windows-only)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions