Skip to content

fix(decompile): repair a torn journal on Windows before resuming - #10527

Open
Plastic-time wants to merge 2 commits into
morluto:mainfrom
Plastic-time:rrfz/repair-decompile-journal-on-windows
Open

Plastic-time wants to merge 2 commits into
morluto:mainfrom
Plastic-time:rrfz/repair-decompile-journal-on-windows

Conversation

@Plastic-time

@Plastic-time Plastic-time commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

On Windows, decompile_all with resume: true refused every export whose journal ended with a torn line. That is what an interrupted export leaves behind, so an interrupted export on Windows could not be resumed. The resume now drops the torn line and continues, as it already did on Linux and macOS.

Fixes #10521

Problem and expected behavior

openResumedJournal opened the journal with O_WRONLY | O_APPEND and then called truncate(committedBytes) on that handle. On Windows, libuv opens an O_APPEND handle with FILE_APPEND_DATA instead of FILE_WRITE_DATA, and SetEndOfFile needs write-data access. The truncate failed with EPERM, and the resume returned invalid_request: "The decompile journal could not be repaired after an interrupted write."

A minimal check on the same machine:

const h = await fs.open("t.txt", fs.constants.O_WRONLY | fs.constants.O_APPEND);
await h.truncate(6); // EPERM: operation not permitted, ftruncate

Change and scope

  • truncateJournal opens a separate O_WRONLY | O_NOFOLLOW handle without O_APPEND, truncates, and closes it through resources.ownFile. A failed truncate or close keeps the existing "could not be repaired" error.
  • The repair now runs before the append handle opens. The append handle and its ownership are unchanged.
  • Nothing else in src/application/decompileDirectory/ truncates, and no other src/ file opens with O_APPEND.

Contribution scope: ordinary fix within existing boundaries; maintainer scope decision not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: openResumedJournal in exportTree.ts.

  • Contribution scope fit and linked maintainer decision: not applicable.

  • CLI and MCP/tool-catalog contract: none.

  • Provider, bridge, target-format, or platform compatibility: Windows resume now works; Linux and macOS behavior is unchanged.

  • Evidence, artifact, provenance, or reconstruction contract: none.

  • Target selection, authority, upstream behavior, and cleanup details: the extra handle is owned and closed like the read handle in readJournalBytes.

  • Generated metadata, package, or installation impact: none.

  • Read existing files or captured data.

  • Change process memory, registers, files, or configuration. (the export's own journal, as before)

Evidence and regression coverage

  • Tests added or updated: none. Two existing tests in src/application/decompileDirectory/decompileAll.test.ts already cover this and fail on Windows: "resumes after a torn final journal line and keeps the journal parseable" and "repairs an invalid UTF-8 tail outside the committed journal". CI runs them only on Linux, where they pass.
  • Base reproduction: src/application/decompileDirectory/ on main 9e24b36: 3 failed / 73 passed. With this change: 1 failed / 75 passed.
  • The one remaining failure is "refuses a journal that is a symlink", which fails on main too because this Windows account cannot create symlinks (EPERM from symlink).
  • Remaining proof gaps: no real decompiler provider (Ghidra, Rizin, Kuna) is installed on this machine, so the CLI path was not run end to end. The tests drive the real decompileAll and the real filesystem with a fake provider.

Validation performed

Windows 11 Home 10.0.26200 x64, Node 24.22.1, base 9e24b36.

  • npx vitest run src/application/decompileDirectory/ on main source: 3 failed / 73 passed.
  • Same with this change: 1 failed / 75 passed (the symlink EPERM above).
  • npm run check:fast: passed (typecheck, lint)

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: Windows 11 only; no decompiler provider.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the new handle uses the same O_NOFOLLOW flag as the other journal opens.

Author readiness

Human owner: @Plastic-time

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

Windows opens an O_APPEND handle with FILE_APPEND_DATA only, so ftruncate
through the journal's append handle fails with EPERM and every resume after
an interrupted write was refused. Truncate through a separate write handle
first, then open the append handle.
@greptile-apps

greptile-apps Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge.

Summary

Repairs torn journals through a separate write handle before opening the append handle. This avoids the Windows restriction on truncating append-only handles.

  • Windows resumes exports after trimming a torn journal tail.

Reviews (1) · Last reviewed commit: "docs(decompile): say why the journal ope..." · Reviewed by Greptile

@Plastic-time
Plastic-time marked this pull request as ready for review November 14, 2026 11:42
@chatgpt-codex-connector

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ❌ Failed 2026-11-14T11:42:40.680631Z
We couldn’t complete this review. Comment @codex review to try again.
799c12a Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] decompile_all resume on Windows refuses a journal with a torn last line

1 participant