python-hwpx 3.2.0__tar.gz → 3.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_hwpx-3.2.0/src/python_hwpx.egg-info → python_hwpx-3.3.1}/PKG-INFO +2 -2
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/README.md +1 -1
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/pyproject.toml +3 -1
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/blueprint/model.py +103 -7
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/package_validator.py +325 -226
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/oracle.py +162 -19
- {python_hwpx-3.2.0 → python_hwpx-3.3.1/src/python_hwpx.egg-info}/PKG-INFO +2 -2
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/python_hwpx.egg-info/SOURCES.txt +1 -0
- python_hwpx-3.3.1/tests/test_visual_oracle_budget.py +207 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/LICENSE +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/NOTICE +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/setup.cfg +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/blueprint/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/blueprint/bundle.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/blueprint/catalog.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/blueprint/dump.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/blueprint/mapping.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/blueprint/native.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/blueprint/replay.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/catalog.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/cli.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/commands.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/document.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/form_plan.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/model.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/path.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/query.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/agent/story.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/authoring.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/benchmark/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/benchmark/blind_eval.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/body_patch.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/builder/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/builder/core.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/builder/report.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/badges.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/corpus/corpus.json +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/corpus/meeting_summary.hwpx +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/corpus/notice.hwpx +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/corpus/report_table.hwpx +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/corpus.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/report.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/roundtrip_batch.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/conformance/runner.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/data/Skeleton.hwpx +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/_support.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/composer.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/harvest.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/plan.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profile.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/application_form/fragments/body.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/application_form/fragments/heading.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/application_form/fragments/info_table.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/application_form/fragments/title.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/application_form/profile.json +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/application_form/template.hwpx +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/home_notice/fragments/body.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/home_notice/fragments/heading.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/home_notice/fragments/title.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/home_notice/profile.json +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/home_notice/template.hwpx +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/official_notice/fragments/body.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/official_notice/fragments/heading.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/official_notice/fragments/info_table.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/official_notice/fragments/title.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/official_notice/profile.json +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/official_notice/template.hwpx +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/report/fragments/body.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/report/fragments/heading.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/report/fragments/info_table.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/report/fragments/title.xml +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/report/profile.json +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/profiles/report/template.hwpx +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/design/validator.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/document.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/evalplan_fill.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/exam/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/exam/compose.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/exam/ir.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/exam/measure.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/exam/parser.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/exam/profile.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/fill_residue.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fill.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fit/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fit/apply.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fit/engine.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fit/measure.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fit/policy.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fit/report.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fit/seal.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/form_fit/wordbox.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/formfill_quality.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/guidance_scan.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/ingest/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/ingest/base.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/ingest/hwpx_converter.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/layout/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/layout/lint.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/layout/report.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/opc/package.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/opc/relationships.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/opc/security.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/opc/xml_utils.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/_document_impl.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/_document_primitives.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/body.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/canonical_defaults.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/common.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/document.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/document_parts.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/header.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/header_part.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/memo.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/namespaces.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/numbering.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/objects.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/paragraph.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/parser.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/run.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/schema.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/section.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/section_format.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/section_story.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/simple_parts.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/table.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/oxml/utils.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/package.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/patch.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/presets/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/presets/proposal.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/py.typed +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/quality/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/quality/ledger.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/quality/policy.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/quality/report.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/quality/save_pipeline.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/table_patch.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/template_formfit.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/templates.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/_schemas/header.xsd +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/_schemas/section.xsd +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/advanced_generators.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/archive_cli.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/doc_diff.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/exporter.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/fuzz/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/fuzz/__main__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/fuzz/catalog.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/fuzz/generator.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/fuzz/minimize.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/fuzz/runner.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/generic_inventory.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/id_integrity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/idempotence.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/ir_equality.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/layout_preview.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/mail_merge.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/markdown_export.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/object_finder.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/official_lint.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/package_reconcile.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/page_guard.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/pii.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/read_fidelity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/recover.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/redline.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/repair.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/report_parser.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/report_utils.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/roundtrip_diff.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/style_profile.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/table_cleanup.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/table_compute.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/table_navigation.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/template_analyzer.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/text_extract_cli.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/text_extractor.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/toc_author.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/toc_fidelity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/tools/validator.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/__init__.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/_hancom_open_rate.ps1 +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/_refresh_hwpx_mac.applescript +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/_render_hwpx.ps1 +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/_render_hwpx_mac.applescript +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/detectors.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/diff.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/fixture_corpus.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/hancom_worker.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/masks.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/page_qa.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/qa_contracts.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/qa_metrics.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/hwpx/visual/report.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/python_hwpx.egg-info/dependency_links.txt +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/python_hwpx.egg-info/entry_points.txt +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/python_hwpx.egg-info/requires.txt +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/src/python_hwpx.egg-info/top_level.txt +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_advanced_generators.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_blueprint_contracts.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_blueprint_dump.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_blueprint_replay.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_catalog.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_cli.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_commands.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_contracts.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_document.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_mixed_form.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_path_query.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_agent_story_commands.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_authoring_profile_routing.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_authoring_profile_routing_oracle.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_authoring_render_check.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_blind_eval_fixture.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_body_patch.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_builder_core.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_builder_plan_v2.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_builder_vertical_slice.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_cell_line_spacing.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_comment_node_robustness.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_conformance.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_corpus_read_fidelity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_coverage_promotion.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_coverage_targets.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_design_builder.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_deviations_registry.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_doc_diff.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_document_context_manager.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_document_formatting.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_document_plan.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_document_plan_computed_fields.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_document_save_api.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_evalplan_fill.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_exam_compose.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_exam_compose_oracle.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_exam_fixtures.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_exam_ir.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_exam_measure.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_exam_parser.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_exam_profile.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_existing_document_format_editing.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_fill_residue.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_form_fields.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_form_fill_split_run.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_form_fit.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_form_fit_integration.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_form_fit_seal.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_form_fit_seal_placement.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_form_fit_wordbox.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_formfill_quality.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_fuzz_loop.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_fuzz_regressions.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_gap_closure_tools.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_government_report_preset.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_government_table_profile.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_guidance_scan.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_hancom_render_worker.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_hp_tab_support.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_hwpxlib_corpus_read.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_id_generator_range.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_id_integrity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_image_object_workflow.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_ingest.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_inline_models.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_integration_hwpx_compatibility.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_integration_roundtrip.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_kordoc_absorption.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_layout_lint.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_layout_preview.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_mail_merge_fit.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_mail_merge_table_compute.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_mail_merge_xlsx.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_markdown_export.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_memo_and_style_editing.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_mixed_form_plan.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_namespace_handling.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_new_features.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_official_document_style.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_official_lint_gongmun_gate.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_official_lint_tableaware.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_opc_package.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_open_safety_corpus.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_openrate.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_oxml_modularization.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_oxml_parsing.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_packaging_license_metadata.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_packaging_py_typed.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_paragraph_keep_together.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_paragraph_section_management.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_pii.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_pii_structural.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_pii_wiring.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_proposal_preset.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_public_runtime_boundary.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_question_split_detector.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_read_fidelity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_recover_broken_zip.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_redline_authoring.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_redline_verify.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_repair_repack.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_report_parser.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_report_utils.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_repr_snapshots.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_rhwp_t1_gates.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_rhwp_t2_verification.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_roundtrip_fidelity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_row_heights.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_save_pipeline.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_save_pipeline_no_bypass.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_section_headers.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_set_paragraph_format_keep.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_skeleton_template_ids.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_split_cell.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_split_merged_cell.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_style_profile.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_table_cleanup.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_table_navigation.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_table_patch.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_table_patch_dryrun.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_table_patch_m105.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_tables_default_border.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_template_analyzer_enrichment.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_template_formfit.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_text_extractor_annotations.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_toc_author.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_toc_fidelity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_validation_severity.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_validator_comment_nodes.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_version_metadata.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_visual_fixture_corpus.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_visual_oracle.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_visual_qa_contracts.py +0 -0
- {python_hwpx-3.2.0 → python_hwpx-3.3.1}/tests/test_visual_qa_metrics.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-hwpx
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.1
|
|
4
4
|
Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리
|
|
5
5
|
Author: python-hwpx Maintainers
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -70,7 +70,7 @@ Dynamic: license-file
|
|
|
70
70
|
`hwpx-mcp-server`와 `hwpx-plugin`은 같은 프로젝트가 직접 유지보수하는 first-party 연동 구성요소입니다.
|
|
71
71
|
“first-party”는 프로젝트 유지보수 관계를 뜻하며, 한컴 또는 제3자의 공식 인증을 뜻하지 않습니다.
|
|
72
72
|
|
|
73
|
-
현재 PyPI 공개 릴리스는 `python-hwpx 3.
|
|
73
|
+
현재 PyPI 공개 릴리스는 `python-hwpx 3.3.1`입니다. 일반
|
|
74
74
|
`pip install python-hwpx`로 이 릴리스를 설치할 수 있습니다.
|
|
75
75
|
현재 패키지 분류는 `Development Status :: 3 - Alpha`입니다. 이 분류는 API와 제품의
|
|
76
76
|
성숙도를 나타내며, 공개 버전이나 플러그인의 최소 호환 버전을 대신하지 않습니다.
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
`hwpx-mcp-server`와 `hwpx-plugin`은 같은 프로젝트가 직접 유지보수하는 first-party 연동 구성요소입니다.
|
|
26
26
|
“first-party”는 프로젝트 유지보수 관계를 뜻하며, 한컴 또는 제3자의 공식 인증을 뜻하지 않습니다.
|
|
27
27
|
|
|
28
|
-
현재 PyPI 공개 릴리스는 `python-hwpx 3.
|
|
28
|
+
현재 PyPI 공개 릴리스는 `python-hwpx 3.3.1`입니다. 일반
|
|
29
29
|
`pip install python-hwpx`로 이 릴리스를 설치할 수 있습니다.
|
|
30
30
|
현재 패키지 분류는 `Development Status :: 3 - Alpha`입니다. 이 분류는 API와 제품의
|
|
31
31
|
성숙도를 나타내며, 공개 버전이나 플러그인의 최소 호환 버전을 대신하지 않습니다.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-hwpx"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.3.1"
|
|
8
8
|
description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -137,6 +137,7 @@ files = [
|
|
|
137
137
|
"src/hwpx/tools/template_analyzer.py",
|
|
138
138
|
"src/hwpx/tools/text_extract_cli.py",
|
|
139
139
|
"src/hwpx/tools/text_extractor.py",
|
|
140
|
+
"src/hwpx/visual/oracle.py",
|
|
140
141
|
"tests/template_automation/helpers.py",
|
|
141
142
|
"tests/template_automation/generate_fixtures.py",
|
|
142
143
|
]
|
|
@@ -175,6 +176,7 @@ include = [
|
|
|
175
176
|
"src/hwpx/tools/template_analyzer.py",
|
|
176
177
|
"src/hwpx/tools/text_extract_cli.py",
|
|
177
178
|
"src/hwpx/tools/text_extractor.py",
|
|
179
|
+
"src/hwpx/visual/oracle.py",
|
|
178
180
|
"tests/template_automation/helpers.py",
|
|
179
181
|
"tests/template_automation/generate_fixtures.py",
|
|
180
182
|
]
|
|
@@ -242,10 +242,7 @@ def _validate_resource(value: object, *, name: str) -> dict[str, Any]:
|
|
|
242
242
|
return record
|
|
243
243
|
|
|
244
244
|
|
|
245
|
-
def
|
|
246
|
-
"""Validate and detach one strict ``hwpx.agent-blueprint/v1`` manifest."""
|
|
247
|
-
|
|
248
|
-
manifest = _object(value, "blueprint")
|
|
245
|
+
def _validate_manifest_header(manifest: dict[str, Any]) -> None:
|
|
249
246
|
_exact_keys(manifest, required=_MANIFEST_KEYS, name="blueprint")
|
|
250
247
|
if manifest["schemaVersion"] != BLUEPRINT_SCHEMA:
|
|
251
248
|
raise AgentContractError("invalid_syntax", "unsupported blueprint schema", target="schemaVersion")
|
|
@@ -257,12 +254,16 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
257
254
|
if mode not in BLUEPRINT_MODES:
|
|
258
255
|
raise AgentContractError("invalid_syntax", "unsupported blueprint mode", target="mode")
|
|
259
256
|
|
|
257
|
+
|
|
258
|
+
def _validate_source(manifest: dict[str, Any]) -> None:
|
|
260
259
|
source = _object(manifest["source"], "source")
|
|
261
260
|
_exact_keys(source, required={"revision", "label"}, name="source")
|
|
262
261
|
if not REVISION_PATTERN.fullmatch(str(source["revision"])):
|
|
263
262
|
raise AgentContractError("invalid_syntax", "source.revision must be sha256", target="source.revision")
|
|
264
263
|
_validate_public_json(source, name="source")
|
|
265
264
|
|
|
265
|
+
|
|
266
|
+
def _validate_root(manifest: dict[str, Any]) -> dict[str, Any]:
|
|
266
267
|
root = _object(manifest["root"], "root")
|
|
267
268
|
_exact_keys(
|
|
268
269
|
root,
|
|
@@ -275,7 +276,12 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
275
276
|
raise AgentContractError("unknown_kind", "root kind is unknown", target="root.kind")
|
|
276
277
|
if not str(root["sourcePath"]).startswith("/"):
|
|
277
278
|
raise AgentContractError("invalid_syntax", "root sourcePath must be semantic", target="root.sourcePath")
|
|
279
|
+
return root
|
|
280
|
+
|
|
278
281
|
|
|
282
|
+
def _validate_nodes(
|
|
283
|
+
manifest: dict[str, Any],
|
|
284
|
+
) -> tuple[list[dict[str, Any]], set[str], dict[str, dict[str, Any]]]:
|
|
279
285
|
nodes = _list(manifest["nodes"], "nodes")
|
|
280
286
|
if not nodes or len(nodes) > MAX_BLUEPRINT_NODES:
|
|
281
287
|
raise AgentContractError("resource_limit", "node count is outside limits", target="nodes")
|
|
@@ -321,7 +327,15 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
321
327
|
_validate_public_json(node["sourceHint"], name=f"{name}.sourceHint")
|
|
322
328
|
detached_nodes.append(node)
|
|
323
329
|
nodes_by_id[node_id] = node
|
|
330
|
+
return detached_nodes, node_ids, nodes_by_id
|
|
331
|
+
|
|
324
332
|
|
|
333
|
+
def _check_node_tree_shape(
|
|
334
|
+
root: dict[str, Any],
|
|
335
|
+
detached_nodes: list[dict[str, Any]],
|
|
336
|
+
node_ids: set[str],
|
|
337
|
+
nodes_by_id: dict[str, dict[str, Any]],
|
|
338
|
+
) -> str:
|
|
325
339
|
if str(root["blueprintId"]) not in node_ids:
|
|
326
340
|
raise AgentContractError("invariant_violation", "root does not reference a node", target="root")
|
|
327
341
|
for index, node in enumerate(detached_nodes):
|
|
@@ -344,6 +358,14 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
344
358
|
raise AgentContractError(
|
|
345
359
|
"invariant_violation", "blueprint nodes must form one rooted tree", target="nodes"
|
|
346
360
|
)
|
|
361
|
+
return root_id
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _check_node_reachability(
|
|
365
|
+
root_id: str,
|
|
366
|
+
node_ids: set[str],
|
|
367
|
+
nodes_by_id: dict[str, dict[str, Any]],
|
|
368
|
+
) -> None:
|
|
347
369
|
visited: set[str] = set()
|
|
348
370
|
visiting: set[str] = set()
|
|
349
371
|
|
|
@@ -364,6 +386,10 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
364
386
|
if visited != node_ids:
|
|
365
387
|
raise AgentContractError("invariant_violation", "blueprint contains orphan nodes", target="nodes")
|
|
366
388
|
|
|
389
|
+
|
|
390
|
+
def _validate_dependency_records(
|
|
391
|
+
manifest: dict[str, Any],
|
|
392
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]:
|
|
367
393
|
styles = _list(manifest["styles"], "styles")
|
|
368
394
|
numbering = _list(manifest["numbering"], "numbering")
|
|
369
395
|
resources = _list(manifest["resources"], "resources")
|
|
@@ -382,6 +408,14 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
382
408
|
detached_resources = [
|
|
383
409
|
_validate_resource(item, name=f"resources[{index}]") for index, item in enumerate(resources)
|
|
384
410
|
]
|
|
411
|
+
return detached_styles, detached_numbering, detached_resources
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _check_dependency_uniqueness(
|
|
415
|
+
detached_styles: list[dict[str, Any]],
|
|
416
|
+
detached_numbering: list[dict[str, Any]],
|
|
417
|
+
detached_resources: list[dict[str, Any]],
|
|
418
|
+
) -> list[str]:
|
|
385
419
|
style_keys = [str(item["key"]) for item in detached_styles]
|
|
386
420
|
numbering_keys = [str(item["key"]) for item in detached_numbering]
|
|
387
421
|
resource_keys = [str(item["key"]) for item in detached_resources]
|
|
@@ -393,10 +427,18 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
393
427
|
raise AgentContractError("invariant_violation", "resource asset paths must be unique", target="resources")
|
|
394
428
|
if sum(int(item["size"]) for item in detached_resources) > MAX_TOTAL_ASSET_BYTES:
|
|
395
429
|
raise AgentContractError("resource_limit", "total asset bytes exceed limit", target="resources")
|
|
430
|
+
return dependency_keys
|
|
431
|
+
|
|
396
432
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
433
|
+
def _check_node_dependency_refs(
|
|
434
|
+
detached_nodes: list[dict[str, Any]],
|
|
435
|
+
detached_styles: list[dict[str, Any]],
|
|
436
|
+
detached_numbering: list[dict[str, Any]],
|
|
437
|
+
detached_resources: list[dict[str, Any]],
|
|
438
|
+
) -> None:
|
|
439
|
+
style_key_set = {str(item["key"]) for item in detached_styles}
|
|
440
|
+
numbering_key_set = {str(item["key"]) for item in detached_numbering}
|
|
441
|
+
resource_key_set = {str(item["key"]) for item in detached_resources}
|
|
400
442
|
for index, node in enumerate(detached_nodes):
|
|
401
443
|
if not set(node["styleRefs"]) <= style_key_set:
|
|
402
444
|
raise AgentContractError("invariant_violation", "node has an orphan style reference", target=f"nodes[{index}].styleRefs")
|
|
@@ -405,6 +447,12 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
405
447
|
if not set(node["resourceRefs"]) <= resource_key_set:
|
|
406
448
|
raise AgentContractError("invariant_violation", "node has an orphan resource reference", target=f"nodes[{index}].resourceRefs")
|
|
407
449
|
|
|
450
|
+
|
|
451
|
+
def _validate_reference_records(
|
|
452
|
+
manifest: dict[str, Any],
|
|
453
|
+
node_ids: set[str],
|
|
454
|
+
dependency_keys: list[str],
|
|
455
|
+
) -> tuple[list[dict[str, Any]], dict[str, list[str]]]:
|
|
408
456
|
references = _list(manifest["references"], "references")
|
|
409
457
|
if len(references) > MAX_REFERENCES:
|
|
410
458
|
raise AgentContractError("resource_limit", "reference count exceeds limit", target="references")
|
|
@@ -423,6 +471,13 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
423
471
|
if target in node_ids:
|
|
424
472
|
reference_edges[str(record["from"])].append(target)
|
|
425
473
|
reference_records.append(record)
|
|
474
|
+
return reference_records, reference_edges
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _check_reference_inventory(
|
|
478
|
+
detached_nodes: list[dict[str, Any]],
|
|
479
|
+
reference_records: list[dict[str, Any]],
|
|
480
|
+
) -> None:
|
|
426
481
|
for index, node in enumerate(detached_nodes):
|
|
427
482
|
declared_fields = {
|
|
428
483
|
str(record["field"])
|
|
@@ -431,6 +486,12 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
431
486
|
}
|
|
432
487
|
if set(node["references"]) != declared_fields:
|
|
433
488
|
raise AgentContractError("invariant_violation", "node reference inventory is inconsistent", target=f"nodes[{index}].references")
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def _check_reference_acyclicity(
|
|
492
|
+
node_ids: set[str],
|
|
493
|
+
reference_edges: dict[str, list[str]],
|
|
494
|
+
) -> None:
|
|
434
495
|
reference_visited: set[str] = set()
|
|
435
496
|
reference_visiting: set[str] = set()
|
|
436
497
|
|
|
@@ -448,6 +509,8 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
448
509
|
for node_id in sorted(node_ids):
|
|
449
510
|
visit_reference(node_id)
|
|
450
511
|
|
|
512
|
+
|
|
513
|
+
def _validate_metadata_sections(manifest: dict[str, Any]) -> list[Any]:
|
|
451
514
|
unsupported = _list(manifest["unsupported"], "unsupported")
|
|
452
515
|
for index, item in enumerate(unsupported):
|
|
453
516
|
record = _object(item, f"unsupported[{index}]")
|
|
@@ -471,7 +534,10 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
471
534
|
if fidelity["ceiling"] not in FIDELITY_LEVELS:
|
|
472
535
|
raise AgentContractError("invalid_syntax", "fidelity ceiling is invalid", target="fidelity.ceiling")
|
|
473
536
|
fidelity["reasons"] = [str(item) for item in _list(fidelity["reasons"], "fidelity.reasons")]
|
|
537
|
+
return unsupported
|
|
474
538
|
|
|
539
|
+
|
|
540
|
+
def _verify_manifest_integrity(manifest: dict[str, Any], verify_hash: bool) -> None:
|
|
475
541
|
_validate_public_json(manifest, name="blueprint")
|
|
476
542
|
declared_hash = manifest["blueprintHash"]
|
|
477
543
|
if not SHA256_PATTERN.fullmatch(str(declared_hash)):
|
|
@@ -479,6 +545,36 @@ def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool =
|
|
|
479
545
|
if verify_hash and declared_hash != blueprint_hash(manifest):
|
|
480
546
|
raise AgentContractError("verification_failed", "blueprintHash mismatch", target="blueprintHash")
|
|
481
547
|
canonical_manifest_bytes(manifest, include_hash=True)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def validate_blueprint_manifest(value: Mapping[str, Any], *, verify_hash: bool = True) -> dict[str, Any]:
|
|
551
|
+
"""Validate and detach one strict ``hwpx.agent-blueprint/v1`` manifest."""
|
|
552
|
+
|
|
553
|
+
manifest = _object(value, "blueprint")
|
|
554
|
+
_validate_manifest_header(manifest)
|
|
555
|
+
_validate_source(manifest)
|
|
556
|
+
root = _validate_root(manifest)
|
|
557
|
+
|
|
558
|
+
detached_nodes, node_ids, nodes_by_id = _validate_nodes(manifest)
|
|
559
|
+
root_id = _check_node_tree_shape(root, detached_nodes, node_ids, nodes_by_id)
|
|
560
|
+
_check_node_reachability(root_id, node_ids, nodes_by_id)
|
|
561
|
+
|
|
562
|
+
detached_styles, detached_numbering, detached_resources = _validate_dependency_records(manifest)
|
|
563
|
+
dependency_keys = _check_dependency_uniqueness(
|
|
564
|
+
detached_styles, detached_numbering, detached_resources
|
|
565
|
+
)
|
|
566
|
+
_check_node_dependency_refs(
|
|
567
|
+
detached_nodes, detached_styles, detached_numbering, detached_resources
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
reference_records, reference_edges = _validate_reference_records(
|
|
571
|
+
manifest, node_ids, dependency_keys
|
|
572
|
+
)
|
|
573
|
+
_check_reference_inventory(detached_nodes, reference_records)
|
|
574
|
+
_check_reference_acyclicity(node_ids, reference_edges)
|
|
575
|
+
|
|
576
|
+
unsupported = _validate_metadata_sections(manifest)
|
|
577
|
+
_verify_manifest_integrity(manifest, verify_hash)
|
|
482
578
|
return {
|
|
483
579
|
**manifest,
|
|
484
580
|
"nodes": detached_nodes,
|