Skip to content

fix: refuse text edits that shift opaque HWP5 range tags - #498

Open
entelecheia wants to merge 8 commits into
mainfrom
fix/470-range-tag-edits
Open

entelecheia wants to merge 8 commits into
mainfrom
fix/470-range-tag-edits

Conversation

@entelecheia

@entelecheia entelecheia commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

Text-length changes previously retained opaque HWP5 PARA_RANGE_TAG records with stale WCHAR positions. The editor now preflights selected edits and refuses a width-changing splice before mutation, with a Korean cause and a remedy: use equal-WCHAR-width text, or remove the range marks in Hangul and retry.

Closes #470.

  • The local audit used the installed release CLI's read-only JSON dump on eight genuine HWP5 fixtures. None contained PARA_RANGE_TAG; the broader local saved-sample directory was unavailable. This PR therefore takes the refusal option. It does not infer a payload layout or rewrite offsets. Format reference: 한글문서파일형식 5.0 §4.3.5.
  • A shared presence/width guard covers global/addressed replacement, private splices, named fields, slots, inferred forms, field/bookmark/hyperlink/image/seal insertion, and object-anchor removal. Batches preflight every selected replacement before writes, including opposing deltas whose total is zero. Widths follow the existing serialized encoding: tabs occupy eight WCHARs and non-BMP text occupies two.
  • Field values normalize tabs to InlineCtrl(TAB) before both width checks and insertion. The same builder serves field creation and hyperlink displays. Field-value extraction retains inline tabs, and hyperlink style boundaries use the full inserted WCHAR width. This prevents a one-WCHAR field value from accepting a tab that the HWP5 writer expands to eight WCHARs, while allowing a genuinely equal-width eight-WCHAR replacement. Slot/form splices already use the shared tab normalization.
  • Whole-cell rebuilding refuses if it would discard range tags anywhere in the replaced subtree, including captions and raw mirrors. An exact tagged-cell no-op keeps the original paragraphs. Equal-width text splices retain opaque tag bytes. Unselected tagged captions do not block unrelated body edits.
  • Formerly infallible Rust mutators now return Result and CLI callers propagate the controlled refusal. Existing CLI/MCP JSON shapes are unchanged. The native reader, opaque payload preservation and identity serialization are unchanged. The binding pipeline already rejects opaque target records; its low-level splice now also propagates errors.
  • Regression inputs are owner-authored opaque sentinels, not specification derivatives or saved-corpus extracts. The English-only touched-file rule also translates existing comments without changing specimen literals or test names.

Validation:

  • Original pre-fix tests compiled: 25 converter refusal tests failed at their intended assertions while three safe controls passed; three CLI refusal tests failed at their intended assertions while two safe controls passed. The existing binding rejection control passed.
  • Tab regressions compiled on reviewed head 307f949 plus test-only commit fdefc3b: four new unit tests and the CLI tab-refusal test failed at their intended assertions; the same-width non-tab control passed. The CLI setup verified a named one-WCHAR field and its opaque tag in the serialized source, then failed because the old editor accepted the tab edit. Other cases cover equal-width tab acceptance, field creation width and hyperlink style restoration.
  • Full remote Linux scripts/check.sh passed at final head 2f3dbfda0e205e3f49655233cfae0859509c3360, rebased onto main 521c4ff, with stdin closed and two build/test workers. All 39 new regression/control tests passed (31 range-tag units, two field creation/style tests, six CLI tests). Local-only fixtures were absent (49 skips, including four optional); public PDF parity was skipped because the pinned Poppler version was unavailable.
== check: OK (fmt/clippy/test/crate-edges/pdf-runner/structured-corpus/claims/doc-surface/release-block/readiness-selfcheck/skip-accounting/public-parity=skipped) skipped-for-missing-fixtures=49 (optional=4) ==
  • No genuine range-tag layout or person-run Hancom open/export evidence is claimed. Hosted three-OS CI is separate from the remote Linux gate.

Copilot AI balanced review requested due to automatic review settings November 10, 2026 16:15
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

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

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-10T16:19:25.123759Z 307f949 PR opened
ℹ️ 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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 307f949398

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/hwp-convert/src/field.rs Resolved
@entelecheia
entelecheia force-pushed the fix/470-range-tag-edits branch from 307f949 to 4cd428f Compare November 10, 2026 17:42
@entelecheia
entelecheia force-pushed the fix/470-range-tag-edits branch from 4cd428f to 2f3dbfd Compare November 10, 2026 17:43
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.

Text edits leave HWP5 PARA_RANGE_TAG offsets stale

2 participants