v8unpack-agent 0.1.0__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.
Files changed (113) hide show
  1. v8unpack_agent-0.1.0/LICENSE +21 -0
  2. v8unpack_agent-0.1.0/PKG-INFO +285 -0
  3. v8unpack_agent-0.1.0/README.md +268 -0
  4. v8unpack_agent-0.1.0/pyproject.toml +60 -0
  5. v8unpack_agent-0.1.0/setup.cfg +4 -0
  6. v8unpack_agent-0.1.0/tests/test_catalog_resolver.py +317 -0
  7. v8unpack_agent-0.1.0/tests/test_catalog_resolver_issue172.py +202 -0
  8. v8unpack_agent-0.1.0/tests/test_catalog_resolver_raw_header.py +229 -0
  9. v8unpack_agent-0.1.0/tests/test_chain_zero_binding_issue116.py +516 -0
  10. v8unpack_agent-0.1.0/tests/test_common_modules_issue151.py +198 -0
  11. v8unpack_agent-0.1.0/tests/test_coverage_metric.py +327 -0
  12. v8unpack_agent-0.1.0/tests/test_decode_element_data_path.py +222 -0
  13. v8unpack_agent-0.1.0/tests/test_discover_managed_forms_issue55.py +395 -0
  14. v8unpack_agent-0.1.0/tests/test_docs_contract_issue245.py +118 -0
  15. v8unpack_agent-0.1.0/tests/test_drift_checker.py +312 -0
  16. v8unpack_agent-0.1.0/tests/test_drift_checker_external.py +350 -0
  17. v8unpack_agent-0.1.0/tests/test_drift_checker_issue91.py +140 -0
  18. v8unpack_agent-0.1.0/tests/test_drift_content_hash.py +171 -0
  19. v8unpack_agent-0.1.0/tests/test_elem_parser.py +265 -0
  20. v8unpack_agent-0.1.0/tests/test_elem_parser_issue103.py +109 -0
  21. v8unpack_agent-0.1.0/tests/test_elem_parser_issue105.py +449 -0
  22. v8unpack_agent-0.1.0/tests/test_elem_parser_issue108.py +477 -0
  23. v8unpack_agent-0.1.0/tests/test_elem_parser_issue123.py +247 -0
  24. v8unpack_agent-0.1.0/tests/test_elem_parser_issue85.py +928 -0
  25. v8unpack_agent-0.1.0/tests/test_elem_parser_issue89.py +340 -0
  26. v8unpack_agent-0.1.0/tests/test_elem_sha256_drift.py +617 -0
  27. v8unpack_agent-0.1.0/tests/test_elem_structure_hash.py +223 -0
  28. v8unpack_agent-0.1.0/tests/test_examples_contract_issue246.py +36 -0
  29. v8unpack_agent-0.1.0/tests/test_examples_determinism_issue251.py +137 -0
  30. v8unpack_agent-0.1.0/tests/test_form_artifact.py +50 -0
  31. v8unpack_agent-0.1.0/tests/test_form_classifier_issue109.py +263 -0
  32. v8unpack_agent-0.1.0/tests/test_form_classifier_issue112.py +270 -0
  33. v8unpack_agent-0.1.0/tests/test_form_classifier_issue98.py +439 -0
  34. v8unpack_agent-0.1.0/tests/test_form_context.py +495 -0
  35. v8unpack_agent-0.1.0/tests/test_form_context_issue147.py +418 -0
  36. v8unpack_agent-0.1.0/tests/test_form_context_issue148.py +264 -0
  37. v8unpack_agent-0.1.0/tests/test_form_context_object_attributes.py +135 -0
  38. v8unpack_agent-0.1.0/tests/test_form_entry_protocol_issue191.py +220 -0
  39. v8unpack_agent-0.1.0/tests/test_form_identity_issue226.py +278 -0
  40. v8unpack_agent-0.1.0/tests/test_form_key_public_issue134.py +106 -0
  41. v8unpack_agent-0.1.0/tests/test_form_paths.py +41 -0
  42. v8unpack_agent-0.1.0/tests/test_form_router.py +256 -0
  43. v8unpack_agent-0.1.0/tests/test_form_router_hash_baseline.py +211 -0
  44. v8unpack_agent-0.1.0/tests/test_form_router_mixed.py +285 -0
  45. v8unpack_agent-0.1.0/tests/test_form_scan_index_elem.py +372 -0
  46. v8unpack_agent-0.1.0/tests/test_form_scan_index_paths_issue239.py +251 -0
  47. v8unpack_agent-0.1.0/tests/test_form_summary_issue69.py +278 -0
  48. v8unpack_agent-0.1.0/tests/test_forms_index.py +72 -0
  49. v8unpack_agent-0.1.0/tests/test_full_pipeline_walkthrough_issue175.py +242 -0
  50. v8unpack_agent-0.1.0/tests/test_init_form_context_exports_issue124.py +101 -0
  51. v8unpack_agent-0.1.0/tests/test_init_lazy_imports_issue128.py +130 -0
  52. v8unpack_agent-0.1.0/tests/test_init_lazy_imports_issue131.py +267 -0
  53. v8unpack_agent-0.1.0/tests/test_init_lazy_imports_issue134.py +250 -0
  54. v8unpack_agent-0.1.0/tests/test_init_lazy_imports_issue140.py +213 -0
  55. v8unpack_agent-0.1.0/tests/test_legacy_form_json_extractor.py +249 -0
  56. v8unpack_agent-0.1.0/tests/test_managed_fixtures.py +263 -0
  57. v8unpack_agent-0.1.0/tests/test_managed_form_summary.py +180 -0
  58. v8unpack_agent-0.1.0/tests/test_object_decoder.py +601 -0
  59. v8unpack_agent-0.1.0/tests/test_object_decoder_layout_contract_issue160.py +167 -0
  60. v8unpack_agent-0.1.0/tests/test_packaging_issue149.py +89 -0
  61. v8unpack_agent-0.1.0/tests/test_pipeline.py +127 -0
  62. v8unpack_agent-0.1.0/tests/test_platform_reference_types_issue165.py +123 -0
  63. v8unpack_agent-0.1.0/tests/test_platform_types_cross_config_issue164.py +208 -0
  64. v8unpack_agent-0.1.0/tests/test_post_run_report_issue196.py +277 -0
  65. v8unpack_agent-0.1.0/tests/test_production_runner_issue198.py +509 -0
  66. v8unpack_agent-0.1.0/tests/test_reference_type_resolution.py +232 -0
  67. v8unpack_agent-0.1.0/tests/test_run_report_docs_issue248.py +101 -0
  68. v8unpack_agent-0.1.0/tests/test_scan_external_forms.py +250 -0
  69. v8unpack_agent-0.1.0/tests/test_scan_forms.py +309 -0
  70. v8unpack_agent-0.1.0/tests/test_scan_forms_cli.py +161 -0
  71. v8unpack_agent-0.1.0/tests/test_scan_forms_external_bsl.py +223 -0
  72. v8unpack_agent-0.1.0/tests/test_scan_forms_load.py +171 -0
  73. v8unpack_agent-0.1.0/tests/test_scan_forms_reference_type_docs_issue168.py +97 -0
  74. v8unpack_agent-0.1.0/tests/test_scan_forms_root_issue234.py +52 -0
  75. v8unpack_agent-0.1.0/tests/test_scan_root_invalid_reserved_issue242.py +55 -0
  76. v8unpack_agent-0.1.0/tests/test_scan_warning_codes_issue167.py +422 -0
  77. v8unpack_agent-0.1.0/tests/test_skd_extractor.py +144 -0
  78. v8unpack_agent-0.1.0/tests/test_skd_extractor_batch.py +171 -0
  79. v8unpack_agent-0.1.0/tests/test_unindexed_forms_report_issue235.py +89 -0
  80. v8unpack_agent-0.1.0/tests/test_unresolved_refs_metric_issue278.py +115 -0
  81. v8unpack_agent-0.1.0/tests/test_warning_error_paths_issue123.py +102 -0
  82. v8unpack_agent-0.1.0/v8unpack_agent/__init__.py +322 -0
  83. v8unpack_agent-0.1.0/v8unpack_agent/_safe_paths.py +135 -0
  84. v8unpack_agent-0.1.0/v8unpack_agent/catalog_resolver.py +354 -0
  85. v8unpack_agent-0.1.0/v8unpack_agent/chain_data_path.py +546 -0
  86. v8unpack_agent-0.1.0/v8unpack_agent/cli.py +230 -0
  87. v8unpack_agent-0.1.0/v8unpack_agent/common_modules.py +117 -0
  88. v8unpack_agent-0.1.0/v8unpack_agent/coverage_metric.py +364 -0
  89. v8unpack_agent-0.1.0/v8unpack_agent/drift_checker.py +494 -0
  90. v8unpack_agent-0.1.0/v8unpack_agent/elem_parser.py +1452 -0
  91. v8unpack_agent-0.1.0/v8unpack_agent/form_artifact.py +118 -0
  92. v8unpack_agent-0.1.0/v8unpack_agent/form_classifier.py +462 -0
  93. v8unpack_agent-0.1.0/v8unpack_agent/form_context.py +493 -0
  94. v8unpack_agent-0.1.0/v8unpack_agent/form_identity.py +299 -0
  95. v8unpack_agent-0.1.0/v8unpack_agent/form_paths.py +81 -0
  96. v8unpack_agent-0.1.0/v8unpack_agent/form_router.py +168 -0
  97. v8unpack_agent-0.1.0/v8unpack_agent/form_summary.py +163 -0
  98. v8unpack_agent-0.1.0/v8unpack_agent/forms_index.py +145 -0
  99. v8unpack_agent-0.1.0/v8unpack_agent/managed_form_summary.py +71 -0
  100. v8unpack_agent-0.1.0/v8unpack_agent/managed_forms.py +338 -0
  101. v8unpack_agent-0.1.0/v8unpack_agent/object_decoder.py +662 -0
  102. v8unpack_agent-0.1.0/v8unpack_agent/pipeline.py +202 -0
  103. v8unpack_agent-0.1.0/v8unpack_agent/platform_reference_types.py +68 -0
  104. v8unpack_agent-0.1.0/v8unpack_agent/run_report.py +381 -0
  105. v8unpack_agent-0.1.0/v8unpack_agent/runner.py +583 -0
  106. v8unpack_agent-0.1.0/v8unpack_agent/scan_forms.py +998 -0
  107. v8unpack_agent-0.1.0/v8unpack_agent/skd_extractor.py +273 -0
  108. v8unpack_agent-0.1.0/v8unpack_agent.egg-info/PKG-INFO +285 -0
  109. v8unpack_agent-0.1.0/v8unpack_agent.egg-info/SOURCES.txt +111 -0
  110. v8unpack_agent-0.1.0/v8unpack_agent.egg-info/dependency_links.txt +1 -0
  111. v8unpack_agent-0.1.0/v8unpack_agent.egg-info/entry_points.txt +2 -0
  112. v8unpack_agent-0.1.0/v8unpack_agent.egg-info/requires.txt +8 -0
  113. v8unpack_agent-0.1.0/v8unpack_agent.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Иофик Михаил (MRDK80)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,285 @@
1
+ Metadata-Version: 2.4
2
+ Name: v8unpack-agent
3
+ Version: 0.1.0
4
+ Summary: Agent-pipeline adapter for v8unpack: path factory, FormArtifact, drift index
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/MRDK80/v8unpack-agent
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: v8unpack>=1.2.13
11
+ Provides-Extra: test
12
+ Requires-Dist: pytest>=7; extra == "test"
13
+ Provides-Extra: dev
14
+ Requires-Dist: mypy==2.3.1; extra == "dev"
15
+ Requires-Dist: ruff==0.16.4; extra == "dev"
16
+ Dynamic: license-file
17
+
18
+ # v8unpack-agent
19
+
20
+ Прикладной инструмент над upstream-распаковщиком `v8unpack`: превращает
21
+ выгрузку конфигурации 1С в читаемый текстовый слой, машиночитаемую опись
22
+ форм и компактный контекст для LLM.
23
+
24
+ Задача пакета — дать агенту достоверные сведения о формах и модулях без
25
+ угадывания. Где данные нечитаемы, результат помечается как неполный, а не
26
+ достраивается догадками.
27
+
28
+ ## Границы и non-goals
29
+
30
+ - Пакет не распаковывает контейнер сам: дерево выгрузки готовит upstream
31
+ `v8unpack`, а агент работает с уже раскрытым каталогом.
32
+ - Production-адаптера одиночного `Form.bin` в пакете нет: реализацию
33
+ распаковщика передаёт вызывающая сторона.
34
+ - Функций `index_cf()` и `rag.rebuild()` не существует; векторная индексация
35
+ находится вне scope пакета.
36
+ - Пакет не подключается к живой информационной базе и не читает её данные.
37
+
38
+ ## Кто что решает
39
+
40
+ | Компонент | Ответственность |
41
+ |---|---|
42
+ | upstream `v8unpack` | распаковка контейнеров в дерево файлов |
43
+ | `v8unpack-agent` | опись форм, текстовый слой, индексы, контекст, отчёт о запуске |
44
+ | вызывающая сторона | реализация распаковщика формы и решения по degraded-результату |
45
+
46
+ ## Установка
47
+
48
+ Основная установка из PyPI:
49
+
50
+ ```bash
51
+ pip install v8unpack-agent
52
+ ```
53
+
54
+ Исторический pre-release `0.1.0rc1` остаётся доступным в PyPI и по умолчанию
55
+ не выбирается резолверами (нужен явный pin или `--pre`):
56
+
57
+ ```bash
58
+ pip install v8unpack-agent==0.1.0rc1
59
+ ```
60
+
61
+ Установка актуального состояния `main` напрямую из Git остаётся вариантом для
62
+ проверки ещё не опубликованных изменений:
63
+
64
+ ```bash
65
+ pip install git+https://github.com/MRDK80/v8unpack-agent.git
66
+ ```
67
+
68
+ Установка для разработки из клона:
69
+
70
+ ```bash
71
+ pip install -e ".[test]"
72
+ ```
73
+
74
+ upstream `v8unpack` нужен для фактического извлечения текстов, включая
75
+ поддержку внешних отчётов `.erf`.
76
+
77
+ ## Быстрый старт: CLI
78
+
79
+ Один прогон — один отчёт и один код возврата. На вход подаётся каталог
80
+ уже распакованной выгрузки.
81
+
82
+ ```bash
83
+ v8unpack-agent-run <корень_выгрузки> --report-path post-run.json
84
+ ```
85
+
86
+ Аргумент `--report-path` обязателен, значения по умолчанию нет, а
87
+ каталог-родитель должен существовать заранее.
88
+
89
+ | Код | Значение |
90
+ |---:|---|
91
+ | 0 | все объекты обработаны полностью |
92
+ | 2 | ошибка аргументов или корень не является каталогом |
93
+ | 3 | degraded: есть частичные или отказавшие объекты |
94
+ | 4 | управляемая фатальная ошибка пайплайна |
95
+ | 5 | ошибка записи отчёта |
96
+ | 6 | фатальная ошибка и ошибка записи одновременно |
97
+
98
+ Degraded — неуспешное завершение: неполный контекст не должен выглядеть
99
+ успехом для автоматизации. Полное описание аргументов, стадий и кодов причин —
100
+ в [`docs/runner.md`](docs/runner.md), схема отчёта — в
101
+ [`docs/run_report.md`](docs/run_report.md).
102
+
103
+ ## Быстрый старт: Python
104
+
105
+ Каноническая цепочка: обнаружение источников, распаковка по `FormBinSource`,
106
+ обновление индекса.
107
+
108
+ ```python
109
+ from pathlib import Path
110
+
111
+ from v8unpack_agent import unpack_all_forms, update_forms_index
112
+ from v8unpack_agent.form_artifact import FormArtifact
113
+ from v8unpack_agent.form_identity import FormBinSource
114
+ from v8unpack_agent.scan_forms import scan_forms
115
+
116
+ dump_root = Path("unpacked_cf")
117
+ unpacked_root = Path("text_layer")
118
+
119
+
120
+ def unpack_one(source: FormBinSource, root: Path) -> FormArtifact:
121
+ """Здесь вызывается реальное извлечение текстов из source.bin_path."""
122
+ return FormArtifact.for_form(root, source.name)
123
+
124
+
125
+ scan_index = scan_forms(dump_root)
126
+ print(f"Найдено форм: {scan_index.total}")
127
+
128
+ artifacts = unpack_all_forms(dump_root, unpacked_root, unpack_one)
129
+ index = update_forms_index(dump_root, unpacked_root, artifacts)
130
+ index.save(Path("forms_index.json"))
131
+ ```
132
+
133
+ Распаковщик получает `FormBinSource` и корень текстового слоя — ровно два
134
+ аргумента. Существующая трёхаргументная реализация подключается только через
135
+ `adapt_legacy_unpacker()`. Подробный разбор и отбор по `form_ids` — в
136
+ [`docs/pipeline.md`](docs/pipeline.md), готовые сценарии — в
137
+ [`examples/README.md`](examples/README.md).
138
+
139
+ ## Пайплайн
140
+
141
+ | Шаг | Функция | Результат |
142
+ |---|---|---|
143
+ | опись форм | `scan_forms()` | `FormScanIndex` с layout-метаданными и индексом ссылочных типов |
144
+ | обнаружение источников | `discover_form_sources()` | `FormBinSource` с каноническим `form_id` |
145
+ | распаковка | `unpack_all_forms()` | `FormArtifact` на каждую форму |
146
+ | структура формы | `parse_elem_json()` | элементы и привязки `data_path`, best-effort |
147
+ | реквизиты объекта | `decode_object_attributes()` | `Properties` и `TabularSections` |
148
+ | классификация | `classify_form()` | `object`, `service` или `unknown` |
149
+ | внешние отчёты | `unpack_erf()` | текстовый слой и запросы СКД |
150
+ | индекс актуальности | `update_forms_index()` | `FormsIndex` с ключом `form_id` |
151
+ | контроль дрейфа | `check_drift()` | `DriftReport` по хешам |
152
+ | контекст для LLM | `build_form_context()` | `FormContext` и промпт-фрагмент |
153
+
154
+ ## Ключевые возможности
155
+
156
+ - Одноимённые формы разных владельцев не затирают друг друга: идентичность —
157
+ `form_id`, а не имя.
158
+ - Формы без кода попадают в опись: elem-only ветка заполняет `elem_json_path`.
159
+ - Частичный результат всегда явный: `extraction_ok=False` невозможен без
160
+ непустого `extraction_warnings`.
161
+ - Ссылочные типы реквизитов приводятся к имени объекта метаданных; неизвестный
162
+ UUID остаётся `Ref#<uuid>` и не угадывается.
163
+ - Сервисные формы отделены от объектных, чтобы метрика покрытия не занижалась
164
+ архитектурным паттерном платформы.
165
+ - Дрейф детектируется по хешам модуля и структуры, а не только по времени
166
+ изменения файла.
167
+ - Сериализация индексов переносима: в файлы пишутся только относительные
168
+ POSIX-пути, одинаковые на POSIX и NT.
169
+
170
+ ## Версии схем
171
+
172
+ Три независимые версии, которые не сравниваются друг с другом.
173
+
174
+ | Артефакт | Версия | Где описан |
175
+ |---|---:|---|
176
+ | `FormsIndex` | 2 | [`docs/pipeline.md`](docs/pipeline.md) |
177
+ | `FormScanIndex` | 2 при чтении legacy 1 | [`docs/scan_forms.md`](docs/scan_forms.md) |
178
+ | post-run report | 1 | [`docs/run_report.md`](docs/run_report.md) |
179
+
180
+ ## Публичная поверхность
181
+
182
+ | Модуль | Основные имена | Канонический документ |
183
+ |---|---|---|
184
+ | `scan_forms` | `scan_forms`, `FormEntry`, `FormScanIndex`, `scan_warning_code` | [scan_forms](docs/scan_forms.md) |
185
+ | `pipeline` | `unpack_all_forms`, `unpack_erf`, `update_forms_index`, `FormUnpacker` | [pipeline](docs/pipeline.md) |
186
+ | `form_identity` | `FormBinSource`, `discover_form_sources`, `select_sources`, `adapt_legacy_unpacker` | [pipeline](docs/pipeline.md) |
187
+ | `form_artifact` | `FormArtifact` | [pipeline](docs/pipeline.md) |
188
+ | `forms_index` | `FormsIndex`, `FormsIndexEntry`, `is_form_stale` | [pipeline](docs/pipeline.md) |
189
+ | `runner`, `cli` | `RunOptions`, `RunOutcome`, `run_pipeline` | [runner](docs/runner.md) |
190
+ | `run_report` | `PostRunReport`, `ObjectRunResult`, `write_post_run_report` | [run_report](docs/run_report.md) |
191
+ | `elem_parser` | `parse_elem_json`, `ElemIndexResult`, `UnindexedReason` | [elem_parser](docs/elem_parser.md) |
192
+ | `form_classifier` | `classify_form`, `FormClass`, `classify_empty_tree_form` | [form_classifier](docs/form_classifier.md) |
193
+ | `coverage_metric` | `calc_data_path_coverage`, `CoverageReport` | [form_classifier](docs/form_classifier.md) |
194
+ | `object_decoder` | `decode_object_attributes`, `DecodeResult`, `DecodeError` | [object_decoder](docs/object_decoder.md) |
195
+ | `catalog_resolver` | `resolve_data_path`, `ResolvedBinding` | [catalog_resolver](docs/catalog_resolver.md) |
196
+ | `form_context` | `FormContext`, `build_form_context`, `to_llm_prompt_fragment` | [form_context](docs/form_context.md) |
197
+ | `form_summary` | `build_form_summary`, `to_normalized_json` | [form_summary](docs/form_summary.md) |
198
+ | `form_router` | `FormRouter`, `form_paths` | [form_router](docs/form_router.md) |
199
+ | `common_modules` | `scan_common_modules`, `build_common_module_context` | [common_modules](docs/common_modules.md) |
200
+ | `managed_forms` | `discover_elem_forms`, `ElemFormEntry` | [managed_forms_structure](docs/managed_forms_structure.md) |
201
+ | `skd_extractor` | `extract_skd_queries`, `extract_all_skd_queries` | [skd_extractor](docs/skd_extractor.md) |
202
+ | `drift_checker` | `check_drift`, `DriftReport` | [drift_checker](docs/drift_checker.md) |
203
+
204
+ ## Документация
205
+
206
+ ### Начало работы
207
+
208
+ - [Текущее состояние реализации](docs/IMPLEMENTATION_STATUS.md)
209
+ - [Процесс контрибуции](CONTRIBUTING.md)
210
+ - [История изменений](CHANGELOG.md)
211
+
212
+ ### Пайплайн и запуск
213
+
214
+ - [Контракт пайплайна и `FormUnpacker`](docs/pipeline.md)
215
+ - [Production runner, CLI и коды возврата](docs/runner.md)
216
+ - [Схема отчёта о запуске](docs/run_report.md)
217
+
218
+ ### Формы и разбор
219
+
220
+ - [Структура элементов и `UnindexedReason`](docs/elem_parser.md)
221
+ - [Классификация форм и метрика покрытия](docs/form_classifier.md)
222
+ - [Сводка по форме](docs/form_summary.md)
223
+ - [Контекст для LLM](docs/form_context.md)
224
+ - [Маршрутизация путей формы](docs/form_router.md)
225
+ - [Структура управляемых форм](docs/managed_forms_structure.md)
226
+ - [Структура внешних обработок и отчётов](docs/external_forms_structure.md)
227
+ - [Запросы СКД](docs/skd_extractor.md)
228
+
229
+ ### Метаданные и индексы
230
+
231
+ - [Опись форм и индекс ссылочных типов](docs/scan_forms.md)
232
+ - [Реквизиты объекта из raw-секции `header`](docs/object_decoder.md)
233
+ - [Разрешение `data_path`](docs/catalog_resolver.md)
234
+ - [Общие модули](docs/common_modules.md)
235
+ - [Контроль дрейфа](docs/drift_checker.md)
236
+
237
+ ### Исследования
238
+
239
+ Датированные протоколы с агрегатами; выводы не переписываются задним числом.
240
+
241
+ - [Доля неиндексированных форм](docs/research/unindexed_share_issue229.md)
242
+ - [Разрешение ссылочных типов](docs/research/ref_resolver_issue143.md)
243
+ - [Нессылочные типовые грани](docs/research/non_reference_type_facets_issue166.md)
244
+ - [Кросс-конфигурационная проверка типов](docs/research/platform_types_cross_config_issue164.md)
245
+ - [Пустые реквизиты объекта](docs/research/missing_object_attributes_issue163.md)
246
+ - [Структура `Form.bin`](docs/research/form_bin_issue150.md)
247
+ - [Валидация на третьей выгрузке](docs/research/third_configuration_validation.md)
248
+
249
+ ### Примеры и политики
250
+
251
+ - [Исполняемые примеры](examples/README.md)
252
+ - [Политика ветвления](docs/branch_protection.md)
253
+ - [Аудит документации](docs/documentation_audit_issue245.md)
254
+
255
+ ## Известные ограничения
256
+
257
+ - Обычные (неуправляемые) формы хранят разметку в бинарном виде и частично
258
+ не читаются; такие формы получают `unknown` и исключаются из знаменателя
259
+ метрики.
260
+ - Квалификаторы реквизитов и составные типы не декодируются.
261
+ - Индекс ссылочных типов строится только для видов метаданных с доказанной
262
+ ссылочной формой; остальные UUID остаются `Ref#<uuid>`.
263
+ - В режиме `mode="external"` индекс ссылочных типов не собирается.
264
+ - Распаковщики из `examples/` и тестов — заглушки и не читают бинарный
265
+ формат.
266
+ - Пакет ещё не опубликован в индексе пакетов (issue #149).
267
+
268
+ ## Тесты
269
+
270
+ ```bash
271
+ pytest
272
+ ```
273
+
274
+ Фикстуры синтетические: реальная выгрузка для прогона не требуется. Порядок
275
+ работы с ветками, проверки перед PR и требования к обезличенности описаны в
276
+ [CONTRIBUTING.md](CONTRIBUTING.md).
277
+
278
+ ## Связанное
279
+
280
+ - [`saby-integration/v8unpack`](https://github.com/saby-integration/v8unpack) —
281
+ upstream-распаковщик на Python, лицензия MIT.
282
+
283
+ ## Лицензия
284
+
285
+ MIT — см. [LICENSE](LICENSE).
@@ -0,0 +1,268 @@
1
+ # v8unpack-agent
2
+
3
+ Прикладной инструмент над upstream-распаковщиком `v8unpack`: превращает
4
+ выгрузку конфигурации 1С в читаемый текстовый слой, машиночитаемую опись
5
+ форм и компактный контекст для LLM.
6
+
7
+ Задача пакета — дать агенту достоверные сведения о формах и модулях без
8
+ угадывания. Где данные нечитаемы, результат помечается как неполный, а не
9
+ достраивается догадками.
10
+
11
+ ## Границы и non-goals
12
+
13
+ - Пакет не распаковывает контейнер сам: дерево выгрузки готовит upstream
14
+ `v8unpack`, а агент работает с уже раскрытым каталогом.
15
+ - Production-адаптера одиночного `Form.bin` в пакете нет: реализацию
16
+ распаковщика передаёт вызывающая сторона.
17
+ - Функций `index_cf()` и `rag.rebuild()` не существует; векторная индексация
18
+ находится вне scope пакета.
19
+ - Пакет не подключается к живой информационной базе и не читает её данные.
20
+
21
+ ## Кто что решает
22
+
23
+ | Компонент | Ответственность |
24
+ |---|---|
25
+ | upstream `v8unpack` | распаковка контейнеров в дерево файлов |
26
+ | `v8unpack-agent` | опись форм, текстовый слой, индексы, контекст, отчёт о запуске |
27
+ | вызывающая сторона | реализация распаковщика формы и решения по degraded-результату |
28
+
29
+ ## Установка
30
+
31
+ Основная установка из PyPI:
32
+
33
+ ```bash
34
+ pip install v8unpack-agent
35
+ ```
36
+
37
+ Исторический pre-release `0.1.0rc1` остаётся доступным в PyPI и по умолчанию
38
+ не выбирается резолверами (нужен явный pin или `--pre`):
39
+
40
+ ```bash
41
+ pip install v8unpack-agent==0.1.0rc1
42
+ ```
43
+
44
+ Установка актуального состояния `main` напрямую из Git остаётся вариантом для
45
+ проверки ещё не опубликованных изменений:
46
+
47
+ ```bash
48
+ pip install git+https://github.com/MRDK80/v8unpack-agent.git
49
+ ```
50
+
51
+ Установка для разработки из клона:
52
+
53
+ ```bash
54
+ pip install -e ".[test]"
55
+ ```
56
+
57
+ upstream `v8unpack` нужен для фактического извлечения текстов, включая
58
+ поддержку внешних отчётов `.erf`.
59
+
60
+ ## Быстрый старт: CLI
61
+
62
+ Один прогон — один отчёт и один код возврата. На вход подаётся каталог
63
+ уже распакованной выгрузки.
64
+
65
+ ```bash
66
+ v8unpack-agent-run <корень_выгрузки> --report-path post-run.json
67
+ ```
68
+
69
+ Аргумент `--report-path` обязателен, значения по умолчанию нет, а
70
+ каталог-родитель должен существовать заранее.
71
+
72
+ | Код | Значение |
73
+ |---:|---|
74
+ | 0 | все объекты обработаны полностью |
75
+ | 2 | ошибка аргументов или корень не является каталогом |
76
+ | 3 | degraded: есть частичные или отказавшие объекты |
77
+ | 4 | управляемая фатальная ошибка пайплайна |
78
+ | 5 | ошибка записи отчёта |
79
+ | 6 | фатальная ошибка и ошибка записи одновременно |
80
+
81
+ Degraded — неуспешное завершение: неполный контекст не должен выглядеть
82
+ успехом для автоматизации. Полное описание аргументов, стадий и кодов причин —
83
+ в [`docs/runner.md`](docs/runner.md), схема отчёта — в
84
+ [`docs/run_report.md`](docs/run_report.md).
85
+
86
+ ## Быстрый старт: Python
87
+
88
+ Каноническая цепочка: обнаружение источников, распаковка по `FormBinSource`,
89
+ обновление индекса.
90
+
91
+ ```python
92
+ from pathlib import Path
93
+
94
+ from v8unpack_agent import unpack_all_forms, update_forms_index
95
+ from v8unpack_agent.form_artifact import FormArtifact
96
+ from v8unpack_agent.form_identity import FormBinSource
97
+ from v8unpack_agent.scan_forms import scan_forms
98
+
99
+ dump_root = Path("unpacked_cf")
100
+ unpacked_root = Path("text_layer")
101
+
102
+
103
+ def unpack_one(source: FormBinSource, root: Path) -> FormArtifact:
104
+ """Здесь вызывается реальное извлечение текстов из source.bin_path."""
105
+ return FormArtifact.for_form(root, source.name)
106
+
107
+
108
+ scan_index = scan_forms(dump_root)
109
+ print(f"Найдено форм: {scan_index.total}")
110
+
111
+ artifacts = unpack_all_forms(dump_root, unpacked_root, unpack_one)
112
+ index = update_forms_index(dump_root, unpacked_root, artifacts)
113
+ index.save(Path("forms_index.json"))
114
+ ```
115
+
116
+ Распаковщик получает `FormBinSource` и корень текстового слоя — ровно два
117
+ аргумента. Существующая трёхаргументная реализация подключается только через
118
+ `adapt_legacy_unpacker()`. Подробный разбор и отбор по `form_ids` — в
119
+ [`docs/pipeline.md`](docs/pipeline.md), готовые сценарии — в
120
+ [`examples/README.md`](examples/README.md).
121
+
122
+ ## Пайплайн
123
+
124
+ | Шаг | Функция | Результат |
125
+ |---|---|---|
126
+ | опись форм | `scan_forms()` | `FormScanIndex` с layout-метаданными и индексом ссылочных типов |
127
+ | обнаружение источников | `discover_form_sources()` | `FormBinSource` с каноническим `form_id` |
128
+ | распаковка | `unpack_all_forms()` | `FormArtifact` на каждую форму |
129
+ | структура формы | `parse_elem_json()` | элементы и привязки `data_path`, best-effort |
130
+ | реквизиты объекта | `decode_object_attributes()` | `Properties` и `TabularSections` |
131
+ | классификация | `classify_form()` | `object`, `service` или `unknown` |
132
+ | внешние отчёты | `unpack_erf()` | текстовый слой и запросы СКД |
133
+ | индекс актуальности | `update_forms_index()` | `FormsIndex` с ключом `form_id` |
134
+ | контроль дрейфа | `check_drift()` | `DriftReport` по хешам |
135
+ | контекст для LLM | `build_form_context()` | `FormContext` и промпт-фрагмент |
136
+
137
+ ## Ключевые возможности
138
+
139
+ - Одноимённые формы разных владельцев не затирают друг друга: идентичность —
140
+ `form_id`, а не имя.
141
+ - Формы без кода попадают в опись: elem-only ветка заполняет `elem_json_path`.
142
+ - Частичный результат всегда явный: `extraction_ok=False` невозможен без
143
+ непустого `extraction_warnings`.
144
+ - Ссылочные типы реквизитов приводятся к имени объекта метаданных; неизвестный
145
+ UUID остаётся `Ref#<uuid>` и не угадывается.
146
+ - Сервисные формы отделены от объектных, чтобы метрика покрытия не занижалась
147
+ архитектурным паттерном платформы.
148
+ - Дрейф детектируется по хешам модуля и структуры, а не только по времени
149
+ изменения файла.
150
+ - Сериализация индексов переносима: в файлы пишутся только относительные
151
+ POSIX-пути, одинаковые на POSIX и NT.
152
+
153
+ ## Версии схем
154
+
155
+ Три независимые версии, которые не сравниваются друг с другом.
156
+
157
+ | Артефакт | Версия | Где описан |
158
+ |---|---:|---|
159
+ | `FormsIndex` | 2 | [`docs/pipeline.md`](docs/pipeline.md) |
160
+ | `FormScanIndex` | 2 при чтении legacy 1 | [`docs/scan_forms.md`](docs/scan_forms.md) |
161
+ | post-run report | 1 | [`docs/run_report.md`](docs/run_report.md) |
162
+
163
+ ## Публичная поверхность
164
+
165
+ | Модуль | Основные имена | Канонический документ |
166
+ |---|---|---|
167
+ | `scan_forms` | `scan_forms`, `FormEntry`, `FormScanIndex`, `scan_warning_code` | [scan_forms](docs/scan_forms.md) |
168
+ | `pipeline` | `unpack_all_forms`, `unpack_erf`, `update_forms_index`, `FormUnpacker` | [pipeline](docs/pipeline.md) |
169
+ | `form_identity` | `FormBinSource`, `discover_form_sources`, `select_sources`, `adapt_legacy_unpacker` | [pipeline](docs/pipeline.md) |
170
+ | `form_artifact` | `FormArtifact` | [pipeline](docs/pipeline.md) |
171
+ | `forms_index` | `FormsIndex`, `FormsIndexEntry`, `is_form_stale` | [pipeline](docs/pipeline.md) |
172
+ | `runner`, `cli` | `RunOptions`, `RunOutcome`, `run_pipeline` | [runner](docs/runner.md) |
173
+ | `run_report` | `PostRunReport`, `ObjectRunResult`, `write_post_run_report` | [run_report](docs/run_report.md) |
174
+ | `elem_parser` | `parse_elem_json`, `ElemIndexResult`, `UnindexedReason` | [elem_parser](docs/elem_parser.md) |
175
+ | `form_classifier` | `classify_form`, `FormClass`, `classify_empty_tree_form` | [form_classifier](docs/form_classifier.md) |
176
+ | `coverage_metric` | `calc_data_path_coverage`, `CoverageReport` | [form_classifier](docs/form_classifier.md) |
177
+ | `object_decoder` | `decode_object_attributes`, `DecodeResult`, `DecodeError` | [object_decoder](docs/object_decoder.md) |
178
+ | `catalog_resolver` | `resolve_data_path`, `ResolvedBinding` | [catalog_resolver](docs/catalog_resolver.md) |
179
+ | `form_context` | `FormContext`, `build_form_context`, `to_llm_prompt_fragment` | [form_context](docs/form_context.md) |
180
+ | `form_summary` | `build_form_summary`, `to_normalized_json` | [form_summary](docs/form_summary.md) |
181
+ | `form_router` | `FormRouter`, `form_paths` | [form_router](docs/form_router.md) |
182
+ | `common_modules` | `scan_common_modules`, `build_common_module_context` | [common_modules](docs/common_modules.md) |
183
+ | `managed_forms` | `discover_elem_forms`, `ElemFormEntry` | [managed_forms_structure](docs/managed_forms_structure.md) |
184
+ | `skd_extractor` | `extract_skd_queries`, `extract_all_skd_queries` | [skd_extractor](docs/skd_extractor.md) |
185
+ | `drift_checker` | `check_drift`, `DriftReport` | [drift_checker](docs/drift_checker.md) |
186
+
187
+ ## Документация
188
+
189
+ ### Начало работы
190
+
191
+ - [Текущее состояние реализации](docs/IMPLEMENTATION_STATUS.md)
192
+ - [Процесс контрибуции](CONTRIBUTING.md)
193
+ - [История изменений](CHANGELOG.md)
194
+
195
+ ### Пайплайн и запуск
196
+
197
+ - [Контракт пайплайна и `FormUnpacker`](docs/pipeline.md)
198
+ - [Production runner, CLI и коды возврата](docs/runner.md)
199
+ - [Схема отчёта о запуске](docs/run_report.md)
200
+
201
+ ### Формы и разбор
202
+
203
+ - [Структура элементов и `UnindexedReason`](docs/elem_parser.md)
204
+ - [Классификация форм и метрика покрытия](docs/form_classifier.md)
205
+ - [Сводка по форме](docs/form_summary.md)
206
+ - [Контекст для LLM](docs/form_context.md)
207
+ - [Маршрутизация путей формы](docs/form_router.md)
208
+ - [Структура управляемых форм](docs/managed_forms_structure.md)
209
+ - [Структура внешних обработок и отчётов](docs/external_forms_structure.md)
210
+ - [Запросы СКД](docs/skd_extractor.md)
211
+
212
+ ### Метаданные и индексы
213
+
214
+ - [Опись форм и индекс ссылочных типов](docs/scan_forms.md)
215
+ - [Реквизиты объекта из raw-секции `header`](docs/object_decoder.md)
216
+ - [Разрешение `data_path`](docs/catalog_resolver.md)
217
+ - [Общие модули](docs/common_modules.md)
218
+ - [Контроль дрейфа](docs/drift_checker.md)
219
+
220
+ ### Исследования
221
+
222
+ Датированные протоколы с агрегатами; выводы не переписываются задним числом.
223
+
224
+ - [Доля неиндексированных форм](docs/research/unindexed_share_issue229.md)
225
+ - [Разрешение ссылочных типов](docs/research/ref_resolver_issue143.md)
226
+ - [Нессылочные типовые грани](docs/research/non_reference_type_facets_issue166.md)
227
+ - [Кросс-конфигурационная проверка типов](docs/research/platform_types_cross_config_issue164.md)
228
+ - [Пустые реквизиты объекта](docs/research/missing_object_attributes_issue163.md)
229
+ - [Структура `Form.bin`](docs/research/form_bin_issue150.md)
230
+ - [Валидация на третьей выгрузке](docs/research/third_configuration_validation.md)
231
+
232
+ ### Примеры и политики
233
+
234
+ - [Исполняемые примеры](examples/README.md)
235
+ - [Политика ветвления](docs/branch_protection.md)
236
+ - [Аудит документации](docs/documentation_audit_issue245.md)
237
+
238
+ ## Известные ограничения
239
+
240
+ - Обычные (неуправляемые) формы хранят разметку в бинарном виде и частично
241
+ не читаются; такие формы получают `unknown` и исключаются из знаменателя
242
+ метрики.
243
+ - Квалификаторы реквизитов и составные типы не декодируются.
244
+ - Индекс ссылочных типов строится только для видов метаданных с доказанной
245
+ ссылочной формой; остальные UUID остаются `Ref#<uuid>`.
246
+ - В режиме `mode="external"` индекс ссылочных типов не собирается.
247
+ - Распаковщики из `examples/` и тестов — заглушки и не читают бинарный
248
+ формат.
249
+ - Пакет ещё не опубликован в индексе пакетов (issue #149).
250
+
251
+ ## Тесты
252
+
253
+ ```bash
254
+ pytest
255
+ ```
256
+
257
+ Фикстуры синтетические: реальная выгрузка для прогона не требуется. Порядок
258
+ работы с ветками, проверки перед PR и требования к обезличенности описаны в
259
+ [CONTRIBUTING.md](CONTRIBUTING.md).
260
+
261
+ ## Связанное
262
+
263
+ - [`saby-integration/v8unpack`](https://github.com/saby-integration/v8unpack) —
264
+ upstream-распаковщик на Python, лицензия MIT.
265
+
266
+ ## Лицензия
267
+
268
+ MIT — см. [LICENSE](LICENSE).
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "v8unpack-agent"
7
+ version = "0.1.0"
8
+ description = "Agent-pipeline adapter for v8unpack: path factory, FormArtifact, drift index"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ dependencies = [
14
+ "v8unpack>=1.2.13",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ test = [
19
+ "pytest>=7",
20
+ ]
21
+ dev = [
22
+ "mypy==2.3.1",
23
+ "ruff==0.16.4",
24
+ ]
25
+
26
+ [project.scripts]
27
+ # Production entry point (#198). Альтернативный вызов без установки пакета:
28
+ # python -m v8unpack_agent.cli
29
+ v8unpack-agent-run = "v8unpack_agent.cli:main"
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/MRDK80/v8unpack-agent"
33
+
34
+ [tool.setuptools]
35
+ packages = ["v8unpack_agent"]
36
+
37
+ [tool.pytest.ini_options]
38
+ pythonpath = ["."]
39
+
40
+ [tool.ruff]
41
+ target-version = "py310"
42
+
43
+ # Набор правил не сужается: используется дефолт запиненного ruff 0.16.4.
44
+ # Явный select отключил бы уже обнаруженный долг (ISC004, PLW1510) без разбора,
45
+ # что противоречит scope #161.
46
+
47
+ [tool.ruff.lint.per-file-ignores]
48
+ # Deprecated-шим с PEP 562: имена выдаются модульным __getattr__, а __dir__()
49
+ # возвращает sorted(__all__). Статически ruff этого не видит; удалять имена
50
+ # из __all__ нельзя — сломается контракт устаревших алиасов.
51
+ "v8unpack_agent/managed_form_summary.py" = ["F822"]
52
+ # Примеры запускаются как `python examples/...`; shebang декоративен.
53
+ # Единая смена git-mode каталога в scope #161 не входит.
54
+ "examples/**/*.py" = ["EXE001"]
55
+
56
+ [tool.mypy]
57
+ python_version = "3.10"
58
+ files = ["v8unpack_agent", "examples"]
59
+ show_error_codes = true
60
+ warn_unused_configs = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+