Skip to content

feat(hangul): import, edit, and export HWP/HWPX - #320

Draft
seonghobae wants to merge 78 commits into
mainfrom
feat/hwp-hwpx-authoring
Draft

feat(hangul): import, edit, and export HWP/HWPX#320
seonghobae wants to merge 78 commits into
mainfrom
feat/hwp-hwpx-authoring

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Closes #319 when this Draft satisfies applicable exact-head gates and the #118 release freeze permits integration.
Closes #365 when the cleanup-containment repair integrates.
Closes #366 when hostile public-option access hardening integrates.
Closes #367 when structural engine-metadata validation integrates.

Current authority / product boundary

Protected shipped truth is independently resolved main@3b38ead2d00f44eb578d0689087b9293b3dabe1e. Current exact Draft head is 5ee6e4d0c3b2a804f39a8d186a8bdd7c2a38a782 on feat/hwp-hwpx-authoring, against that live base. Keep this PR Draft and unmerged while #118 owns the v0.6.0 freeze.

Inkspan owns the bounded deterministic HWP/HWPX byte/JSON bridge, local conversion rules, capability metadata, payload-redacted diagnostics, resource validation, cleanup containment, and committed synthetic fixture contracts. The host injects and owns the document engine and retains filesystem/network/transport/auth/tenancy/persistence/credential/model/deployment/durable-audit authority. Standalone Inkspan requires no service, DB, credential, network or model.

Implemented Hangul slice

  • open HWP/HWPX through host-owned HangulDocumentEngine and project the supported structural subset into framework-neutral editor JSON;
  • export edited JSON as HWPX by default or legacy HWP explicitly;
  • preserve paragraphs, headings, text alignment, bold/italic/strike, structural bullet/ordered lists, block quotes, code blocks and basic table topology while unsupported structures fail closed;
  • enforce source/output byte ceilings, genuine Uint8Array snapshots, payload-redacted errors and deterministic frozen capability metadata;
  • contain hostile option access and host cleanup failures without reflecting/stringifying unknown thrown values;
  • validate host-returned source format and selection HTML as strings before member access or coercion;
  • reject hostile structural metadata above 4,096 sections, 1,000,000 paragraphs per section, or 16,777,216 UTF-16 code units per paragraph before traversal/index/mutation authority crosses back to the host engine;
  • prove known synthetic documents against expected paragraphs/tables and fail closed on unsupported shapes.

The structural ceilings are Inkspan safety/resource limits, not HWP/HWPX format maxima. docs/HANGUL.md, ADR 0030, and src/hangul/documentationContract.test.ts bind the code and documentation to the same values.

Known-document fixtures

Committed OWPML sections under src/hangul/fixtures/ are wrapped as HWPX ZIP containers and legacy HWP fixture containers:

  • briefing-minutes.section.xml must project to heading Briefing Minutes, the quarterly-status paragraph, and the Topic Name / Owner Team table;
  • unsupported-shape.section.xml must fail closed with payload-redacted UNSUPPORTED_DOCUMENT_NODE instead of keeping Opening Remarks while dropping a shape;
  • exported HWPX and HWP bytes are reopened through the same fixture engine and compared against the same semantic JSON.

These fixtures are synthetic and contain no customer documents.

Test-first reliability lineage

Cleanup containment (#365)

RED 4481666a6bbddb467ac0d9318c45d3b37a411cad proved host free() throws could escape or replace an existing normalized Inkspan failure. GREEN 521900fde4347bc8b9cb4b966d7f1a930e9c7155 added one cleanup boundary.

Hostile option access (#366)

RED 907b088a77a6c0324b2907cba13235da8d508e58 proved hostile getters could leak private thrown values before engine authority. GREEN 0786240d8e30e66effd8326fad3d5d67af427f4d added payload-redacted INVALID_CONFIGURATION containment.

Deterministic capabilities contradiction

RED 59740977d26beadb5ef61cdd83990749e0616b31 failed typecheck because required capability metadata did not exist. GREEN e389742f85a2916241198af4676510f431207cdb added one frozen capability object.

Structural engine metadata validation (#367)

Earlier lineage rejected negative/fractional counts and stabilized section traversal, but safe-integer validation alone remained unbounded. Resource-bound RED 030512a5496c8fa869fc0cb1c79724334d6919b1 ran exact-checkout CI 32394561877; build-and-test job 96508269729 typechecked, entered 100% coverage, then exhausted the Node/Vitest heap near 4 GiB while exercising forged enormous metadata and terminated with OOM / ERR_IPC_CHANNEL_CLOSED. That run is deliberately non-passing RED evidence.

Narrow source fix 88936bd85345ec81daba1eaa497ed21418ce6dab bounds section count, per-section paragraph count, and paragraph length before child traversal or host mutation. dcefbe6, 487e6cb, and current head 5ee6e4d add the machine-checkable docs contract and converge HANGUL/ADR truth.

Non-string engine text

RED 642ec3c1aef0d9b12441909ef253658c9526fecd required rejection of non-string getSourceFormat() / exportSelectionHtml() values before member access or coercion. The repair remains in this head.

Exact-current-head evidence

For exact head 5ee6e4d0c3b2a804f39a8d186a8bdd7c2a38a782 against live base main@3b38ead2d00f44eb578d0689087b9293b3dabe1e at the latest fresh refetch:

  • CI 32395091285: completed / success;
  • build-and-test 96510085342: exact-current-head checkout, 161 test files / 898 tests, 100% statements / branches / functions / lines, packed-package verification, library build and demo build all successful;
  • Office Python 3.11-3.14 jobs: completed / success with dependency, coverage, docstring, wheel, schema and license gates;
  • Cross-engine Clipboard / Playwright job: completed / success;
  • Security Scan 32395091192: completed / success;
  • SAST Semgrep 32395091052: completed / success;
  • qualifying latest-push independent approval remains absent.

All observed repository-owned exact-head workflows are now terminal success; the absent qualifying independent approval remains non-passing. Predecessor-head success is not transferred. Any current-head failure requires owner-bound RCA and repair before this evidence section can claim acceptance.

Integration / release boundary

Keep this next-release feature Draft/unmerged while #118 owns v0.6.0 operational release acceptance from protected main@3b38ead2d00f44eb578d0689087b9293b3dabe1e. Do not transfer predecessor evidence, move protected main for next-release feature work, weaken gates, or synthesize tag/release identity from a branch ref. Any head/live-base/ruleset movement requires fresh classification.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: de70a271-8a49-48e5-b388-35719a3a85dd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

cursor[bot]
cursor Bot previously requested changes Aug 16, 2026

@cursor cursor 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.

Review (head f5449f2)

Hangul import/edit/export is a real buyer-visible format gap and belongs in Inkspan as a bounded, independently usable package subpath. Keep this Draft while #118 owns the exact v0.6.0 source identity.

Blocking

ADR identity collides and is not indexed.

  • This PR adds docs/adr/0027-hangul-document-authoring-boundary.md.
  • #141 already owns 0027 for bounded DOCX page layout (earliest claimant, 2026-08-10).
  • #248/#249/#285 reuse 0027 for writing diagnostics and #248 also uses 0028.
  • This head's docs/adr/README.md still ends at 0026, so the Hangul decision is not discoverable from the canonical index.

Next action: refetch the open ADR queue, renumber this decision to the next unused identifier (currently 0029 or later), update filename/doctoring/TRACEABILITY/tests, and add the unique index row. Do not take 0027/0028.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment thread docs/adr/0027-hangul-document-authoring-boundary.md Outdated
@seonghobae
seonghobae dismissed cursor[bot]’s stale review August 17, 2026 01:21

Dismissed as stale after the requested ADR collision/index repair was implemented on current exact head c2ada8e. The inline thread is outdated and resolved; this dismissal does not constitute approval. Live governance still requires a qualifying independent approval on the latest push.

seonghobae and others added 21 commits August 17, 2026 18:03
Add committed synthetic OWPML briefing and shape documents, wrap them as HWPX ZIP and legacy HWP containers, and require the public bridge to return the expected paragraphs/tables or fail closed.

Co-authored-by: Seongho Bae <[email protected]>
Record the committed briefing and shape fixtures, require unsupported structures to fail closed instead of warning, and keep the compatibility guide executable.

Co-authored-by: Seongho Bae <[email protected]>
@cursor
cursor Bot force-pushed the feat/hwp-hwpx-authoring branch from 7c79ade to f51cdd7 Compare August 17, 2026 18:10
@seonghobae

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Labels

None yet

Projects

None yet

2 participants