# python-hwpx product boundary `python-hwpx` is the primary product: an independently useful HWPX document library comparable in role to `python-docx`. ## Core owns - the document object model and public facade; - OPC package and OXML part ownership; - generic reading, traversal, editing, formatting, and table primitives; - serialization, byte/story preservation, validation, rollback, and recovery; - renderer-neutral quality contracts such as `RenderBackend`, `EditMask`, and `VisualReport`. ## Companion layers own `python-hwpx-automation` owns office workflows, genre/profile/policy decisions, agent plans, Hancom discovery, and renderer binding. `hwpx-skill` owns task judgment, routing, and prompt guidance. Core must not import either companion package. A new core module that was not present at the 4.2.0 baseline requires an explicit entry in [`module-ownership.json`](./module-ownership.json); falling through the generic core rule is not enough. ## Mixed-module decisions - `quality` and `table_patch` stay core. - renderer-neutral protocols are core; Hancom/backend binding and visual execution belong to `python-hwpx-automation`. - document diff keeps a generic diff model in core; task-plan composition moves. - mail merge keeps generic HWPX binding in core; sanitization/policy is injected. - tracked-change format primitives stay core; oracle verification moves. - benchmark and conformance campaign runners are repository QA, not product API. The 5.0 boundary is closed: every non-core disposition (`mcp-migrate`, `split`, `dev-only`, and `undecided`) must remain exactly zero. A non-core file fails the ownership gate even if it would have fit beneath the historical baseline or carried a self-consistent ledger exception. The 4.2 path baseline is the repository fixture `module-ownership-baseline-4.2.json`, pinned independently by path count and SHA-256 in the checker, ledger, and tests. The gate never reads Git history, so the same result is required in full, shallow, single-commit, and gitless source copies. Removal is a second, independent invariant. The exact 77 paths removed in 5.0 are recorded in `module-ownership-removed-5.0.json` at SHA-256 `4b8b4da35b3cf44503eb0dba05e335de1894ca5dd0b2fb668692a69e21ae6172`. Every one must remain absent even if an empty file, a new implementation, or a new ledger exception would otherwise classify as core. Public-artifact hygiene normalizes both wheel members (`hwpx/...`) and sdist members (`/src/hwpx/...`) back to repository paths and applies the same zero-resurrection inventory. ## Closed import capability The gate AST-inspects every core Python module. Absolute imports are fail-closed: the allowed roots are the running Python's standard library, `hwpx`, and the three declared runtime dependencies `lxml`, `openpyxl`, and `latex2mathml`. Everything else—including the companion products, MCP SDK, PDF/rendering, numerical, COM, and macOS GUI bindings—is rejected without maintaining a guessable denylist. Dynamic loading is capability-gated, not inferred from literal target strings. Core cannot acquire `importlib` loaders, `importlib.util`, `find_spec`, builtin `__import__`, `eval`, or `exec`. `subprocess` and `ctypes` imports and `os.system`/`os.popen` (including the covered alias and reflective forms) are also forbidden. The only exceptions are: - one exact `from importlib import resources` import used by packaged templates; - `PackageNotFoundError` and `version` imported directly from `importlib.metadata`, inside `_resolve_version()` rather than at module scope — a module-level binding leaks `hwpx.PackageNotFoundError` onto the top-level surface even though it is not in `__all__`, so keep the import in the function; - the two code-pinned `importlib.import_module(module_name)` calls in `src/hwpx/__init__.py`. The two lazy calls, their line/version-neutral AST fingerprint, and their 12-entry literal-map inventory are pinned. Each call also performs a runtime fail-closed check that the selected module is exactly `hwpx` or below `hwpx.*`; mutating the private map cannot turn it into an external import. This is an architectural dependency ratchet, not a Python security sandbox. Arbitrary standard-library laundering, native extensions, generated bytecode, and intentionally obfuscated runtime code are outside its threat model. The gate covers normal source imports and the explicitly tested acquisition, aliasing, assignment, and reflection forms; release review and package tests remain responsible for hostile-code scenarios.